diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2157929 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +.sconf_temp/ +.sconsign.dblite +/build +/dist +/crap +/pkg +/mnt +/demo +/bbctrl-* +node_modules +*~ +\#* +*.pyc +__pycache__ +*.egg-info +/upload +/*.img +*.so +*.deb +*.zip +/rpi-share +/package-lock.json + +*.elf +*.hex diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3355047 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,394 @@ +Buildbotics CNC Controller Firmware Changelog +============================================= + +## v0.4.14 + - Handle file uploads with '#' or '?' in the name. + - Added "step mode" to Web based jogging. + - Fixed touch screen Web jogging. + +## v0.4.13 + - Support for OMRON MX2 VFD. + - Better error handling in WiFi configuration. + - Fix open WiFi access. + - Improved video camera performance. + - Allow up to 4 camera clients at once. + - Add axis bounds GCode variables ``#<_x_min>``, ``#<_x_max>``, etc. + - Expose ``junction-accel`` planning parameter. + - Fixed problem with manual firmware upload on OSX. + - Ignore cameras that do not support MJPEG format video. + +## v0.4.12 + - Segments straddle arc in linearization. + - Control max-arc-error with GCode var. + - Implemented path modes G61, G61.1 & G64 with naive CAM and basic blending. + - Log GCode messages to "Messages" tab. + - Acknowledging a message on one browser clears it for all. + - Automatically reload Web view when file changes. + - Added ``config-screen`` script. Web based screen config to come later. + - Suppress message popup with (MSG,# No popup message). + - Show latest GCode message in ``Message`` field on CONTROL page. + - Marked several GCodes supported in cheat sheet. + - Solved planner lookahead failure for most reasonable cases. + - Prevent cutting off distant parts of 3D path view. + - Raised default ``latch-backoff`` to 100mm and ``zero-backoff`` to 5mm. + - Added ``max-deviation`` option. + - Fixed problem with GCode boolean expression parsing. #232. + - Ensure 2uS step pulse width. + +## v0.4.11 + - Don't reset global offsets on M2. + - Test shunt and show error on failure. + - Report spindle status codes from Modbus. + - Save more log files in bug report. + - Fixed indicators low-side units. + - Support for YL600, YL620 & YL620-A VFDs. + - Move Modbus indicators to tool page. + - Support for Sunfar E300 VFD. + - Set GCODE_SCRIPT_PATH to support GCode file routines. + - Fix pause bug introduced in v0.4.10. + +## v0.4.10 + - Fix demo password check + - Fix bug were fast clicks could cause jog commands to arrive out of order. + - Fix bug where planner position may not sync after jog. + - Show power shutdown on indicators page. + - Show all motors in shutdown when in power shutdown. + - Improved GCode error messages. + - Put controller into estop when in power shutdown. + +## v0.4.9 + - Enforce 6A per motor channel peak current limit. + - Adjust config values above max or below min instead of resetting to default. + +## v0.4.8 + - Fixed log rotating. + - Use systemd service instead of init.d. + - Fix planner terminate. + - Changed AVR serial interrupt priorites. + - Increased AVR serial and command buffers. + - Boost HDMI signal. + - Rewrote RPi serial driver. + - Automatically scale max CPU speed to reduce RPi temp. + - Disable USB camera if RPi temperature above 80°C, back on at 75°C. + - Respect offsets in canned cycle moves. #219 + - Fixed G53 warning. + - Fixed delayed offset update after M2 or M30 end of program. + - Handle multiple consecutive config resets correctly. + - Fixed log CPU usage problem introduced in v0.4.6. + - Show RPi temp on indicators page. + - Show red thermometer if RPi temp exceeds 80°C. + +## v0.4.7 + - Fix homing switch to motor channel mapping with non-standard axis order. + - Added ``switch-debounce`` and ``switch-lockout`` config options. + - Handle corrupt GCode simulation data correctly. + - Fixes for exception logging. + - Always limit motor max-velocity. #209 + - Sync GCode and planner files to disk after write. + - Added warning about reliability in a noisy environment on WiFi config page. + - EStop on motor fault. + - Fixed ETA line wrapping on Web interface. + - Fixed zeroing with non-zero offset when unhomed. #211 + - Handle file paths uploaded from Windows correctly. #212 + - Don't retain estop state through reboot. + - Log when RPi gets hot. + - Support Modbus multi-write mode. + - Added support for Nowforever VFDs. + +## v0.4.6 + - Fixed a rare ``Negative s-curve time`` error. + - Don't allow manual axis homing when soft limits are not set. + - Right click to enable camera crosshair. + - Demo mode. + - Limit idle-current to 2A. + - Removed dangerous ``power-mode`` in favor of simpler ``enabled`` option. + - Fixed bug where motor driver could fail to disabled during estop. + - Restored estop text. + +## v0.4.5 + - Fix for random errors while running VFD. + - Fix bug where planner would not continue after optional pause (M1). + - Fix lockup on invalid no move probe G38.x. #183 + - Fix zeroing homed axis after jog. + - Fix VFD communication at higher baud rates (> 9600). #184 + +## v0.4.4 + - Write version to log file. + - Write time to log file periodically. + - Show simulation progress with or with out 3D view. + - Synchronize file list between browsers. + - Increased max simulation time to 24hrs. + - Added button to download current GCode file. + - Blink play button to indicate pause. + - Many layout tweaks/improvements. + - Don't abort simulations when system time changes. + - Only allow one camera stream at a time. + +## v0.4.2 + - Suppress ``Auto-creating missing tool`` warning. + - Prevent ``Stream is closed`` error. + - Suppress ``WebGL not supported`` warning. + - Fixed Web disconnect during simulation of large GCode. + - Disable outputs on estop. + - Improved switch debouncing for better homing. + - Handle zero length dwell correctly. + - Fixed problem with cached GCode file upload when file changed on disk. + - Run simulation at low process priority. + - Added ``Bug Report`` button to ``Admin`` -> ``General``. + - Only render 3D view as needed to save CPU. + - Prevent lockup due to browser causing out of memory condition. + - Show error message when too large GCode upload fails. + - Much faster 3D view loading. + +## v0.4.1 + - Fix toolpath view axes bug. + - Added LASER intensity view. + - Fixed reverse path planner bug. + - Video size and path view controls persistent over browser reload. + - Fixed time and progress bugs. + - Added PWM rapid auto off feature for LASER/Plasma. + - Added dynamic PWM for LASER/Plasma. + - Added motor faults table to indicators page. + - Emit error and indicate FAULT on axis for motor driver faults. + - Display axis motor FAULT on LCD. + - Fixed bug with rapid repeated unpause. + +## v0.4.0 + - Increased display precision of position and motor config. + - Added support for 256 microstepping. + - Smoother operation at 250k step rate by doubling clock as needed. + - Indicators tab improvements. + - Much improved camera support. + - Camera hotpluging. + - Move camera video to header. + - Click to switch video size. + - Automount/unmount USB drives. + - Automatically install ``buildbotics.gc`` when no other GCode exists. + - Preplan GCode and check for errors. + - Display 3D view of program tool paths in browser. + - Display accurate time remaining, ETA and progress during run. + - Automatically collapse moves in planner which are too short in time. + - Show IO status indicators on configuration pages. + - Check that axis dimensions fit path plan dimensions. + - Show machine working envelope in path plan viewer. + - Don't reload browser view on reconnect unless controller has reloaded. + - Increased max switch backoff search distance. + - Major improvements for LASER raster GCodes. + - Fixed major bug in command queuing. + - Ignore Program Number O-Codes. + - Improved planning of collinear line segments. + - Allow PWM output up to 320kHz and no slower than 8Hz. + +## v0.3.28 + - Show step rate on motor configuration page. + - Limit motor max-velocity such that step rate cannot exceed 250k. + - Fixed deceleration bug at full 250k step rate. + +## v0.3.27 + - Fixed homing in imperial mode. + +## v0.3.26 + - Removed VFD test. + - Show VFD status on configuration page. + - Show VFD commands fail counts. + - Marked some VFD types as beta. + +## v0.3.25 + - Error on home if max-soft-limit <= min-soft-limit + 1. #139 + - Decrease boot time networking delay. + - Default to US keyboard layout. #145 + - Added configuration option to show metric or imperial units in browser. #74 + - Implemented fine jogging control in Web interface. #147 + +## v0.3.24 + - Added unhome button on axis position popup. + - Ignore soft limits of max <= min. + - Fixed problem with restarting program in imperial units mode. + - Handle GCode with infinite or very long loops correctly. + - Fixed Huanyang spindle restart after stop. + +## v0.3.23 + - Fix for modbus read operation. + - Finalized AC-Tech VFD support. + - Preliminary FR-D700 VFD support. + - Ignore leading zeros in modbus messages. + - Handle older PWR firmwares. + +## v0.3.22 + - Fix position loss after program pause. #130 + - Correctly handle disabled axes. + - Fixed config checkbox not displaying defaulted enabled correctly. + - Added Custom Modbus VFD programming. + +## v0.3.21 + - Implemented M70-M73 modal state save/restore. + - Added support for modbus VFDs. + - Start Huanyang spindle with out first pressing Start button on VFD. + - Faster switching of large GCode files in Web. + - Fixed reported gcode line off by one. + - Disable MDI input while running. + - Stabilized direction pin output during slow moves. + +## v0.3.20 + - Eliminated drift caused by miscounting half microsteps. + - Fixed disappearing GCode in Web. + - More efficient GCode scrolling with very large files. + - Fully functional soft-limited jogging. + - Added client and access-point Wifi configuration. + - Fixed broken hostname Web redirect after change. + - Split admin page -> General & Network. + - Improved calculation of junction velocity limits. + +## v0.3.19 + - Fixed stopping problems. #127 + - Fixed ``Negative s-curve time`` error. + - Improved jogging with soft limits. + - Added site favicon. + - Fixed problems with offsets and imperial units. + - Fixed ``All zero s-curve times`` caused by extremely short, non-zero moves. + - Fixed position drift. + +## v0.3.18 + - Don't enable any tool by default. + +## v0.3.17 + - Fixed pausing fail near end of run bug. + - Show "Upgrading firmware" when upgrading. + - Log excessive pwr communication failures as errors. + - Ensure we can still get out of non-idle cycles when there are errors. + - Less frequent pwr variable updates. + - Stop cancels seek and subsequent estop. + - Fixed bug in AVR/Planner command synchronization. + - Consistently display HOMING state during homing operation. + - Homing zeros axis global offset. + - Added zero all button. #126 + - Separate "Auto" and "MDI" play/pause & stop buttons. #126 + - Moved home all button. #126 + - Display "Video camera not found." instead of broken image icon. + - Show offset positions not absolute on LCD. + - Don't change gcode lines while homing. + - Don't change button states while homing. + - Adding warning about power cyclying during an upgrade. + - Reset planner on AVR errors. + - Fixed pausing with short moves. + - Corrected s-curve accel increasing jogging velocities. + +## v0.3.16 + - Fixed switch debounce bug. + +## v0.3.15 + - Suppress warning missing config.json warning after config reset. + - Fixed EStop reboot loop. + - Removed AVR unexpected reboot error. + +## v0.3.14 + - Fixed: Config fails silently after web disconnect #112 + - Always reload the page after a disconnect. + - Honor soft limits #111 (but not when jogging) + - Limit switch going active while moving causes estop. #54 + - Added more links to help page. + - Fixed axis display on LCD. #122 + - Added GCode cheat sheet. + - Fixed LCD boot splash screen. #121 + - Implemented tool change procedures and pause message box. #81 + - Implemented program start and end procedures. + +## v0.3.13 + - Disable spindle and loads on stop. + - Fixed several state transition (stop, pause, estop, etc.) problems. + +## v0.3.12 + - Updated DB25 M2 breakout diagram. + - Enabled AVR watchdog. + - Fixed problem with selecting newly uploaded file. + - More thorough shutdown of stepper driver in estop. + - Fixed spindle type specific options. + - No more ``Unexpected AVR firmware reboot`` errors on estop clear. + - Downgraded ``Machine alarmed - Command not processed`` errors to warnings. + - Suppress unnecessary axis homing warnings. + - More details for axis homing errors. + - Support GCode messages e.g. (MSG, Hello World!) + - Support programmed pauses. i.e. M0 + +## v0.3.11 + - Suppressed ``firmware rebooted`` warning. + - Error on unexpected AVR reboot. + - Fixed pin fault output. + - No longer using interrupts for switch inputs. Debouncing on clock tick. + +## v0.3.10 + - Fixed "Flood" display, changed to "Load 1" and "Load 2". #108 + - Highlight loads when on. + - Fixed axis zeroing. + - Fixed bug in home position set after successful home. #109 + - Fixed ugly Web error dumps. + - Allow access to log file from Web. + - Rotate log so it does not grow too big. + - Keep same GCode file through browser reload. #20 + +## v0.3.9 + - Fixed bug in move exec that was causing bumping between moves. + - Fixed planner bug which could create negative s-curve times. + - Hide step and optional pause buttons until they are implemented. + - Fixed pausing problems. + - Limit number of console messages. + - Scrollbar on console view. + - Log debug messages to console in developer mode. + - Fixed AVR log message source. + - Fixed step correction. + - JOGGING, HOMMING and MDI states. + - Fixed position problem with rapid MDI entry. + +## v0.3.8 + - Fixed pwr flags display + - Added pwr fault flags to indicators + +## v0.3.7 + - Allow blocking error dialog for a period of time + - Show actual error message on planner errors + - Reset planner on serious error + - Fixed console clear + - Added helpful info to Video tab + - Changed "Console" tab to "Messages" + - Removed spin up/down velocity options, they don't do anything + - Allow RS485 to work when wires are swapped + - Allow setting VFD ID + - Only show relevant spindle config items + - More robust video camera reset + - Added help page + - Allow upgrade with out Internet + - Limit power fault reporting + - Added load over temp, load limiting and motor overload power faults + +## v0.3.6 + - Set max_usb_current=1 in /boot/config.txt from installer #103 + +## v0.3.5 + - Fixed dwell (G4) + - Always show limit switch indicators regardless of motor enable + - Fixed feed rate display + - Added current GCode unit display + - Fixed homed axis zeroing + - Fixed probe pin input + - Added reload button to video tab + - Don't open error dialog on repeat messages + - Handle large GCode files in browser + - Added max lookahead limit to planner + - Fixed GCode stopping/pausing where ramp down needs more than is in the queue + - Added breakout box diagram to indicators + - Initialize axes offsets to zero on startup + - Fixed conflict between ``x`` state variable and ``x`` axis variable + - Don't show ipv6 addresses on LCD. They don't fit. + +## v0.3.4 + - Added alternate units for motor parameters + - Automatic config file upgrading + - Fixed planner/jog sync + - Fixed planner limits config + - Accel units mm/min² -> m/min² + - Search and latch velocity mm/min -> m/min + - Fixed password update (broken in last version) + - Start Web server earlier in case of Python coding errors + + +Changelog not maintained in previous versions. See git commit log. diff --git a/CODE_TAG b/CODE_TAG new file mode 100644 index 0000000..d36ffb0 --- /dev/null +++ b/CODE_TAG @@ -0,0 +1,22 @@ + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ac86d6d --- /dev/null +++ b/LICENSE @@ -0,0 +1,377 @@ +The Buildbotics firmware ("the software") is free software: you can +redistribute it and/or modify it under the terms of the GNU General +Public License, version 2 (with out any licensing exceptions) as +published by the Free Software Foundation. See the full license terms +below. + +*********************************************************************** + +Substantial effort was made to give credit to all authors of this +software and of the works it was derived from and to adhere to the +terms of the applicable license agreements. If you belive any +mistakes have been made in this regard please contact Joseph Coffland +. + +Portions of this software are copyrighted by the following entities: + + Copyright (c) 2015 - 2016 Buildbotics LLC + Copyright (c) 2014 Thomas Nixon, Jonathan Heathcote (cpp_magic.h) + All rights reserved. + +Each source code file lists the entities which claim copyright to +parts of those files. + +Much of this software was originally written by Alden S. Hart, Jr. and +Robert Giseburt as part of the TinyG project. The TinyG project was +in turn derived from grbl/marlin firmwares. All of the original code +has been reformatted and cleaned up to fit our coding standards. +Functionality in many areas has been substantially modified. + +The original TinyG firmware is licensed under the GPL v2 and includes +an exception which allows use of the source code by non-GPLed +libraires and potentially executables linked to those libraries. The +TinyG project's license did not specify that this exception must be +offered in derived works, therefore this software's license DOES NOT +offer any exceptions to the GPL v2 license. + +*********************************************************************** + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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. + + + Copyright (C) + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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. + + , 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 Lesser General +Public License instead of this License. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..6450ecf --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,12 @@ +recursive-include src/py/bbctrl/http * +include package.json README.md scripts/install.sh +include src/avr/bbctrl-avr-firmware.hex +include src/bbserial/bbserial.ko +include scripts/avr109-flash.py +include scripts/buildbotics.gc +include scripts/xinitrc +include scripts/rc.local +include scripts/bbctrl.service +include scripts/11-automount.rules +recursive-include src/py/camotics * +global-exclude .gitignore diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0aed58e --- /dev/null +++ b/Makefile @@ -0,0 +1,142 @@ +DIR := $(shell dirname $(lastword $(MAKEFILE_LIST))) + +NODE_MODS := $(DIR)/node_modules +PUG := $(NODE_MODS)/.bin/pug +STYLUS := $(NODE_MODS)/.bin/stylus + +TARGET_DIR := build/http +HTML := index +HTML := $(patsubst %,$(TARGET_DIR)/%.html,$(HTML)) +RESOURCES := $(shell find src/resources -type f) +RESOURCES := $(patsubst src/resources/%,$(TARGET_DIR)/%,$(RESOURCES)) +TEMPLS := $(wildcard src/pug/templates/*.pug) + +AVR_FIRMWARE := src/avr/bbctrl-avr-firmware.hex +GPLAN_MOD := rpi-share/camotics/gplan.so +GPLAN_TARGET := src/py/camotics/gplan.so +GPLAN_IMG := gplan-dev.img + +RSYNC_EXCLUDE := \*.pyc __pycache__ \*.egg-info \\\#* \*~ .\\\#\* +RSYNC_EXCLUDE := $(patsubst %,--exclude %,$(RSYNC_EXCLUDE)) +RSYNC_OPTS := $(RSYNC_EXCLUDE) -rv --no-g --delete --force + +VERSION := $(shell sed -n 's/^.*"version": "\([^"]*\)",.*$$/\1/p' package.json) +PKG_NAME := bbctrl-$(VERSION) +PUB_PATH := root@buildbotics.com:/var/www/buildbotics.com/bbctrl +BETA_VERSION := $(VERSION)-rc$(shell ./scripts/next-rc) +BETA_PKG_NAME := bbctrl-$(BETA_VERSION) + +SUBPROJECTS := avr boot pwr jig +WATCH := src/pug src/pug/templates src/stylus src/js src/resources Makefile +WATCH += src/static + +ifndef HOST +HOST=bbctrl.local +endif + +ifndef PASSWORD +PASSWORD=buildbotics +endif + + +all: $(HTML) $(RESOURCES) + @for SUB in $(SUBPROJECTS); do $(MAKE) -C src/$$SUB; done + +pkg: all $(AVR_FIRMWARE) bbserial + ./setup.py sdist + +beta-pkg: pkg + cp dist/$(PKG_NAME).tar.bz2 dist/$(BETA_PKG_NAME).tar.bz2 + +bbserial: + $(MAKE) -C src/bbserial + +gplan: $(GPLAN_TARGET) + +$(GPLAN_TARGET): $(GPLAN_MOD) + cp $< $@ + +$(GPLAN_MOD): $(GPLAN_IMG) + ./scripts/gplan-init-build.sh + git -C rpi-share/cbang fetch + git -C rpi-share/cbang reset --hard FETCH_HEAD + git -C rpi-share/camotics fetch + git -C rpi-share/camotics reset --hard FETCH_HEAD + cp ./scripts/gplan-build.sh rpi-share/ + sudo ./scripts/rpi-chroot.sh $(GPLAN_IMG) /mnt/host/gplan-build.sh + +$(GPLAN_IMG): + ./scripts/gplan-init-build.sh + +.PHONY: $(AVR_FIRMWARE) +$(AVR_FIRMWARE): + $(MAKE) -C src/avr + +publish: pkg + echo -n $(VERSION) > dist/latest.txt + rsync $(RSYNC_OPTS) dist/$(PKG_NAME).tar.bz2 dist/latest.txt $(PUB_PATH)/ + +publish-beta: beta-pkg + echo -n $(BETA_VERSION) > dist/latest-beta.txt + rsync $(RSYNC_OPTS) dist/$(BETA_PKG_NAME).tar.bz2 dist/latest-beta.txt \ + $(PUB_PATH)/ + +update: pkg + http_proxy= curl -i -X PUT -H "Content-Type: multipart/form-data" \ + -F "firmware=@dist/$(PKG_NAME).tar.bz2" -F "password=$(PASSWORD)" \ + http://$(HOST)/api/firmware/update + @-tput sgr0 && echo # Fix terminal output + +build/templates.pug: $(TEMPLS) + mkdir -p build + cat $(TEMPLS) >$@ + +node_modules: package.json + npm install && touch node_modules + +$(TARGET_DIR)/%: src/resources/% + install -D $< $@ + +$(TARGET_DIR)/index.html: build/templates.pug +$(TARGET_DIR)/index.html: $(wildcard src/static/js/*) +$(TARGET_DIR)/index.html: $(wildcard src/static/css/*) +$(TARGET_DIR)/index.html: $(wildcard src/pug/templates/*) +$(TARGET_DIR)/index.html: $(wildcard src/js/*) +$(TARGET_DIR)/index.html: $(wildcard src/stylus/*) +$(TARGET_DIR)/index.html: src/resources/config-template.json + +$(TARGET_DIR)/%.html: src/pug/%.pug node_modules + @mkdir -p $(shell dirname $@) + $(PUG) -O pug-opts.js -P $< -o $(TARGET_DIR) || (rm -f $@; exit 1) + +pylint: + pylint3 -E $(shell find src/py -name \*.py | grep -v flycheck_) + +jshint: + ./node_modules/jshint/bin/jshint --config jshint.json src/js/*.js + +lint: pylint jshint + +watch: + @clear + $(MAKE) + @while sleep 1; do \ + inotifywait -qr -e modify -e create -e delete \ + --exclude .*~ --exclude \#.* $(WATCH); \ + clear; \ + $(MAKE); \ + done + +tidy: + rm -f $(shell find "$(DIR)" -name \*~) + +clean: tidy + rm -rf build html dist + @for SUB in $(SUBPROJECTS); do \ + $(MAKE) -C src/$$SUB clean; \ + done + +dist-clean: clean + rm -rf node_modules + +.PHONY: all install clean tidy pkg gplan lint pylint jshint bbserial diff --git a/README_buildbotics.md b/README_buildbotics.md new file mode 100644 index 0000000..345ab24 --- /dev/null +++ b/README_buildbotics.md @@ -0,0 +1,62 @@ +# Buildbotics CNC Controller Firmware +This repository contains the source code for the Buildbotics CNC Controller. +See [buildbotics.com](https://buildbotics.com/) for more information. + +![Buildboitcs CNC Controller](docs/buildbotics_controller.png) + +## Overview +![Buildbotics architecture overview](docs/buildbotics_architecture_overview.png) + +The main parts of the Buildbotics CNC Controller software and the technologies +they are built with are as follows: + + * Web App - Frontend user interface + * [Javascript](https://www.w3schools.com/js/) + * [HTML5](https://www.w3schools.com/html/) + * [Stylus](http://stylus-lang.com/) + * [Pug.js](https://pugjs.org/) + * [Vue.js](https://vuejs.org/) + + * Controller OS - RaspberryPi Operating System + * [Raspbian](https://www.raspbian.org/) + + * BBCtrl - Python App + * [Python 3](https://www.python.org/) + * [Tornado Web](https://www.tornadoweb.org/) + + * GPlan - Path Planner Python Module + * [C++](http://www.cplusplus.com/) + * [CAMotics](https://camotics.org/) + + * Main AVR Firmware + Bootloader - Real-time step generation, etc. + * [ATxmega192a3u](https://www.microchip.com/wwwproducts/ATxmega192A3U) + * [C](https://en.wikipedia.org/wiki/C_(programming_language)) + + * Pwr AVR Firmware - Power safety + * [ATtiny1634](https://www.microchip.com/wwwproducts/ATtiny1634) + * [C](https://en.wikipedia.org/wiki/C_(programming_language)) + +## Quickstart Guide + +Be sure to read the [development guide](docs/development.md) for more detailed +instructions. + +On a Debian Linux (9.6.0 stable) system: + + # Install the required packages + sudo apt-get update + sudo apt-get install -y build-essential git wget binfmt-support qemu \ + parted gcc-avr avr-libc avrdude pylint3 python3 python3-tornado curl \ + unzip python3-setuptools + curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash - + sudo apt-get install -y nodejs + + # Get the source + git clone https://github.com/buildbotics/bbctrl-firmware + + # Build the Firmware + cd bbctrl-firmware + make pkg + +The resulting package will be a ``.tar.bz2`` file in ``dist``. See the +[development guide](docs/development.md) for more information. diff --git a/docs/bbdev-chroot.md b/docs/bbdev-chroot.md new file mode 100644 index 0000000..be1e45a --- /dev/null +++ b/docs/bbdev-chroot.md @@ -0,0 +1,41 @@ +This document describes how to setup a Buildbotics development environment +on a Debian based system inside a chroot. Building in the chroot ensures that +you have a clean and consistent build environment unaltered by other packages +or manual changes. + +# Install packages required to create chroot + + sudo apt-get update + sudo apt-get install binutils debootstrap + +# Create chroot environment + + mkdir bbdev + sudo debootstrap --arch amd64 stable bbdev http://deb.debian.org/debian + +# Copy downloaded files (optional) +To speed things up you can copy to large downloads, if you already have them, +into the chroot. + + sudo mkdir -p bbdev/opt/bbctrl-firmware/src/bbserial/ + sudo cp 2017-11-29-raspbian-stretch-lite.zip bbdev/opt/bbctrl-firmware/ + sudo cp raspberrypi-kernel_1.20171029-1.tar.gz bbdev/opt/bbctrl-firmware/src/bbserial/ + +# Enter the chroot + + sudo mount --bind /proc bbdev/proc + sudo mount --rbind /sys bbdev/sys + sudo mount --rbind /dev bbdev/dev + sudo chroot bbdev + cd /opt + +Now, follow the instructions in [development.md](development.md) from with in +the chroot. + +# Exit the chroot +To exit the chroot: + + exit + sudo umount bbdev/dev + sudo umount bbdev/sys + sudo umount bbdev/proc diff --git a/docs/buildbotics_architecture_overview.png b/docs/buildbotics_architecture_overview.png new file mode 100644 index 0000000..960307d Binary files /dev/null and b/docs/buildbotics_architecture_overview.png differ diff --git a/docs/buildbotics_controller.png b/docs/buildbotics_controller.png new file mode 100644 index 0000000..e0dd0b9 Binary files /dev/null and b/docs/buildbotics_controller.png differ diff --git a/docs/cross_compile_rpi_kernel_module.md b/docs/cross_compile_rpi_kernel_module.md new file mode 100644 index 0000000..2b04dd3 --- /dev/null +++ b/docs/cross_compile_rpi_kernel_module.md @@ -0,0 +1,68 @@ +# Install the cross compiler + + sudo apt-get update + sudo apt-get install -y gcc-arm-linux-gnueabihf + +# Determine the correct kernel version on the target pi + + dpkg-query -l raspberrypi-kernel + +# Get the kernel source for the correct kernel version + + wget https://github.com/raspberrypi/linux/archive/raspberrypi-kernel_1.20171029-1.tar.gz + tar xf raspberrypi-kernel_1.20171029-1.tar.gz + cd linux-raspberrypi-kernel_1.20171029-1 + +# Prep the kernel source + + KERNEL=kernel7 + make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig + make -j 8 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_prepare + +# Create hello.c module + + #include + #include + + MODULE_LICENSE("GPL"); + + static int hello_init(void) { + printk(KERN_ALERT "Hello world!\n"); + return 0; + } + + static void hello_exit(void) { + printk(KERN_ALERT "Goodbye cruel world!\n"); + } + + module_init(hello_init); + module_exit(hello_exit); + +# Create a Makefile + + CROSS := arm-linux-gnueabihf- + DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) + obj-m := hello.o + + all: + $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) modules + + clean: + $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) clean + +# Compile the module + + ln -sf path/to/rpi-kernel kernel + make + +# Copy module to rpi + + scp hello.ko pi.local: + +# Load it on the pi + + sudo insmod hello.ko + +# Look for message in syslog + + tail /var/log/syslog diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..6a82eb5 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,106 @@ +# Buildbotics CNC Controller Development Guide + +This document describes how to setup your environment for Buildbotics CNC +controller development on Debian Linux. Development on systems other than +Debian Linux are not supported. + +## Installing the Development Prerequisites + +On a Debian Linux (9.6.0 stable) system install the required packages: + + sudo apt-get update + sudo apt-get install -y build-essential git wget binfmt-support qemu \ + parted gcc-avr avr-libc avrdude pylint3 python3 python3-tornado curl \ + unzip python3-setuptools gcc-arm-linux-gnueabihf bc sudo + curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash - + sudo apt-get install -y nodejs + +## Getting the Source Code + + git clone https://github.com/buildbotics/bbctrl-firmware + +## Build the Firmware + + cd bbctrl-firmware + make + +## Build GPlan Module + +GPlan is a Python module written in C++. It must be compiled for ARM so that +it can be used on the Raspberry Pi. This is accomplished using a chroot, qemu +and binfmt to create an emulated ARM build environment. This is faster and +more convenient than building on the RPi itself. All of this is automated. + + make gplan + +The first time this is run it will take quite awhile as it setups up the build +environment. You can run the above command again later to build the latest +version. + +## Build the Firmware Package + + make pkg + +The resulting package will be a ``.tar.bz2`` file in ``dist``. + +## Upload the Firmware Package to a Buildbotics CNC Controller +If you have a Buildbotics CNC controller at ``bbctrl.local``, the default +address, you can upgrade it with the new package like this: + + make update HOST=bbctrl.local PASSWORD= + +Where ```` is the controller's admin password. + +## Updating the Pwr Firmware + +The Pwr firmware must be uploaded manually using an ISP programmer. With the +programmer attached to the pwr chip ISP port on the Builbotics controller's +main board run the following: + + make -C src/pwr program + +## Initializing the main AVR firmware + +The main AVR must also be programmed manually the first time. Later it will be +automatically programmed by the RPi as part of the firmware install. To perform +the initial AVR programming connec the ISP programmer to the main AVR's ISP port +on the Buildbotics controller's main board and run the following: + + make -C src/avr init + +This will set the fuses, install the bootloader and program the firmware. + +## Installing the RaspberryPi base system + +Download the latest Buildbotics CNC controller base image and decompress it: + + wget \ + https://buildbotics.com/upload/2018-05-15-raspbian-stretch-bbctrl.img.xz + xz -d 2018-05-15-raspbian-stretch-bbctrl.img.xz + +Now copy the base system to an SD card. You need a card with at least 8GiB. +After installing the RPi system all data on the SD card will be lost. So make +sure you back up the SD card if there's anything important on it. + +In the command below, make sure you have the correct device or you can +**destroy your Linux system** by overwriting the disk. One way to do this is +to run ``sudo tail -f /var/log/syslog`` before inserting the SD card. After +inserting the card look for log messages containing ``/dev/sdx`` where ``x`` is +a letter. This should be the device name of the SD card. Hit ``CTRL-C`` to +stop following the system log. + + sudo dd bs=4M if=2015-05-05-raspbian-wheezy.img of=/dev/sde + sudo sync + +The first command takes awhile and does not produce any output until it's done. + +Insert the SD card into your RPi and power it on. Plug in the network +connection, wired or wireless. + +## Logging into the Buildbotics Controller + +You can ssh in to the Buildbotics Controller like so: + + ssh bbmc@bbctrl.local + +The default password is ``buildbotics``. It's best if you change this. diff --git a/docs/emu_chroot.md b/docs/emu_chroot.md new file mode 100644 index 0000000..348ed82 --- /dev/null +++ b/docs/emu_chroot.md @@ -0,0 +1,45 @@ +This document describes how to setup the Buildbotics firmware in a chroot +environment for the purposes of demonstrating the user interface. + +On a Debian system install: + + ROOT=/opt/demo + sudo apt-get install -y binutils debootstrap + sudo mkdir $ROOT + sudo debootstrap --arch amd64 stable $ROOT/ http://deb.debian.org/debian + +Then chroot: + + sudo mount --bind /dev $ROOT/dev/ + sudo mount --bind /sys $ROOT/sys/ + sudo mount --bind /proc $ROOT/proc/ + sudo mount --bind /dev/pts $ROOT/dev/pts + sudo chroot $ROOT + +Setup the demo system: + + export LC_ALL=C + apt-get update + apt-get install -y wget git python3-tornado python3-sockjs-tornado \ + python3-setuptools python-six build-essential scons libv8-dev + libpython3-dev + + cd /opt + BASE=https://buildbotics.com/bbctrl + LATEST=$(wget $BASE/latest.txt -O- -q) + wget $BASE/bbctrl-$LATEST.tar.bz2 + tar xf bbctrl-$LATEST.tar.bz2 + ln -sf bbctrl-$LATEST bbctrl + + git clone --depth=1 https://github.com/CauldronDevelopmentLLC/cbang + git clone --depth=1 https://github.com/CauldronDevelopmentLLC/camotics + export CBANG_HOME=/opt/cbang + scons -C cbang -j8 disable_local="re2 libevent" + scons -C camotics -j8 gplan.so with_gui=False + + cd bbctrl + python3 setup.py install + cp /opt/camotics/gplan.so /usr/local/lib/python*/dist-packages/bbctrl-$VERSION-py*.egg/camotics/gplan.so + + mkdir -p /var/lib/bbctrl/upload + useradd -u 1001 bbmc diff --git a/jshint.json b/jshint.json new file mode 100644 index 0000000..bb0cbb3 --- /dev/null +++ b/jshint.json @@ -0,0 +1,15 @@ +{ + "asi": true, + "browser": true, + "devel": true, + "strict": "global", + "globals": { + "$": false, + "require": false, + "module": false, + "Vue": false, + "SockJS": false, + "Gauge": false, + "Clusterize": false + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0cd9d42 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "bbctrl", + "version": "1.0.3", + "homepage": "https://onefinitycnc.com/", + "repository": "https://github.com/OneFinityCNC/onefinity", + "license": "GPL-3.0+", + "dependencies": { + "jshint": "", + "browserify": "", + "jstransformer-stylus": "", + "jstransformer-escape-html": "", + "pug-cli": "" + } +} diff --git a/pug-opts.js b/pug-opts.js new file mode 100644 index 0000000..177616e --- /dev/null +++ b/pug-opts.js @@ -0,0 +1,11 @@ +{ + pretty: true, + filters: { + browserify: function (text, options) { + return require('child_process').execSync( + `./node_modules/.bin/browserify - --basedir src/js`, + {input: text} + ).toString() + } + } +} diff --git a/scripts/11-automount.rules b/scripts/11-automount.rules new file mode 100644 index 0000000..96967d4 --- /dev/null +++ b/scripts/11-automount.rules @@ -0,0 +1,10 @@ +KERNEL!="sd[a-z]*", GOTO="automount_end" +IMPORT{program}="/sbin/blkid -o udev -p %N" +ENV{ID_FS_TYPE}=="", GOTO="automount_end" +ENV{ID_FS_LABEL}!="", ENV{dir_name}="%E{ID_FS_LABEL}" +ENV{ID_FS_LABEL}=="", ENV{dir_name}="usb-%k" +ACTION=="add", ENV{mount_options}="relatime" +ACTION=="add", ENV{ID_FS_TYPE}=="vfat|ntfs", ENV{mount_options}="$env{mount_options},utf8,gid=100,umask=002,sync" +ACTION=="add", RUN+="/bin/mkdir -p /media/%E{dir_name}", RUN+="/bin/mount -o $env{mount_options} /dev/%k /media/%E{dir_name}" +ACTION=="remove", ENV{dir_name}!="", RUN+="/bin/umount -l /media/%E{dir_name}", RUN+="/bin/rmdir /media/%E{dir_name}" +LABEL="automount_end" diff --git a/scripts/avr109-flash.py b/scripts/avr109-flash.py new file mode 100644 index 0000000..2fec936 --- /dev/null +++ b/scripts/avr109-flash.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 + +import sys +import time +import serial +import binascii + + +dev = '/dev/ttyAMA0' +baud = 921600 +boot_id = 'bbctrl ' +verbose = False + + +def crc16(data): + crc = 0xffff + + for x in data: + crc = crc ^ int(x) + for bit in range(8): + if crc & 1: crc = (crc >> 1) ^ 0xa001 + else: crc = crc >> 1 + + return crc + + +def avr_crc32(data, length): + mem = [0xff] * length + + for addr, chunk in data: + for x in chunk: + mem[addr] = x + addr += 1 + + return binascii.crc32(bytes(mem)) + + +def read_intel_hex(f): + base = 0 + pos = 0 + start = 0 + chunk = None + + for line in f: + line = line.strip() + if not line or line[0] != ':': continue + + count = int(line[1:3], 16) + addr = int(line[3:7], 16) + type = int(line[7:9], 16) + data = line[9:-2] + checksum = int(line[-2:], 16) + + if type == 0: + addr += base + data = binascii.unhexlify(bytes(data, 'utf8')) + + if chunk is None or pos != addr: + if chunk is not None: yield (start, chunk) + start = addr + chunk = data + + else: chunk += data + + pos = addr + len(data) + + if type == 2: base = int(data, 16) * 16 + + if chunk is not None: yield (start, chunk) + + +def send(data): + if verbose: print('Sending:', data) + sp.write(bytes(data, 'utf8')) + + +def send_int(x, size): + if verbose: print('Sending: %d', x) + sp.write((x).to_bytes(size, byteorder = 'big')) + + +def recv(count): + data = sp.read(count).decode('utf8') + if count and verbose: print('Received:', data) + return data + + +def recv_int(size): + x = int.from_bytes(sp.read(size), byteorder = 'big') + if verbose: print('Received:', x) + return x + + +# Read firmware hex file +data = list(read_intel_hex(open(sys.argv[1], 'r'))) + +# Open serial port +sp = serial.Serial(dev, baud, timeout = 10) + +# Reset AVR +import RPi.GPIO as gpio +gpio.setwarnings(False) +gpio.setmode(gpio.BCM) +gpio.setup(27, gpio.OUT) +gpio.output(27, 0) +gpio.output(27, 1) +gpio.setup(27, gpio.IN, pull_up_down = gpio.PUD_UP) +time.sleep(0.1) + +# Sync +for i in range(10): send('\x1b') + +# Flush serial +try: + recv(sp.in_waiting) +except: pass + +# Get bootloader ID +send('S') +if boot_id != recv(len(boot_id)): + raise Exception('Failed to communicate with bootloader') + +# Get version +send('V') +major = int(recv(1)) +minor = int(recv(1)) +print('Bootloader version: %d.%d' % (major, minor)) + +# If bootloader is new enough compare checksums +if 0 < major or 1 < minor: + # Get flash length + send('n') + flash_len = recv_int(3) + + # Get current flash CRC + send('X') + new_crc = avr_crc32(data, flash_len) + old_crc = recv_int(4) + if old_crc == new_crc: + print('Flash already up to date') + sys.exit(0) + + print('CRC: old=0x%08x new=0x%08x' % (old_crc, new_crc)) + + # Erase + send('e') + if recv(1) != '\r': raise Exception('Flash erase failed') + +# Get page size +send('b') +if recv(1) != 'Y': raise Exception('Cannot get page size') +page_size = recv_int(2) +print('Page size:', page_size) + + +# Program +print('Programming', end = '') +count = 0 +retry = 0 +for addr, chunk in data: + # Set address + send('H') + send_int(addr, 3) + if recv(1) != '\r': raise Exception('Failed to set address') + + while len(chunk): + sys.stdout.flush() + page = chunk[0:512] + + # Block command + send('B') + + # Send block size + send_int(len(page), 2) + + # Send memory type + send('F') + + # Send block + sp.write(page) + + if recv(1) != '\r': raise Exception('Failed to write block') + + # Get and check block CRC + send('i') + crc = recv_int(2) + expect = crc16(page) + if crc != expect: + retry += 1 + if retry == 5: + raise Exception('CRC mismatch %d != %d' % (crc, expect)) + + print('x', end = '') + continue + + count += len(page) + chunk = chunk[512:] + retry = 0 + print('.', end = '') + + +print('done') +print('Wrote %d bytes' % count) + +# Exit bootloader +send('E') diff --git a/scripts/bbctrl.service b/scripts/bbctrl.service new file mode 100644 index 0000000..8b22cc7 --- /dev/null +++ b/scripts/bbctrl.service @@ -0,0 +1,15 @@ +[Unit] +Description=Buildbotics Controller +After=network.target + +[Service] +User=root +ExecStart=/usr/local/bin/bbctrl -l /var/log/bbctrl.log +WorkingDirectory=/var/lib/bbctrl +Restart=always +StandardOutput=null +Nice=-10 +KillMode=process + +[Install] +WantedBy=multi-user.target diff --git a/scripts/browser b/scripts/browser new file mode 100644 index 0000000..92bd07f --- /dev/null +++ b/scripts/browser @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +import os +import sys +import subprocess + + +def enter_cgroup(): + with open('/sys/fs/cgroup/memory/chrome/tasks', 'w') as f: + f.write(str(os.getpid())) + + +# Start browser +args = ['/usr/lib/chromium-browser/chromium-browser'] + sys.argv[1:] +subprocess.Popen(args, preexec_fn = enter_cgroup).wait() diff --git a/scripts/check-config-vars.py b/scripts/check-config-vars.py new file mode 100644 index 0000000..6fd567a --- /dev/null +++ b/scripts/check-config-vars.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +'''Check that the configuration variable template used on the RPi matches the +variables used in the AVR''' + +import sys +import json + +templ = json.load(open('src/resources/config-template.json', 'r')) +vars = json.load(open('avr/build/vars.json', 'r')) + + +def check(section): + errors = 0 + + for name, entry in section.items(): + if 'type' in entry: + ok = False + + # TODO check that defaults are valid + # TODO check that types match + + if 'code' in entry and not entry['code'] in vars: + print('"%s" with code "%s" not found' % (name, entry['code'])) + + else: ok = True + + if not ok: errors += 1 + + else: errors += check(entry) + + return errors + + +errors = check(templ) +print('\n%d errors' % errors) +sys.exit(errors != 0) diff --git a/scripts/config-screen b/scripts/config-screen new file mode 100644 index 0000000..364c150 --- /dev/null +++ b/scripts/config-screen @@ -0,0 +1,32 @@ +#!/bin/bash + +if [ $# != 3 ]; then + echo "Usage: $0 " + exit 1 +fi + +WIDTH="$1" +HEIGHT="$2" +ROTATION="$3" + +if [[ ! "$WIDTH" =~ ^[0-9]+$ ]]; then + echo "Invalid width '$WIDTH'." + exit 1 +fi + +if [[ ! "$HEIGHT" =~ ^[0-9]+$ ]]; then + echo "Invalid height '$HEIGHT'." + exit 1 +fi + +if [[ ! "$ROTATION" =~ ^[0-3]$ ]]; then + echo "Invalid rotation '$ROTATION'." + exit 1 +fi + + +OPTIONS="framebuffer_width=$WIDTH " +OPTIONS+="framebuffer_height=$HEIGHT " +OPTIONS+="display_rotate=$ROTATION" + +edit-boot-config $OPTIONS diff --git a/scripts/config-wifi b/scripts/config-wifi new file mode 100644 index 0000000..018c676 --- /dev/null +++ b/scripts/config-wifi @@ -0,0 +1,259 @@ +#!/bin/bash -e + +AP=false +DISABLE=false +SSID= +PASS= +CHANNEL=7 +REBOOT=false + +WLAN0_CFG=/etc/network/interfaces.d/wlan0 +HOSTAPD_CFG=/etc/hostapd/hostapd.conf +DNSMASQ_CFG=/etc/dnsmasq.conf +DHCPCD_CFG=/etc/dhcpcd.conf +WPA_CFG=/etc/wpa_supplicant/wpa_supplicant.conf + + +function query_config() { + if [ -e $WLAN0_CFG ]; then + SSID=$(grep wpa-ssid $WLAN0_CFG | + sed 's/^[[:space:]]*wpa-ssid "\([^"]*\)"/\1/') + echo "{\"ssid\": \"$SSID\", \"mode\": \"client\"}" + + else + if [ -e $HOSTAPD_CFG -a -e /etc/default/hostapd ]; then + SSID=$(grep ^ssid= $HOSTAPD_CFG | sed 's/^ssid=\(.*\)$/\1/') + CHANNEL=$(grep ^channel= $HOSTAPD_CFG | + sed 's/^channel=\(.*\)$/\1/') + + echo -n "{\"ssid\": \"$SSID\", " + echo "\"channel\": $CHANNEL, \"mode\": \"ap\"}" + + else + echo "{\"mode\": \"disabled\"}" + fi + fi + +} + + +function disable_wifi() { + rm -f $WLAN0_CFG $HOSTAPD_CFG /etc/default/hostapd +} + + +function configure_wlan0() { + echo "auto wlan0" + echo "allow-hotplug wlan0" + echo "iface wlan0 inet dhcp" + echo " wpa-scan-ssid 1" + echo " wpa-ap-scan 1" + echo " wpa-key-mgmt WPA-PSK" + echo " wpa-proto RSN WPA" + echo " wpa-pairwise CCMP TKIP" + echo " wpa-group CCMP TKIP" + echo " wpa-ssid \"$SSID\"" + + if [ ${#PASS} -ne 0 ]; then + echo " wpa-psk \"$PASS\"" + fi +} + + +function configure_wpa() { + echo "country=US" + echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev" + echo "update_config=1" + + if [ ${#PASS} -eq 0 ]; then + echo "network={" + echo " ssid=\"$SSID\"" + echo " key_mgmt=NONE" + echo "}" + fi +} + + +function configure_dhcpcd() { + echo "hostname" + echo "clientid" + echo "persistent" + echo "option rapid_commit" + echo "option domain_name_servers, domain_name, domain_search, host_name" + echo "option classless_static_routes" + echo "option ntp_servers" + echo "option interface_mtu" + echo "require dhcp_server_identifier" + echo "slaac private" + + if $AP; then + echo + echo "interface wlan0" + echo " static ip_address=192.168.43.1/24" + fi +} + + +function configure_wifi() { + disable_wifi + echo "source-directory /etc/network/interfaces.d" > /etc/network/interfaces + configure_wlan0 > $WLAN0_CFG + configure_wpa > $WPA_CFG + configure_dhcpcd > $DHCPCD_CFG +} + + +function configure_dnsmasq() { + echo "interface=wlan0" + echo "domain-needed" + echo "bogus-priv" + echo "dhcp-range=192.168.43.2,192.168.43.20,255.255.255.0,12h" +} + + +function configure_hostapd() { + echo "interface=wlan0" + echo "driver=nl80211" + echo "ssid=$SSID" + echo "hw_mode=g" + echo "channel=$CHANNEL" + echo "wmm_enabled=0" + echo "macaddr_acl=0" + echo "auth_algs=1" + echo "ignore_broadcast_ssid=0" + echo "wpa=2" + echo "wpa_passphrase=$PASS" + echo "wpa_key_mgmt=WPA-PSK" + echo "wpa_pairwise=TKIP" + echo "rsn_pairwise=CCMP" +} + + +function is_installed() { + dpkg-query -W --showformat='${Status}' $1 | + grep "install ok installed" >/dev/null + if [ $? -eq 0 ]; then echo true; else echo false; fi +} + + +function configure_ap() { + disable_wifi + + # Install packages + ( + $(is_installed dnsmasq) && + $(is_installed hostapd) && + $(is_installed iptables-persistent) + + ) || ( + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -yq dnsmasq hostapd iptables-persistent + ) + + configure_dhcpcd > $DHCPCD_CFG + configure_dnsmasq > $DNSMASQ_CFG + configure_hostapd > $HOSTAPD_CFG + + echo "DAEMON_CONF=\"/etc/hostapd/hostapd.conf\"" > /etc/default/hostapd + + # Enable IP forwarding + sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf + echo 1 > /proc/sys/net/ipv4/ip_forward + + # Enable IP masquerading + iptables -t nat -F + iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE + iptables-save > /etc/iptables/rules.v4 +} + + +function usage() { + echo "Usage: config-wifi [OPTIONS]" + echo + echo "Configure wifi as either a client or access point." + echo + echo "OPTIONS:" + echo + echo " -a Configure access point." + echo " -d Disable wifi." + echo " -r Reboot when done." + echo " -s Set SSID." + echo " -p Set password." + echo " -c Set wifi channel." + echo " -j Report wifi config as JSON data." + echo +} + + +# Parse args +while [ $# -ne 0 ]; do + case "$1" in + -a) AP=true ;; + -d) DISABLE=true ;; + -r) REBOOT=true; ;; + -s) SSID="$2"; shift ;; + -p) PASS="$2"; shift ;; + -c) CHANNEL="$2"; shift ;; + -j) query_config; exit 0 ;; + + -h) + usage + exit 0 + ;; + + *) + usage + echo "Unknown argument '$1'" + exit 1 + esac + + shift +done + + +if $DISABLE; then + disable_wifi + +else + # Check args + function clean_str() { + echo "$1" | tr -d '\n\r"' + } + + SSID=$(clean_str "$SSID") + PASS=$(clean_str "$PASS") + + LANG=C LC_ALL=C # For correct string byte length + + if [ ${#SSID} -eq 0 -o 32 -lt ${#SSID} ]; then + echo "Invalid or missing SSID '$SSID'" + exit 1 + fi + + if [ ${#PASS} -ne 0 ]; then + if [ ${#PASS} -lt 8 -o 128 -lt ${#PASS} ]; then + echo "Invalid passsword" + exit 1 + fi + fi + + echo "$CHANNEL" | grep '^[0-9]\{1,2\}' > /dev/null + if [ $? -ne 0 ]; then + echo "Invalid channel '$CHANNEL'" + exit 1 + fi + + # Execute + if $AP; then + echo "Configuring Wifi access point" + configure_ap + + else + echo "Configuring Wifi" + configure_wifi + fi +fi + + +if $REBOOT; then nohup reboot & fi diff --git a/scripts/demo-chroot b/scripts/demo-chroot new file mode 100644 index 0000000..75e7578 --- /dev/null +++ b/scripts/demo-chroot @@ -0,0 +1,17 @@ +#!/bin/bash -ex + +ROOT="$PWD/demo" + +# Clean up on EXIT +function cleanup { + umount "$ROOT"/{dev/pts,dev,sys,proc} 2>/dev/null || true +} +trap cleanup EXIT + +# mount binds +mount --bind /dev "$ROOT/dev/" +mount --bind /sys "$ROOT/sys/" +mount --bind /proc "$ROOT/proc/" +mount --bind /dev/pts "$ROOT/dev/pts" + +chroot "$ROOT" "$@" diff --git a/scripts/edit-boot-config b/scripts/edit-boot-config new file mode 100644 index 0000000..d7dd4d4 --- /dev/null +++ b/scripts/edit-boot-config @@ -0,0 +1,12 @@ +#!/bin/bash + +cp /boot/config.txt /tmp/ + +edit-config /tmp/config.txt "$@" + +diff /boot/config.txt /tmp/config.txt >/dev/null +if [ $? -eq 1 ]; then + mount -o remount,rw /boot + mv /tmp/config.txt /boot/ + mount -o remount,ro /boot +fi diff --git a/scripts/edit-config b/scripts/edit-config new file mode 100644 index 0000000..05377d6 --- /dev/null +++ b/scripts/edit-config @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +import sys +import argparse +import re + + +def option_type(s, pat = re.compile(r'\w+=.*')): + if not pat.match(s): raise argparse.ArgumentTypeError + return s + + +parser = argparse.ArgumentParser(description = 'Edit config file.') +parser.add_argument('config', help = 'The configuration file to edit.') +parser.add_argument('options', nargs = '*', type = option_type, + metavar = '=', help = 'An option to set.') +args = parser.parse_args() + + +# Parse config lines and options +lines = [] +options = {} +optRE = re.compile(r'(\w+)\s*=\s*([^#]+)(#.*)?') + + +class Line: + def __init__(self, text, name = None, value = None, comment = None): + self.text = text + self.name = name + self.value = value + self.comment = comment + + + def __str__(self): + if self.name is not None: + if self.value.strip(): + text = '%s=%s' % (self.name, self.value) + if self.comment: text += ' # ' + self.comment + text += '\n' + return text + + else: return '' + + return self.text + + +with open(args.config, 'r') as f: + for line in f: + m = optRE.match(line.strip()) + + if m: name, value, comment = m.groups() + else: name, value, comment = None, None, None + + l = Line(line, name, value, comment) + lines.append(l) + if name is not None: options[name] = l + + +# Save original config +config = ''.join(map(str, lines)) + + +# Set options +first = True +for opt in args.options: + name, value = opt.split('=', 1) + + if name in options: options[name].value = value + else: + if first and len(lines) and str(lines[:-1]).strip() != '': + first = False + lines.append(Line('\n')) + + lines.append(Line(None, name, value, None)) + + +# Assemble new config +new_config = ''.join(map(str, lines)) + + +if new_config != config: + with open(args.config, 'w') as f: + f.write(new_config) diff --git a/scripts/gplan-build.sh b/scripts/gplan-build.sh new file mode 100644 index 0000000..a21237d --- /dev/null +++ b/scripts/gplan-build.sh @@ -0,0 +1,6 @@ +#!/bin/bash -ex + +cd /mnt/host +scons -C cbang disable_local="re2 libevent" +export CBANG_HOME="/mnt/host/cbang" +scons -C camotics gplan.so with_gui=0 with_tpl=0 diff --git a/scripts/gplan-init-build.sh b/scripts/gplan-init-build.sh new file mode 100644 index 0000000..6459f49 --- /dev/null +++ b/scripts/gplan-init-build.sh @@ -0,0 +1,59 @@ +#!/bin/bash -ex + +IMG_DATE=2017-11-29 +IMG_BASE=${IMG_DATE}-raspbian-stretch-lite +BASE_URL=https://downloads.raspberrypi.org/raspbian_lite/images +IMG_URL=$BASE_URL/raspbian_lite-2017-12-01/$IMG_BASE.zip +GPLAN_IMG=gplan-dev.img + +# Create dev image +if [ ! -e $GPLAN_IMG ]; then + + # Get base image + if [ ! -e $IMG_BASE.img ]; then + if [ ! -e $IMG_BASE.zip ]; then + wget $IMG_URL + fi + + unzip $IMG_BASE.zip + fi + + # Copy base image + cp $IMG_BASE.img $GPLAN_IMG.tmp + + # Init image + mkdir -p rpi-share + cp ./scripts/gplan-init-dev-img.sh rpi-share + sudo ./scripts/rpi-chroot.sh $GPLAN_IMG.tmp /mnt/host/gplan-init-dev-img.sh + + # Move image + mv $GPLAN_IMG.tmp $GPLAN_IMG +fi + +# Get repos +function fetch_local_repo() { + mkdir -p $1 + git -C $1 init + git -C $1 fetch -t "$2" $3 + git -C $1 reset --hard FETCH_HEAD +} + +mkdir -p rpi-share || true + +if [ ! -e rpi-share/cbang ]; then + if [ "$CBANG_HOME" != "" ]; then + fetch_local_repo rpi-share/cbang "$CBANG_HOME" master + else + git clone https://github.com/CauldronDevelopmentLLC/cbang \ + rpi-share/cbang + fi +fi + +if [ ! -e rpi-share/camotics ]; then + if [ "$CAMOTICS_HOME" != "" ]; then + fetch_local_repo rpi-share/camotics "$CAMOTICS_HOME" master + else + git clone https://github.com/CauldronDevelopmentLLC/camotics \ + rpi-share/camotics + fi +fi diff --git a/scripts/gplan-init-dev-img.sh b/scripts/gplan-init-dev-img.sh new file mode 100644 index 0000000..cfa9d4e --- /dev/null +++ b/scripts/gplan-init-dev-img.sh @@ -0,0 +1,11 @@ +#!/bin/bash -e + +export LC_ALL=C +cd /mnt/host + +# Update the system +apt-get update +#apt-get dist-upgrade -y + +# Install packages +apt-get install -y scons build-essential libssl-dev python3-dev diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 0000000..024b836 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +UPDATE_AVR=true +UPDATE_PY=true +REBOOT=false + +while [ $# -gt 0 ]; do + case "$1" in + --no-avr) UPDATE_AVR=false ;; + --no-py) UPDATE_PY=false ;; + esac + shift 1 +done + + +if $UPDATE_PY; then + systemctl stop bbctrl + + # Update service + rm -f /etc/init.d/bbctrl + cp scripts/bbctrl.service /etc/systemd/system/ + systemctl daemon-reload + systemctl enable bbctrl +fi + +if $UPDATE_AVR; then + ./scripts/avr109-flash.py src/avr/bbctrl-avr-firmware.hex +fi + +# Update config.txt +./scripts/edit-boot-config max_usb_current=1 +./scripts/edit-boot-config config_hdmi_boost=8 + +# TODO Enable GPU +#./scripts/edit-boot-config dtoverlay=vc4-kms-v3d +#./scripts/edit-boot-config gpu_mem=16 +#chmod ug+s /usr/lib/xorg/Xorg + +# Fix camera +grep dwc_otg.fiq_fsm_mask /boot/cmdline.txt >/dev/null +if [ $? -ne 0 ]; then + mount -o remount,rw /boot && + sed -i 's/\(.*\)/\1 dwc_otg.fiq_fsm_mask=0x3/' /boot/cmdline.txt + mount -o remount,ro /boot + REBOOT=true +fi + +# Enable memory cgroups +grep cgroup_memory /boot/cmdline.txt >/dev/null +if [ $? -ne 0 ]; then + mount -o remount,rw /boot && + sed -i 's/\(.*\)/\1 cgroup_memory=1/' /boot/cmdline.txt + mount -o remount,ro /boot + REBOOT=true +fi + +# Remove Hawkeye +if [ -e /etc/init.d/hawkeye ]; then + apt-get remove --purge -y hawkeye +fi + +# Decrease boot delay +sed -i 's/^TimeoutStartSec=.*$/TimeoutStartSec=1/' \ + /etc/systemd/system/network-online.target.wants/networking.service + +# Change to US keyboard layout +sed -i 's/^XKBLAYOUT="gb"$/XKBLAYOUT="us" # Comment stops change on upgrade/' \ + /etc/default/keyboard + +# Setup USB stick automount +diff ./scripts/11-automount.rules /etc/udev/rules.d/11-automount.rules \ + >/dev/null +if [ $? -ne 0 ]; then + cp ./scripts/11-automount.rules /etc/udev/rules.d/ + sed -i 's/^\(MountFlags=slave\)/#\1/' \ + /lib/systemd/system/systemd-udevd.service + REBOOT=true +fi + +# Increase swap +grep 'CONF_SWAPSIZE=1000' /etc/dphys-swapfile >/dev/null +if [ $? -ne 0 ]; then + sed -i 's/^CONF_SWAPSIZE=.*$/CONF_SWAPSIZE=1000/' /etc/dphys-swapfile + REBOOT=true +fi + +# Install xinitrc +cp scripts/xinitrc ~pi/.xinitrc +chmod +x ~pi/.xinitrc +chown pi:pi ~pi/.xinitrc + +# Install bbserial +MODSRC=src/bbserial/bbserial.ko +MODDST=/lib/modules/$(uname -r)/kernel/drivers/tty/serial/bbserial.ko +diff -q $MODSRC $MODDST 2>/dev/null >/dev/null +if [ $? -ne 0 ]; then + cp $MODSRC $MODDST + depmod + REBOOT=true +fi + +# Install rc.local +cp scripts/rc.local /etc/ + +# Install bbctrl +if $UPDATE_PY; then + rm -rf /usr/local/lib/python*/dist-packages/bbctrl-* + ./setup.py install --force + service bbctrl restart + HTTP_DIR=$(find /usr/local/lib/ -type d -name "http") + chmod 777 $HTTP_DIR +fi + +sync + +if $REBOOT; then + echo "Rebooting" + reboot +fi + +echo "Install complete" diff --git a/scripts/log-position-to-gcode b/scripts/log-position-to-gcode new file mode 100644 index 0000000..7597fb4 --- /dev/null +++ b/scripts/log-position-to-gcode @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +import json +import sys + +p = [0, 0, 0] + + +print('F400 G21') + +for line in sys.stdin: + try: + if not line.startswith('I:Comm:> '): continue + line = line[9:] + + data = json.loads(line) + + changed = False + for axis in range(3): + var = 'xyz'[axis] + 'p' + if var in data: + p[axis] = data[var] + changed = True + + if changed: print('G1 X%d Y%d Z%d' % tuple(p)) + + except json.decoder.JSONDecodeError: pass diff --git a/scripts/next-rc b/scripts/next-rc new file mode 100644 index 0000000..2edc50a --- /dev/null +++ b/scripts/next-rc @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +import os +import json + +if os.path.exists('dist/latest-beta.txt'): + with open('dist/latest-beta.txt', 'r') as f: + latest_beta = f.read().strip() + +else: latest_beta = '' + +with open('package.json', 'r') as f: + version = json.load(f)['version'] + +if latest_beta.startswith(version + '-rc'): + print(int(latest_beta[len(version) + 3:]) + 1) + +else: + print(1) diff --git a/scripts/ratpoisonrc b/scripts/ratpoisonrc new file mode 100644 index 0000000..3337674 --- /dev/null +++ b/scripts/ratpoisonrc @@ -0,0 +1 @@ +startup_message off diff --git a/scripts/rc.local b/scripts/rc.local new file mode 100644 index 0000000..43e549a --- /dev/null +++ b/scripts/rc.local @@ -0,0 +1,31 @@ +#!/bin/bash + +# Mount /boot read only +mount -o remount,ro /boot + +# Load bbserial +echo 3f201000.serial > /sys/bus/amba/drivers/uart-pl011/unbind +modprobe -r bbserial +modprobe bbserial + +# Set SPI GPIO mode +gpio mode 27 alt3 + +# Create browser memory limited cgroup +if [ -d sys/fs/cgroup/memory ]; then + CGROUP=/sys/fs/cgroup/memory/chrome + mkdir $CGROUP + chown -R pi:pi $CGROUP + echo 650000000 > $CGROUP/memory.soft_limit_in_bytes + echo 750000000 > $CGROUP/memory.limit_in_bytes +fi + +# Reload udev +/etc/init.d/udev restart + +# Stop boot splash so it doesn't interfere with X if GPU enabled and to save CPU +plymouth quit + +# Start X in /home/pi +cd /home/pi +sudo -u pi startx diff --git a/scripts/reset-video b/scripts/reset-video new file mode 100644 index 0000000..94b46cd --- /dev/null +++ b/scripts/reset-video @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +import sys +import subprocess +import os.path +import time + +if not os.path.exists('/dev/video0'): + print('/dev/video0 not found') + sys.exit(1) + +p = subprocess.Popen('udevadm info -q path /dev/video0'.split(), + stdout = subprocess.PIPE) +s = p.communicate()[0].decode('utf-8') +dev = s.split('/')[7] + +with open('/sys/bus/usb/drivers/usb/unbind', 'w') as f: + f.write(dev) + +time.sleep(1) + +with open('/sys/bus/usb/drivers/usb/bind', 'w') as f: + f.write(dev) diff --git a/scripts/rpi-chroot.sh b/scripts/rpi-chroot.sh new file mode 100644 index 0000000..f7b1f0b --- /dev/null +++ b/scripts/rpi-chroot.sh @@ -0,0 +1,63 @@ +#!/bin/bash -ex + +ROOT="$PWD/rpi-root" +LOOP=9 + +if [ $# -lt 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +IMAGE="$1" +LOOP_DEV=/dev/loop${LOOP} +EXEC= + +if [ $# -gt 1 ]; then + shift + EXEC="$@" +fi + +# install dependecies +if [ ! -e /usr/bin/qemu-arm-static ]; then + apt-get update + apt-get install -y qemu qemu-user-static binfmt-support +fi + +# Clean up on EXIT +function cleanup { + umount "$ROOT"/{dev/pts,dev,sys,proc,boot,mnt/host,} 2>/dev/null || true + losetup -d $LOOP_DEV 2>/dev/null || true + rmdir "$ROOT" 2>/dev/null || true +} +trap cleanup EXIT + +# set up image as loop device +losetup $LOOP_DEV "$IMAGE" +partprobe $LOOP_DEV + +# check and fix filesystems +fsck -f ${LOOP_DEV}p1 +fsck -f ${LOOP_DEV}p2 + +# make dir +mkdir -p "$ROOT" + +# mount partition +mount -o rw ${LOOP_DEV}p2 -t ext4 "$ROOT" +mount -o rw ${LOOP_DEV}p1 "$ROOT/boot" + +# mount binds +mount --bind /dev "$ROOT/dev/" +mount --bind /sys "$ROOT/sys/" +mount --bind /proc "$ROOT/proc/" +mount --bind /dev/pts "$ROOT/dev/pts" +if [ -e ./rpi-share ]; then + mkdir -p "$ROOT/mnt/host" + mount --bind ./rpi-share "$ROOT/mnt/host" +fi + +# copy qemu binary +cp /usr/bin/qemu-arm-static "$ROOT/usr/bin/" + +# chroot to raspbian +chroot "$ROOT" $EXEC diff --git a/scripts/sethostname b/scripts/sethostname new file mode 100644 index 0000000..73093b7 --- /dev/null +++ b/scripts/sethostname @@ -0,0 +1,26 @@ +#!/bin/bash -e + +HOSTNAME="$(echo "$1" | tr '[:upper:]' '[:lower:]')" + +if [ "$HOSTNAME" == "" ]; then + echo "Usage: $0 " + exit 1 +fi + +if [ "$HOSTNAME" == "localhost" ]; then + echo "Cannot set hostname to 'localhost'" + exit 1 +fi + +if [ "$HOSTNAME" =~ ^.*\.local$ ]; then + echo "Hostname cannot end with '.local'" + exit 1 +fi + +if [[ ! "$HOSTNAME" =~ ^[a-zA-Z][a-zA-Z0-9-]{0,62}$ ]]; then + echo "Invalid hostname '$HOSTNAME'" + exit 1 +fi + +sed -i "s/^127.0.1.1\([[:space:]]*\).*$/127.0.1.1\1$HOSTNAME/" /etc/hosts +echo "$HOSTNAME" > /etc/hostname diff --git a/scripts/setup_rpi.sh b/scripts/setup_rpi.sh new file mode 100644 index 0000000..89c8b28 --- /dev/null +++ b/scripts/setup_rpi.sh @@ -0,0 +1,110 @@ +#!/bin/bash -e + +export LC_ALL=C +cd /mnt/host + +# Update the system +apt-get update +apt-get dist-upgrade -y + +# Install packages +apt-get install -y avahi-daemon avrdude minicom python3-pip python3-smbus \ + i2c-tools python3-rpi.gpio libjpeg8 wiringpi dnsmasq hostapd \ + iptables-persistent chromium-browser xorg rpd-plym-splash samba +pip3 install --upgrade tornado sockjs-tornado pyserial + +# Clean +apt-get autoclean + +# Enable avahi +update-rc.d avahi-daemon defaults + +# Change hostname +sed -i "s/raspberrypi/bbctrl/" /etc/hosts /etc/hostname + +# Create bbmc user +useradd -m -p $(openssl passwd -1 buildbotics) -s /bin/bash bbmc +sed -i 's/pi$/pi,bbmc/g' /etc/group +passwd -l pi + +# Disable console on serial port +sed -i 's/console=[a-zA-Z0-9]*,115200 \?//' /boot/cmdline.txt + +# Disable i2c HAT ID probe +echo -n " bcm2708.vc_i2c_override=1" >> /boot/cmdline.txt + +# Enable I2C +sed -i 's/#dtparam=i2c/dtparam=i2c/' /boot/config.txt +#echo 'dtparam=i2c_vc=on' >> /boot/config.txt +echo i2c-bcm2708 >> /etc/modules +echo i2c-dev >> /etc/modules + +# Install bbctrl w/ init.d script +cp bbctrl.init.d /etc/init.d/bbctrl +chmod +x /etc/init.d/bbctrl +update-rc.d bbctrl defaults + +# Disable Pi 3 USART BlueTooth swap +echo -e "\ndtoverlay=pi3-disable-bt" >> /boot/config.txt +rm -f /etc/systemd/system/multi-user.target.wants/hciuart.service + +# Install hawkeye +dpkg -i hawkeye_0.6_armhf.deb +sed -i 's/localhost/0.0.0.0/' /etc/hawkeye/hawkeye.conf +echo 'ACTION=="add", KERNEL=="video0", RUN+="/usr/sbin/service hawkeye restart"' > /etc/udev/rules.d/50-hawkeye.rules +adduser hawkeye video + +# Disable HDMI to save power and remount /boot read-only +sed -i 's/^exit 0$//' /etc/rc.local +echo "mount -o remount,ro /boot" >> /etc/rc.local +echo "gpio mode 27 alt3" >> /etc/rc.local # Enable serial CTS on pin 27 + +# Dynamic clock to save power +echo -e "\n# Dynamic clock\nnohz=on" >> /boot/config.txt + +# Shave 2 sec off of boot time +echo -e "\n# Faster boot\ndtparam=sd_overclock=100" >> /boot/config.txt + +# Enable ssh +touch /boot/ssh + +# Fix boot +sed -i 's/ root=[^ ]* / root=\/dev\/mmcblk0p2/' /boot/cmdline.txt +sed -i 's/^PARTUUID=.*\/boot/\/dev\/mmcblk0p1 \/boot/' /etc/fstab +sed -i 's/^PARTUUID=.*\//\/dev\/mmcblk0p2 \//' /etc/fstab + +# Enable browser in xorg +sed -i 's/allowed_users=console/allowed_users=anybody/' /etc/X11/Xwrapper.config +echo "sudo -u pi startx" >> /etc/rc.local +cp /mnt/host/xinitrc /home/pi/.xinitrc +cp /mnt/host/ratpoisonrc /home/pi/.ratpoisonrc +cp /mnt/host/xorg.conf /etc/X11/ + +# Set screen resolution +sed -i 's/^#disable_overscan/disable_overscan/' /boot/config.txt +sed -i 's/^#framebuffer_/framebuffer_/' /boot/config.txt + +# Boot splash +mkdir -p /usr/share/plymouth/themes/buildbotics/ +cp -av /mnt/host/splash/* /usr/share/plymouth/themes/buildbotics/ +echo -n " quiet splash logo.nologo plymouth.ignore-serial-consoles" >> /boot/cmdline.txt +plymouth-set-default-theme -R buildbotics + +# Samba +# TODO install custom smb.conf +smbpasswd -a bbmc + +# Install bbctrl +tar xf /mnt/host/bbctrl-*.tar.bz2 +cd $(basename bbctrl-*.tar.bz2 .tar.bz2) +./setup.py install +cd .. +rm -rf $(basename bbctrl-*.tar.bz2 .tar.bz2) + + +# Allow any user to shutdown +chmod +s /sbin/{halt,reboot,shutdown,poweroff} + +# Clean up +apt-get autoremove -y +apt-get autoclean -y diff --git a/scripts/ssh-bbctrl b/scripts/ssh-bbctrl new file mode 100644 index 0000000..ad9ed9a --- /dev/null +++ b/scripts/ssh-bbctrl @@ -0,0 +1,16 @@ +#!/bin/bash + +USER=bbmc +HOST=bbctrl.local + +if [ $# -eq 1 ]; then + if [[ "$1" = *@ ]]; then + LOGIN="$1" + else + LOGIN=$USER@"$1" + fi +else + LOGIN=$USER@$HOST +fi + +ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$LOGIN" diff --git a/scripts/svg2abs.js b/scripts/svg2abs.js new file mode 100644 index 0000000..7cfc6dd --- /dev/null +++ b/scripts/svg2abs.js @@ -0,0 +1,64 @@ +function convertToAbsolute(path) { + var x0, y0, x1, y1, x2, y2, segs = path.pathSegList; + + for (var x = 0, y = 0, i = 0, len = segs.numberOfItems; i < len; i++) { + var seg = segs.getItem(i), c = seg.pathSegTypeAsLetter; + + if (/[MLHVCSQTA]/.test(c)){ + if ('x' in seg) x = seg.x; + if ('y' in seg) y = seg.y; + + } else { + if ('x1' in seg) x1 = x + seg.x1; + if ('x2' in seg) x2 = x + seg.x2; + if ('y1' in seg) y1 = y + seg.y1; + if ('y2' in seg) y2 = y + seg.y2; + if ('x' in seg) x += seg.x; + if ('y' in seg) y += seg.y; + + switch(c) { + case 'm': + segs.replaceItem(path.createSVGPathSegMovetoAbs(x, y), i); + break; + case 'l': + segs.replaceItem(path.createSVGPathSegLinetoAbs(x, y), i); + break; + case 'h': + segs.replaceItem(path.createSVGPathSegLinetoHorizontalAbs(x), i); + break; + case 'v': + segs.replaceItem(path.createSVGPathSegLinetoVerticalAbs(y), i); + break; + case 'c': + segs.replaceItem( + path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i); + break; + case 's': + segs.replaceItem( + path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i); + break; + case 'q': + segs.replaceItem( + path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i); + break; + case 't': + segs.replaceItem( + path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i); + break; + case 'a': + segs.replaceItem( + path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle, + seg.largeArcFlag, seg.sweepFlag), i); + break; + + case 'z': case 'Z': + x = x0; + y = y0; + break; + } + } + + // Record the start of a subpath + if (c == 'M' || c == 'm') x0 = x, y0 = y; + } +} diff --git a/scripts/update-bbctrl b/scripts/update-bbctrl new file mode 100644 index 0000000..ad4023b --- /dev/null +++ b/scripts/update-bbctrl @@ -0,0 +1,27 @@ +#!/bin/bash + +( + flock -n 9 + + UPDATE=/var/lib/bbctrl/firmware/update.tar.bz2 + + if [ ! -e "$UPDATE" ]; then + echo "Missing $UPDATE" + exit 1 + fi + + systemctl stop bbctrl + + rm -rf /tmp/update + mkdir /tmp/update + cd /tmp/update + + LOG=/var/log/bbctrl.$(date +%Y%m%d-%H%M%S).install + tar xf "$UPDATE" + cd * + ./scripts/install.sh "$*" 2>&1 > $LOG + + cd - + rm -rf /tmp/update $UPDATE + +) 9> /var/lock/bbctrl.update.lock diff --git a/scripts/upgrade-bbctrl b/scripts/upgrade-bbctrl new file mode 100644 index 0000000..a26d26e --- /dev/null +++ b/scripts/upgrade-bbctrl @@ -0,0 +1,25 @@ +#!/bin/bash -e + +( + flock -n 9 + + VERSION=$(curl -s https://raw.githubusercontent.com/OneFinityCNC/onefinity/master/latest.txt) + PKG_NAME=onefinity-$VERSION + PKG=/var/lib/bbctrl/firmware/update.tar.bz2 + PKG_URL=https://https://raw.githubusercontent.com/OneFinityCNC/onefinity/master/release/$PKG_NAME.tar.bz2 + + logger Installing bbctrl firmware $VERSION + + cd /var/lib/bbctrl + mkdir -p firmware + + echo Downloading $PKG_URL + curl -s $PKG_URL > $PKG + + /usr/local/bin/update-bbctrl + + echo Success + + logger bbctrl firmware $VERSION installed + +) 9> /var/lock/bbctrl.upgrade.lock diff --git a/scripts/xinitrc b/scripts/xinitrc new file mode 100644 index 0000000..048eac4 --- /dev/null +++ b/scripts/xinitrc @@ -0,0 +1,22 @@ +hostinfo.sh & +ratpoison & + +xset -dpms +xset s off +xset s noblank + +while true; do + tvservice -s 2>&1 | grep "state 0x40001" >/dev/null + if [ $? -ne 0 ]; then + # Clear browser errors + PREFS='/home/pi/.config/chromium/Default/Preferences' + sed -i 's/"exited_cleanly":false/"exited_cleanly":true/' $PREFS + sed -i 's/"exit_type":"Crashed"/"exit_type":"Normal"/' $PREFS + + # Start browser + /usr/local/bin/browser --no-first-run --disable-infobars \ + --noerrdialogs --disable-3d-apis http://localhost/ + fi + + sleep 1 +done diff --git a/scripts/xorg.conf b/scripts/xorg.conf new file mode 100644 index 0000000..acbd369 --- /dev/null +++ b/scripts/xorg.conf @@ -0,0 +1,3 @@ +Section "ServerFlags" + Option "DontVTSwitch" "on" +EndSection diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..ba15267 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[sdist] +formats=bztar diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..cf4069d --- /dev/null +++ b/setup.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +from setuptools import setup +import json + +pkg = json.load(open('package.json', 'r')) + + +setup( + name = pkg['name'], + version = pkg['version'], + description = 'Buildbotics Machine Controller', + long_description = open('README.md', 'rt').read(), + author = 'Joseph Coffland', + author_email = 'joseph@buildbotics.org', + platforms = ['any'], + license = pkg['license'], + url = pkg['homepage'], + package_dir = {'': 'src/py'}, + packages = ['bbctrl', 'inevent', 'lcd', 'camotics'], + include_package_data = True, + entry_points = { + 'console_scripts': [ + 'bbctrl = bbctrl:run' + ] + }, + scripts = [ + 'scripts/update-bbctrl', + 'scripts/upgrade-bbctrl', + 'scripts/sethostname', + 'scripts/reset-video', + 'scripts/config-wifi', + 'scripts/config-screen', + 'scripts/edit-config', + 'scripts/edit-boot-config', + 'scripts/browser', + ], + install_requires = 'tornado sockjs-tornado pyserial pyudev smbus2'.split(), + zip_safe = False, + ) diff --git a/src/avr/.gitignore b/src/avr/.gitignore new file mode 100644 index 0000000..abe2d5d --- /dev/null +++ b/src/avr/.gitignore @@ -0,0 +1,14 @@ +# Backup files +*~ +\#* + +build +.dep + +/*.eep +/*.hex +/*.elf +/*.lss +/*.map + +*.o diff --git a/src/avr/BezierMath.md b/src/avr/BezierMath.md new file mode 100644 index 0000000..f9da75a --- /dev/null +++ b/src/avr/BezierMath.md @@ -0,0 +1,56 @@ +# Cubic Bezier + + f(x) = A(1 - x)^3 + 3B(1 - x)^2 x + 3C(1 - x) x^2 + Dx^3 + + -Ax^3 + 3Ax^2 - 3Ax + A + 3Bx^3 - 6Bx^2 + 3Bx + -3Cx^3 + 3Cx^2 + Dx^3 + + + f(x) = (-A + 3B -3C + D)x^3 + (3A - 6B + 3C)x^2 + (-3A + 3B)x + A + + a = -A + 3B - 3C + D + b = 3A - 6B + 3C + c = -3A + 3B + d = A + + f(x) = ax^3 + bx^2 + cx + d + + integral f(x) dx = a/4 x^4 + b/3 x^3 + c/2 x^2 + dx + E + + = (-A + 3B - 3C + D)/4 x^4 + (A - 2B + B) x^3 + 3/2 (B - A) x^2 + Ax + E + +# Quintic Bezier + + A(1 - x)^5 + 5A(1 - x)^4 x + 10A(1 - x)^3 x^2 + 10B(1 - x)^2 x^3 + + 5B(1 - x)x^4 + Bx^5 + + (-6A + 6B)x^5 + (15A - 15B)x^4 + (-10A + 10B)x^3 + A + + 6(B - A)x^5 + 15(A - B)x^4 + 10(B - A)x^3 + A + + x^3 (6(B - A)x^2 + 15(A - B)x + 10(B - A)) + A + + a = 6(B - A) + b = -15(B - A) + c = 10(B - A) + d = A + + f(x) = ax^5 + bx^4 + cx^3 + d + + f(x) = (ax^2 + bx + c)x^3 + d + + + integral f(x) = a/6 x^6 + b/5 x^5 + c/4 x^4 + dx + e + + = (B - A)x^6 - 3(B - A)x^5 + 5/2(B - A)x^4 + Ax + e + + = (B - A)x^4 (x^2 - 3x + 5/2) + Ax + e + + A = 0 + B = 1 + e = 0 + + f(x) = 6x^5 -15x^4 + 10x^3 + int f(x) dx = x^6 - 3x^5 + 5/2x^4 + C diff --git a/src/avr/Makefile b/src/avr/Makefile new file mode 100644 index 0000000..dc490c4 --- /dev/null +++ b/src/avr/Makefile @@ -0,0 +1,40 @@ +# Makefile for the project Bulidbotics firmware +PROJECT = bbctrl-avr-firmware +MCU = atxmega192a3u +CLOCK = 32000000 + +# SRC +SRC = $(wildcard src/*.c) $(wildcard src/*.cpp) $(wildcard src/vfd/*.c) +OBJ := $(patsubst src/%.c,build/%.o,$(SRC)) +OBJ := $(patsubst src/%.cpp,build/%.o,$(OBJ)) +OBJ := $(patsubst src/vfd/%.c,build/vfd/%.o,$(OBJ)) +JSON = vars command messages +JSON := $(patsubst %,build/%.json,$(JSON)) + +all: $(PROJECT).hex $(JSON) size + +include Makefile.common + +CFLAGS += -Isrc + +# Build +$(PROJECT).elf: $(OBJ) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + + +# JSON +build/%.json: src/%.json.in src/%.def + cpp -Isrc $< | sed "/^#.*$$/d;s/'\(.\)'/\"\1\"/g" > $@ + +# Program +init: + $(MAKE) erase + -$(MAKE) fuses + $(MAKE) fuses + $(MAKE) program-boot + $(MAKE) program + +program-boot: + $(MAKE) -C ../boot program + +.PHONY: all init program-boot diff --git a/src/avr/Makefile.common b/src/avr/Makefile.common new file mode 100644 index 0000000..65c9480 --- /dev/null +++ b/src/avr/Makefile.common @@ -0,0 +1,115 @@ +# Compile flags +CC = avr-g++ + +COMMON = -mmcu=$(MCU) -flto -fwhole-program + +CFLAGS += $(COMMON) +CFLAGS += -Wall -Werror +CFLAGS += -Wno-error=strict-aliasing # for _invsqrt +CFLAGS += -std=gnu++98 -DF_CPU=$(CLOCK)UL -O3 +CFLAGS += -funsigned-bitfields -fpack-struct -fshort-enums -funsigned-char +CFLAGS += -MD -MP -MT $@ -MF build/dep/$(@F).d +CFLAGS += -D__STDC_LIMIT_MACROS + +# Linker flags +LDFLAGS += $(COMMON) -Wl,-u,vfprintf -lprintf_flt -lm +LIBS += -lm + +# EEPROM flags +EEFLAGS += -j .eeprom +EEFLAGS += --set-section-flags=.eeprom="alloc,load" +EEFLAGS += --change-section-lma .eeprom=0 --no-change-warnings + +# Programming flags +ifndef (PROGRAMMER) +PROGRAMMER = avrispmkII +#PROGRAMMER = jtag3pdi +endif +PDEV = usb +AVRDUDE_OPTS = -c $(PROGRAMMER) -p $(MCU) -P $(PDEV) + +FUSE0=0xff +FUSE1=0x00 +FUSE2=0xbe +FUSE4=0xff +FUSE5=0xeb + +# Compile +build/%.o: src/%.c + @mkdir -p $(shell dirname $@) + $(CC) $(CFLAGS) -c -o $@ $< + +build/%.o: src/%.cpp + @mkdir -p $(shell dirname $@) + $(CC) $(CFLAGS) -c -o $@ $< + +build/%.o: src/%.S + @mkdir -p $(shell dirname $@) + $(CC) $(CFLAGS) -c -o $@ $< + +# Link +%.hex: %.elf + avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $< $@ + +%.eep: %.elf + avr-objcopy $(EEFLAGS) -O ihex $< $@ + +%.lss: %.elf + avr-objdump -h -S $< > $@ + +size: $(PROJECT).elf + @for X in A B C; do\ + echo '****************************************************************' ;\ + avr-size -$$X --mcu=$(MCU) $(PROJECT).elf ;\ + done + +data-usage: $(PROJECT).elf + avr-nm -CS --size-sort -t decimal $(PROJECT).elf | grep ' [BbDd] ' + + +prog-usage: $(PROJECT).elf + avr-nm -CS --size-sort -t decimal $(PROJECT).elf | grep -v ' [BbDd] ' + +# Program +reset: + avrdude $(AVRDUDE_OPTS) + +erase: + avrdude $(AVRDUDE_OPTS) -e + +program: $(PROJECT).hex + avrdude $(AVRDUDE_OPTS) -U flash:w:$(PROJECT).hex:i + +verify: $(PROJECT).hex + avrdude $(AVRDUDE_OPTS) -U flash:v:$(PROJECT).hex:i + +fuses: + avrdude $(AVRDUDE_OPTS) -U fuse0:w:$(FUSE0):m -U fuse1:w:$(FUSE1):m \ + -U fuse2:w:$(FUSE2):m -U fuse4:w:$(FUSE4):m -U fuse5:w:$(FUSE5):m + +read_fuses: + avrdude $(AVRDUDE_OPTS) -q -q -U fuse0:r:-:h -U fuse1:r:-:h -U fuse2:r:-:h \ + -U fuse4:r:-:h -U fuse5:r:-:h + +signature: + avrdude $(AVRDUDE_OPTS) -q -q -U signature:r:-:h + +prodsig: + avrdude $(AVRDUDE_OPTS) -q -q -U prodsig:r:-:h + +usersig: + avrdude $(AVRDUDE_OPTS) -q -q -U usersig:r:-:h + +# Clean +tidy: + rm -f $(shell find -name \*~ -o -name \#\*) + +clean: tidy + rm -rf $(PROJECT).elf $(PROJECT).hex $(PROJECT).eep $(PROJECT).lss \ + $(PROJECT).map build + +.PHONY: tidy clean size reset erase program fuses read_fuses prodsig +.PHONY: signature usersig data-usage prog-usage + +# Dependencies +-include $(shell mkdir -p build/dep) $(wildcard build/dep/*) diff --git a/src/avr/README.md b/src/avr/README.md new file mode 100644 index 0000000..97f5eb0 --- /dev/null +++ b/src/avr/README.md @@ -0,0 +1,18 @@ +The Buildbotics firmware is a 4 axis motion control system designed for +high-performance on small to mid-sized machines. It operates in conjunction +with the Buildbotics RaspberryPi firmware. + +# Build Instructions +To build in Linux run: + + make + +Other make commands are: + + * **size** - Display program and data sizes + * **program** - program using AVR dude and an avrispmkII + * **erase** - Erase chip + * **fuses** - Write AVR fuses bytes + * **read_fuses** - Read and print AVR fuse bytes + * **clean** - Remove build files + * **tidy** - Remove backup files diff --git a/src/avr/data_usage.py b/src/avr/data_usage.py new file mode 100644 index 0000000..27863ec --- /dev/null +++ b/src/avr/data_usage.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import re +import shlex +import subprocess + + +lineRE = r'%(addr)s ' +command = 'avr-objdump -j .bss -t buildbotics.elf' + +proc = subprocess.Popen(shlex.split(command), stdout = subprocess.PIPE) + +out, err = proc.communicate() + +if proc.returncode: + print(out) + raise Exception('command failed') + +def get_sizes(data): + for line in data.decode().split('\n'): + if not re.match(r'^[0-9a-f]{8} .*', line): continue + + size, name = int(line[22:30], 16), line[31:] + if not size: continue + + yield (size, name) + +sizes = sorted(get_sizes(out)) +total = sum(x[0] for x in sizes) + +for size, name in sizes: + print('% 6d %5.2f%% %s' % (size, size / total * 100, name)) + +print('-' * 40) +print('% 6d Total' % total) diff --git a/src/avr/emu/.gitignore b/src/avr/emu/.gitignore new file mode 100644 index 0000000..0bbedea --- /dev/null +++ b/src/avr/emu/.gitignore @@ -0,0 +1 @@ +bbemu diff --git a/src/avr/emu/Makefile b/src/avr/emu/Makefile new file mode 100644 index 0000000..7f982b2 --- /dev/null +++ b/src/avr/emu/Makefile @@ -0,0 +1,38 @@ +TARGET = bbemu + +SRC:=$(wildcard ../src/*.c) $(wildcard ../src/*.cpp) +OBJ:=$(patsubst %.cpp,%.o,$(patsubst %.c,%.o,$(SRC))) +OBJ:=$(patsubst ../src/%,build/%,$(OBJ)) +SRC+=src/emu.c +OBJ+=build/emu.o + +CFLAGS = -I../src -Isrc -Wall -Werror -DDEBUG -g -std=gnu++98 +CFLAGS += -MD -MP -MT $@ -MF build/$(@F).d +CFLAGS += -DF_CPU=32000000 -Wno-class-memaccess -pthread +LDFLAGS = -lm -pthread + +all: $(TARGET) + +$(TARGET): $(OBJ) + g++ -o $@ $(OBJ) $(LDFLAGS) + +build/%.o: ../src/%.c + g++ -c -o $@ $(CFLAGS) $< + +build/%.o: src/%.c + g++ -c -o $@ $(CFLAGS) $< + +build/%.o: ../src/%.cpp + g++ -c -o $@ $(CFLAGS) $< + +# Clean +tidy: + rm -f $(shell find -name \*~ -o -name \#\*) + +clean: tidy + rm -rf $(TARGET) build + +.PHONY: tidy clean all + +# Dependencies +-include $(shell mkdir -p build) $(wildcard build/*.d) diff --git a/src/avr/emu/src/avr/eeprom.h b/src/avr/emu/src/avr/eeprom.h new file mode 100644 index 0000000..bafb8b0 --- /dev/null +++ b/src/avr/emu/src/avr/eeprom.h @@ -0,0 +1,34 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#define EEMEM + +#define eeprom_update_word(PTR, VAL) *(PTR) = (VAL) +#define eeprom_read_word(PTR) *(PTR) +#define eeprom_is_ready() true diff --git a/src/avr/emu/src/avr/interrupt.h b/src/avr/emu/src/avr/interrupt.h new file mode 100644 index 0000000..10fd67b --- /dev/null +++ b/src/avr/emu/src/avr/interrupt.h @@ -0,0 +1,35 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "io.h" + +void cli(); +void sei(); + +#define ISR(X) void __##X() diff --git a/src/avr/emu/src/avr/io.h b/src/avr/emu/src/avr/io.h new file mode 100644 index 0000000..3d0e145 --- /dev/null +++ b/src/avr/emu/src/avr/io.h @@ -0,0 +1,7718 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +/***************************************************************************** + * + * Copyright (C) 2015 Atmel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ****************************************************************************/ + +#ifndef _AVR_ATXMEGA192A3U_H_INCLUDED +#define _AVR_ATXMEGA192A3U_H_INCLUDED + +#include + +#define _SFR_MEM8(mem_addr) (*(volatile uint8_t *)(io_mem + (mem_addr))) +extern volatile uint8_t io_mem[4096]; + +/* Ungrouped common registers */ +#define GPIOR0 _SFR_MEM8(0x0000) /* General Purpose IO Register 0 */ +#define GPIOR1 _SFR_MEM8(0x0001) /* General Purpose IO Register 1 */ +#define GPIOR2 _SFR_MEM8(0x0002) /* General Purpose IO Register 2 */ +#define GPIOR3 _SFR_MEM8(0x0003) /* General Purpose IO Register 3 */ +#define GPIOR4 _SFR_MEM8(0x0004) /* General Purpose IO Register 4 */ +#define GPIOR5 _SFR_MEM8(0x0005) /* General Purpose IO Register 5 */ +#define GPIOR6 _SFR_MEM8(0x0006) /* General Purpose IO Register 6 */ +#define GPIOR7 _SFR_MEM8(0x0007) /* General Purpose IO Register 7 */ +#define GPIOR8 _SFR_MEM8(0x0008) /* General Purpose IO Register 8 */ +#define GPIOR9 _SFR_MEM8(0x0009) /* General Purpose IO Register 9 */ +#define GPIORA _SFR_MEM8(0x000A) /* General Purpose IO Register 10 */ +#define GPIORB _SFR_MEM8(0x000B) /* General Purpose IO Register 11 */ +#define GPIORC _SFR_MEM8(0x000C) /* General Purpose IO Register 12 */ +#define GPIORD _SFR_MEM8(0x000D) /* General Purpose IO Register 13 */ +#define GPIORE _SFR_MEM8(0x000E) /* General Purpose IO Register 14 */ +#define GPIORF _SFR_MEM8(0x000F) /* General Purpose IO Register 15 */ + +/* Deprecated */ +#define GPIO0 _SFR_MEM8(0x0000) /* General Purpose IO Register 0 */ +#define GPIO1 _SFR_MEM8(0x0001) /* General Purpose IO Register 1 */ +#define GPIO2 _SFR_MEM8(0x0002) /* General Purpose IO Register 2 */ +#define GPIO3 _SFR_MEM8(0x0003) /* General Purpose IO Register 3 */ +#define GPIO4 _SFR_MEM8(0x0004) /* General Purpose IO Register 4 */ +#define GPIO5 _SFR_MEM8(0x0005) /* General Purpose IO Register 5 */ +#define GPIO6 _SFR_MEM8(0x0006) /* General Purpose IO Register 6 */ +#define GPIO7 _SFR_MEM8(0x0007) /* General Purpose IO Register 7 */ +#define GPIO8 _SFR_MEM8(0x0008) /* General Purpose IO Register 8 */ +#define GPIO9 _SFR_MEM8(0x0009) /* General Purpose IO Register 9 */ +#define GPIOA _SFR_MEM8(0x000A) /* General Purpose IO Register 10 */ +#define GPIOB _SFR_MEM8(0x000B) /* General Purpose IO Register 11 */ +#define GPIOC _SFR_MEM8(0x000C) /* General Purpose IO Register 12 */ +#define GPIOD _SFR_MEM8(0x000D) /* General Purpose IO Register 13 */ +#define GPIOE _SFR_MEM8(0x000E) /* General Purpose IO Register 14 */ +#define GPIOF _SFR_MEM8(0x000F) /* General Purpose IO Register 15 */ + +#define CCP _SFR_MEM8(0x0034) /* Configuration Change Protection */ +#define RAMPD _SFR_MEM8(0x0038) /* Ramp D */ +#define RAMPX _SFR_MEM8(0x0039) /* Ramp X */ +#define RAMPY _SFR_MEM8(0x003A) /* Ramp Y */ +#define RAMPZ _SFR_MEM8(0x003B) /* Ramp Z */ +#define EIND _SFR_MEM8(0x003C) /* Extended Indirect Jump */ +#define SPL _SFR_MEM8(0x003D) /* Stack Pointer Low */ +#define SPH _SFR_MEM8(0x003E) /* Stack Pointer High */ +#define SREG _SFR_MEM8(0x003F) /* Status Register */ + +/* C Language Only */ +#if !defined (__ASSEMBLER__) + +#include + +typedef volatile uint8_t register8_t; +typedef volatile uint16_t register16_t; +typedef volatile uint32_t register32_t; + + +#ifdef _WORDREGISTER +#undef _WORDREGISTER +#endif +#define _WORDREGISTER(regname) \ + __extension__ union \ + { \ + register16_t regname; \ + struct \ + { \ + register8_t regname ## L; \ + register8_t regname ## H; \ + }; \ + } + +#ifdef _DWORDREGISTER +#undef _DWORDREGISTER +#endif +#define _DWORDREGISTER(regname) \ + __extension__ union \ + { \ + register32_t regname; \ + struct \ + { \ + register8_t regname ## 0; \ + register8_t regname ## 1; \ + register8_t regname ## 2; \ + register8_t regname ## 3; \ + }; \ + } + + +/* +========================================================================== +IO Module Structures +========================================================================== +*/ + + +/* +-------------------------------------------------------------------------- +VPORT - Virtual Ports +-------------------------------------------------------------------------- +*/ + +/* Virtual Port */ +typedef struct VPORT_struct +{ + register8_t DIR; /* I/O Port Data Direction */ + register8_t OUT; /* I/O Port Output */ + register8_t IN; /* I/O Port Input */ + register8_t INTFLAGS; /* Interrupt Flag Register */ +} VPORT_t; + + +/* +-------------------------------------------------------------------------- +XOCD - On-Chip Debug System +-------------------------------------------------------------------------- +*/ + +/* On-Chip Debug System */ +typedef struct OCD_struct +{ + register8_t OCDR0; /* OCD Register 0 */ + register8_t OCDR1; /* OCD Register 1 */ +} OCD_t; + + +/* +-------------------------------------------------------------------------- +CPU - CPU +-------------------------------------------------------------------------- +*/ + +/* CCP signatures */ +typedef enum CCP_enum +{ + CCP_SPM_gc = (0x9D<<0), /* SPM Instruction Protection */ + CCP_IOREG_gc = (0xD8<<0), /* IO Register Protection */ +} CCP_t; + + +/* +-------------------------------------------------------------------------- +CLK - Clock System +-------------------------------------------------------------------------- +*/ + +/* Clock System */ +typedef struct CLK_struct +{ + register8_t CTRL; /* Control Register */ + register8_t PSCTRL; /* Prescaler Control Register */ + register8_t LOCK; /* Lock register */ + register8_t RTCCTRL; /* RTC Control Register */ + register8_t USBCTRL; /* USB Control Register */ +} CLK_t; + + +/* Power Reduction */ +typedef struct PR_struct +{ + register8_t PRGEN; /* General Power Reduction */ + register8_t PRPA; /* Power Reduction Port A */ + register8_t PRPB; /* Power Reduction Port B */ + register8_t PRPC; /* Power Reduction Port C */ + register8_t PRPD; /* Power Reduction Port D */ + register8_t PRPE; /* Power Reduction Port E */ + register8_t PRPF; /* Power Reduction Port F */ +} PR_t; + +/* System Clock Selection */ +typedef enum CLK_SCLKSEL_enum +{ + CLK_SCLKSEL_RC2M_gc = (0x00<<0), /* Internal 2 MHz RC Oscillator */ + CLK_SCLKSEL_RC32M_gc = (0x01<<0), /* Internal 32 MHz RC Oscillator */ + CLK_SCLKSEL_RC32K_gc = (0x02<<0), /* Internal 32.768 kHz RC Oscillator */ + CLK_SCLKSEL_XOSC_gc = (0x03<<0), /* External Crystal Oscillator or Clock */ + CLK_SCLKSEL_PLL_gc = (0x04<<0), /* Phase Locked Loop */ +} CLK_SCLKSEL_t; + +/* Prescaler A Division Factor */ +typedef enum CLK_PSADIV_enum +{ + CLK_PSADIV_1_gc = (0x00<<2), /* Divide by 1 */ + CLK_PSADIV_2_gc = (0x01<<2), /* Divide by 2 */ + CLK_PSADIV_4_gc = (0x03<<2), /* Divide by 4 */ + CLK_PSADIV_8_gc = (0x05<<2), /* Divide by 8 */ + CLK_PSADIV_16_gc = (0x07<<2), /* Divide by 16 */ + CLK_PSADIV_32_gc = (0x09<<2), /* Divide by 32 */ + CLK_PSADIV_64_gc = (0x0B<<2), /* Divide by 64 */ + CLK_PSADIV_128_gc = (0x0D<<2), /* Divide by 128 */ + CLK_PSADIV_256_gc = (0x0F<<2), /* Divide by 256 */ + CLK_PSADIV_512_gc = (0x11<<2), /* Divide by 512 */ +} CLK_PSADIV_t; + +/* Prescaler B and C Division Factor */ +typedef enum CLK_PSBCDIV_enum +{ + CLK_PSBCDIV_1_1_gc = (0x00<<0), /* Divide B by 1 and C by 1 */ + CLK_PSBCDIV_1_2_gc = (0x01<<0), /* Divide B by 1 and C by 2 */ + CLK_PSBCDIV_4_1_gc = (0x02<<0), /* Divide B by 4 and C by 1 */ + CLK_PSBCDIV_2_2_gc = (0x03<<0), /* Divide B by 2 and C by 2 */ +} CLK_PSBCDIV_t; + +/* RTC Clock Source */ +typedef enum CLK_RTCSRC_enum +{ + CLK_RTCSRC_ULP_gc = (0x00<<1), /* 1.024 kHz from internal 32kHz ULP */ + CLK_RTCSRC_TOSC_gc = (0x01<<1), /* 1.024 kHz from 32.768 kHz crystal oscillator on TOSC */ + CLK_RTCSRC_RCOSC_gc = (0x02<<1), /* 1.024 kHz from internal 32.768 kHz RC oscillator */ + CLK_RTCSRC_TOSC32_gc = (0x05<<1), /* 32.768 kHz from 32.768 kHz crystal oscillator on TOSC */ + CLK_RTCSRC_RCOSC32_gc = (0x06<<1), /* 32.768 kHz from internal 32.768 kHz RC oscillator */ + CLK_RTCSRC_EXTCLK_gc = (0x07<<1), /* External Clock from TOSC1 */ +} CLK_RTCSRC_t; + +/* USB Prescaler Division Factor */ +typedef enum CLK_USBPSDIV_enum +{ + CLK_USBPSDIV_1_gc = (0x00<<3), /* Divide by 1 */ + CLK_USBPSDIV_2_gc = (0x01<<3), /* Divide by 2 */ + CLK_USBPSDIV_4_gc = (0x02<<3), /* Divide by 4 */ + CLK_USBPSDIV_8_gc = (0x03<<3), /* Divide by 8 */ + CLK_USBPSDIV_16_gc = (0x04<<3), /* Divide by 16 */ + CLK_USBPSDIV_32_gc = (0x05<<3), /* Divide by 32 */ +} CLK_USBPSDIV_t; + +/* USB Clock Source */ +typedef enum CLK_USBSRC_enum +{ + CLK_USBSRC_PLL_gc = (0x00<<1), /* PLL */ + CLK_USBSRC_RC32M_gc = (0x01<<1), /* Internal 32 MHz RC Oscillator */ +} CLK_USBSRC_t; + + +/* +-------------------------------------------------------------------------- +SLEEP - Sleep Controller +-------------------------------------------------------------------------- +*/ + +/* Sleep Controller */ +typedef struct SLEEP_struct +{ + register8_t CTRL; /* Control Register */ +} SLEEP_t; + +/* Sleep Mode */ +typedef enum SLEEP_SMODE_enum +{ + SLEEP_SMODE_IDLE_gc = (0x00<<1), /* Idle mode */ + SLEEP_SMODE_PDOWN_gc = (0x02<<1), /* Power-down Mode */ + SLEEP_SMODE_PSAVE_gc = (0x03<<1), /* Power-save Mode */ + SLEEP_SMODE_STDBY_gc = (0x06<<1), /* Standby Mode */ + SLEEP_SMODE_ESTDBY_gc = (0x07<<1), /* Extended Standby Mode */ +} SLEEP_SMODE_t; + + + +#define SLEEP_MODE_IDLE (0x00<<1) +#define SLEEP_MODE_PWR_DOWN (0x02<<1) +#define SLEEP_MODE_PWR_SAVE (0x03<<1) +#define SLEEP_MODE_STANDBY (0x06<<1) +#define SLEEP_MODE_EXT_STANDBY (0x07<<1) +/* +-------------------------------------------------------------------------- +OSC - Oscillator +-------------------------------------------------------------------------- +*/ + +/* Oscillator */ +typedef struct OSC_struct +{ + register8_t CTRL; /* Control Register */ + register8_t STATUS; /* Status Register */ + register8_t XOSCCTRL; /* External Oscillator Control Register */ + register8_t XOSCFAIL; /* Oscillator Failure Detection Register */ + register8_t RC32KCAL; /* 32.768 kHz Internal Oscillator Calibration Register */ + register8_t PLLCTRL; /* PLL Control Register */ + register8_t DFLLCTRL; /* DFLL Control Register */ +} OSC_t; + +/* Oscillator Frequency Range */ +typedef enum OSC_FRQRANGE_enum +{ + OSC_FRQRANGE_04TO2_gc = (0x00<<6), /* 0.4 - 2 MHz */ + OSC_FRQRANGE_2TO9_gc = (0x01<<6), /* 2 - 9 MHz */ + OSC_FRQRANGE_9TO12_gc = (0x02<<6), /* 9 - 12 MHz */ + OSC_FRQRANGE_12TO16_gc = (0x03<<6), /* 12 - 16 MHz */ +} OSC_FRQRANGE_t; + +/* External Oscillator Selection and Startup Time */ +typedef enum OSC_XOSCSEL_enum +{ + OSC_XOSCSEL_EXTCLK_gc = (0x00<<0), /* External Clock - 6 CLK */ + OSC_XOSCSEL_32KHz_gc = (0x02<<0), /* 32.768 kHz TOSC - 32K CLK */ + OSC_XOSCSEL_XTAL_256CLK_gc = (0x03<<0), /* 0.4-16 MHz XTAL - 256 CLK */ + OSC_XOSCSEL_XTAL_1KCLK_gc = (0x07<<0), /* 0.4-16 MHz XTAL - 1K CLK */ + OSC_XOSCSEL_XTAL_16KCLK_gc = (0x0B<<0), /* 0.4-16 MHz XTAL - 16K CLK */ +} OSC_XOSCSEL_t; + +/* PLL Clock Source */ +typedef enum OSC_PLLSRC_enum +{ + OSC_PLLSRC_RC2M_gc = (0x00<<6), /* Internal 2 MHz RC Oscillator */ + OSC_PLLSRC_RC32M_gc = (0x02<<6), /* Internal 32 MHz RC Oscillator */ + OSC_PLLSRC_XOSC_gc = (0x03<<6), /* External Oscillator */ +} OSC_PLLSRC_t; + +/* 2 MHz DFLL Calibration Reference */ +typedef enum OSC_RC2MCREF_enum +{ + OSC_RC2MCREF_RC32K_gc = (0x00<<0), /* Internal 32.768 kHz RC Oscillator */ + OSC_RC2MCREF_XOSC32K_gc = (0x01<<0), /* External 32.768 kHz Crystal Oscillator */ +} OSC_RC2MCREF_t; + +/* 32 MHz DFLL Calibration Reference */ +typedef enum OSC_RC32MCREF_enum +{ + OSC_RC32MCREF_RC32K_gc = (0x00<<1), /* Internal 32.768 kHz RC Oscillator */ + OSC_RC32MCREF_XOSC32K_gc = (0x01<<1), /* External 32.768 kHz Crystal Oscillator */ + OSC_RC32MCREF_USBSOF_gc = (0x02<<1), /* USB Start of Frame */ +} OSC_RC32MCREF_t; + + +/* +-------------------------------------------------------------------------- +DFLL - DFLL +-------------------------------------------------------------------------- +*/ + +/* DFLL */ +typedef struct DFLL_struct +{ + register8_t CTRL; /* Control Register */ + register8_t reserved_0x01; + register8_t CALA; /* Calibration Register A */ + register8_t CALB; /* Calibration Register B */ + register8_t COMP0; /* Oscillator Compare Register 0 */ + register8_t COMP1; /* Oscillator Compare Register 1 */ + register8_t COMP2; /* Oscillator Compare Register 2 */ + register8_t reserved_0x07; +} DFLL_t; + + +/* +-------------------------------------------------------------------------- +RST - Reset +-------------------------------------------------------------------------- +*/ + +/* Reset */ +typedef struct RST_struct +{ + register8_t STATUS; /* Status Register */ + register8_t CTRL; /* Control Register */ +} RST_t; + + +/* +-------------------------------------------------------------------------- +WDT - Watch-Dog Timer +-------------------------------------------------------------------------- +*/ + +/* Watch-Dog Timer */ +typedef struct WDT_struct +{ + register8_t CTRL; /* Control */ + register8_t WINCTRL; /* Windowed Mode Control */ + register8_t STATUS; /* Status */ +} WDT_t; + +/* Period setting */ +typedef enum WDT_PER_enum +{ + WDT_PER_8CLK_gc = (0x00<<2), /* 8 cycles (8ms @ 3.3V) */ + WDT_PER_16CLK_gc = (0x01<<2), /* 16 cycles (16ms @ 3.3V) */ + WDT_PER_32CLK_gc = (0x02<<2), /* 32 cycles (32ms @ 3.3V) */ + WDT_PER_64CLK_gc = (0x03<<2), /* 64 cycles (64ms @ 3.3V) */ + WDT_PER_128CLK_gc = (0x04<<2), /* 128 cycles (0.128s @ 3.3V) */ + WDT_PER_256CLK_gc = (0x05<<2), /* 256 cycles (0.256s @ 3.3V) */ + WDT_PER_512CLK_gc = (0x06<<2), /* 512 cycles (0.512s @ 3.3V) */ + WDT_PER_1KCLK_gc = (0x07<<2), /* 1K cycles (1s @ 3.3V) */ + WDT_PER_2KCLK_gc = (0x08<<2), /* 2K cycles (2s @ 3.3V) */ + WDT_PER_4KCLK_gc = (0x09<<2), /* 4K cycles (4s @ 3.3V) */ + WDT_PER_8KCLK_gc = (0x0A<<2), /* 8K cycles (8s @ 3.3V) */ +} WDT_PER_t; + +/* Closed window period */ +typedef enum WDT_WPER_enum +{ + WDT_WPER_8CLK_gc = (0x00<<2), /* 8 cycles (8ms @ 3.3V) */ + WDT_WPER_16CLK_gc = (0x01<<2), /* 16 cycles (16ms @ 3.3V) */ + WDT_WPER_32CLK_gc = (0x02<<2), /* 32 cycles (32ms @ 3.3V) */ + WDT_WPER_64CLK_gc = (0x03<<2), /* 64 cycles (64ms @ 3.3V) */ + WDT_WPER_128CLK_gc = (0x04<<2), /* 128 cycles (0.128s @ 3.3V) */ + WDT_WPER_256CLK_gc = (0x05<<2), /* 256 cycles (0.256s @ 3.3V) */ + WDT_WPER_512CLK_gc = (0x06<<2), /* 512 cycles (0.512s @ 3.3V) */ + WDT_WPER_1KCLK_gc = (0x07<<2), /* 1K cycles (1s @ 3.3V) */ + WDT_WPER_2KCLK_gc = (0x08<<2), /* 2K cycles (2s @ 3.3V) */ + WDT_WPER_4KCLK_gc = (0x09<<2), /* 4K cycles (4s @ 3.3V) */ + WDT_WPER_8KCLK_gc = (0x0A<<2), /* 8K cycles (8s @ 3.3V) */ +} WDT_WPER_t; + + +/* +-------------------------------------------------------------------------- +MCU - MCU Control +-------------------------------------------------------------------------- +*/ + +/* MCU Control */ +typedef struct MCU_struct +{ + register8_t DEVID0; /* Device ID byte 0 */ + register8_t DEVID1; /* Device ID byte 1 */ + register8_t DEVID2; /* Device ID byte 2 */ + register8_t REVID; /* Revision ID */ + register8_t JTAGUID; /* JTAG User ID */ + register8_t reserved_0x05; + register8_t MCUCR; /* MCU Control */ + register8_t ANAINIT; /* Analog Startup Delay */ + register8_t EVSYSLOCK; /* Event System Lock */ + register8_t AWEXLOCK; /* AWEX Lock */ + register8_t reserved_0x0A; + register8_t reserved_0x0B; +} MCU_t; + + +/* +-------------------------------------------------------------------------- +PMIC - Programmable Multi-level Interrupt Controller +-------------------------------------------------------------------------- +*/ + +/* Programmable Multi-level Interrupt Controller */ +typedef struct PMIC_struct +{ + register8_t STATUS; /* Status Register */ + register8_t INTPRI; /* Interrupt Priority */ + register8_t CTRL; /* Control Register */ + register8_t reserved_0x03; + register8_t reserved_0x04; + register8_t reserved_0x05; + register8_t reserved_0x06; + register8_t reserved_0x07; + register8_t reserved_0x08; + register8_t reserved_0x09; + register8_t reserved_0x0A; + register8_t reserved_0x0B; + register8_t reserved_0x0C; + register8_t reserved_0x0D; + register8_t reserved_0x0E; + register8_t reserved_0x0F; +} PMIC_t; + + +/* +-------------------------------------------------------------------------- +PORTCFG - Port Configuration +-------------------------------------------------------------------------- +*/ + +/* I/O port Configuration */ +typedef struct PORTCFG_struct +{ + register8_t MPCMASK; /* Multi-pin Configuration Mask */ + register8_t reserved_0x01; + register8_t VPCTRLA; /* Virtual Port Control Register A */ + register8_t VPCTRLB; /* Virtual Port Control Register B */ + register8_t CLKEVOUT; /* Clock and Event Out Register */ + register8_t EBIOUT; /* EBI Output register */ + register8_t EVOUTSEL; /* Event Output Select */ +} PORTCFG_t; + +/* Virtual Port Mapping */ +typedef enum PORTCFG_VP02MAP_enum +{ + PORTCFG_VP02MAP_PORTA_gc = (0x00<<0), /* Mapped To PORTA */ + PORTCFG_VP02MAP_PORTB_gc = (0x01<<0), /* Mapped To PORTB */ + PORTCFG_VP02MAP_PORTC_gc = (0x02<<0), /* Mapped To PORTC */ + PORTCFG_VP02MAP_PORTD_gc = (0x03<<0), /* Mapped To PORTD */ + PORTCFG_VP02MAP_PORTE_gc = (0x04<<0), /* Mapped To PORTE */ + PORTCFG_VP02MAP_PORTF_gc = (0x05<<0), /* Mapped To PORTF */ + PORTCFG_VP02MAP_PORTG_gc = (0x06<<0), /* Mapped To PORTG */ + PORTCFG_VP02MAP_PORTH_gc = (0x07<<0), /* Mapped To PORTH */ + PORTCFG_VP02MAP_PORTJ_gc = (0x08<<0), /* Mapped To PORTJ */ + PORTCFG_VP02MAP_PORTK_gc = (0x09<<0), /* Mapped To PORTK */ + PORTCFG_VP02MAP_PORTL_gc = (0x0A<<0), /* Mapped To PORTL */ + PORTCFG_VP02MAP_PORTM_gc = (0x0B<<0), /* Mapped To PORTM */ + PORTCFG_VP02MAP_PORTN_gc = (0x0C<<0), /* Mapped To PORTN */ + PORTCFG_VP02MAP_PORTP_gc = (0x0D<<0), /* Mapped To PORTP */ + PORTCFG_VP02MAP_PORTQ_gc = (0x0E<<0), /* Mapped To PORTQ */ + PORTCFG_VP02MAP_PORTR_gc = (0x0F<<0), /* Mapped To PORTR */ +} PORTCFG_VP02MAP_t; + +/* Virtual Port Mapping */ +typedef enum PORTCFG_VP13MAP_enum +{ + PORTCFG_VP13MAP_PORTA_gc = (0x00<<4), /* Mapped To PORTA */ + PORTCFG_VP13MAP_PORTB_gc = (0x01<<4), /* Mapped To PORTB */ + PORTCFG_VP13MAP_PORTC_gc = (0x02<<4), /* Mapped To PORTC */ + PORTCFG_VP13MAP_PORTD_gc = (0x03<<4), /* Mapped To PORTD */ + PORTCFG_VP13MAP_PORTE_gc = (0x04<<4), /* Mapped To PORTE */ + PORTCFG_VP13MAP_PORTF_gc = (0x05<<4), /* Mapped To PORTF */ + PORTCFG_VP13MAP_PORTG_gc = (0x06<<4), /* Mapped To PORTG */ + PORTCFG_VP13MAP_PORTH_gc = (0x07<<4), /* Mapped To PORTH */ + PORTCFG_VP13MAP_PORTJ_gc = (0x08<<4), /* Mapped To PORTJ */ + PORTCFG_VP13MAP_PORTK_gc = (0x09<<4), /* Mapped To PORTK */ + PORTCFG_VP13MAP_PORTL_gc = (0x0A<<4), /* Mapped To PORTL */ + PORTCFG_VP13MAP_PORTM_gc = (0x0B<<4), /* Mapped To PORTM */ + PORTCFG_VP13MAP_PORTN_gc = (0x0C<<4), /* Mapped To PORTN */ + PORTCFG_VP13MAP_PORTP_gc = (0x0D<<4), /* Mapped To PORTP */ + PORTCFG_VP13MAP_PORTQ_gc = (0x0E<<4), /* Mapped To PORTQ */ + PORTCFG_VP13MAP_PORTR_gc = (0x0F<<4), /* Mapped To PORTR */ +} PORTCFG_VP13MAP_t; + +/* System Clock Output Port */ +typedef enum PORTCFG_CLKOUT_enum +{ + PORTCFG_CLKOUT_OFF_gc = (0x00<<0), /* System Clock Output Disabled */ + PORTCFG_CLKOUT_PC7_gc = (0x01<<0), /* System Clock Output on Port C pin 7 */ + PORTCFG_CLKOUT_PD7_gc = (0x02<<0), /* System Clock Output on Port D pin 7 */ + PORTCFG_CLKOUT_PE7_gc = (0x03<<0), /* System Clock Output on Port E pin 7 */ +} PORTCFG_CLKOUT_t; + +/* Peripheral Clock Output Select */ +typedef enum PORTCFG_CLKOUTSEL_enum +{ + PORTCFG_CLKOUTSEL_CLK1X_gc = (0x00<<2), /* 1x Peripheral Clock Output to pin */ + PORTCFG_CLKOUTSEL_CLK2X_gc = (0x01<<2), /* 2x Peripheral Clock Output to pin */ + PORTCFG_CLKOUTSEL_CLK4X_gc = (0x02<<2), /* 4x Peripheral Clock Output to pin */ +} PORTCFG_CLKOUTSEL_t; + +/* Event Output Port */ +typedef enum PORTCFG_EVOUT_enum +{ + PORTCFG_EVOUT_OFF_gc = (0x00<<4), /* Event Output Disabled */ + PORTCFG_EVOUT_PC7_gc = (0x01<<4), /* Event Channel 7 Output on Port C pin 7 */ + PORTCFG_EVOUT_PD7_gc = (0x02<<4), /* Event Channel 7 Output on Port D pin 7 */ + PORTCFG_EVOUT_PE7_gc = (0x03<<4), /* Event Channel 7 Output on Port E pin 7 */ +} PORTCFG_EVOUT_t; + +/* Clock and Event Output Port */ +typedef enum PORTCFG_CLKEVPIN_enum +{ + PORTCFG_CLKEVPIN_PIN7_gc = (0x00<<7), /* Clock and Event Ouput on PIN 7 */ + PORTCFG_CLKEVPIN_PIN4_gc = (0x01<<7), /* Clock and Event Ouput on PIN 4 */ +} PORTCFG_CLKEVPIN_t; + +/* EBI Address Output Port */ +typedef enum PORTCFG_EBIADROUT_enum +{ + PORTCFG_EBIADROUT_PF_gc = (0x00<<2), /* EBI port 3 address output on PORTF pins 0 to 7 */ + PORTCFG_EBIADROUT_PE_gc = (0x01<<2), /* EBI port 3 address output on PORTE pins 0 to 7 */ + PORTCFG_EBIADROUT_PFH_gc = (0x02<<2), /* EBI port 3 address output on PORTF pins 4 to 7 */ + PORTCFG_EBIADROUT_PEH_gc = (0x03<<2), /* EBI port 3 address output on PORTE pins 4 to 7 */ +} PORTCFG_EBIADROUT_t; + +/* EBI Chip Select Output Port */ +typedef enum PORTCFG_EBICSOUT_enum +{ + PORTCFG_EBICSOUT_PH_gc = (0x00<<0), /* EBI chip select output to PORTH pin 4 to 7 */ + PORTCFG_EBICSOUT_PL_gc = (0x01<<0), /* EBI chip select output to PORTL pin 4 to 7 */ + PORTCFG_EBICSOUT_PF_gc = (0x02<<0), /* EBI chip select output to PORTF pin 4 to 7 */ + PORTCFG_EBICSOUT_PE_gc = (0x03<<0), /* EBI chip select output to PORTE pin 4 to 7 */ +} PORTCFG_EBICSOUT_t; + +/* Event Output Select */ +typedef enum PORTCFG_EVOUTSEL_enum +{ + PORTCFG_EVOUTSEL_0_gc = (0x00<<0), /* Event Channel 0 output to pin */ + PORTCFG_EVOUTSEL_1_gc = (0x01<<0), /* Event Channel 1 output to pin */ + PORTCFG_EVOUTSEL_2_gc = (0x02<<0), /* Event Channel 2 output to pin */ + PORTCFG_EVOUTSEL_3_gc = (0x03<<0), /* Event Channel 3 output to pin */ + PORTCFG_EVOUTSEL_4_gc = (0x04<<0), /* Event Channel 4 output to pin */ + PORTCFG_EVOUTSEL_5_gc = (0x05<<0), /* Event Channel 5 output to pin */ + PORTCFG_EVOUTSEL_6_gc = (0x06<<0), /* Event Channel 6 output to pin */ + PORTCFG_EVOUTSEL_7_gc = (0x07<<0), /* Event Channel 7 output to pin */ +} PORTCFG_EVOUTSEL_t; + + +/* +-------------------------------------------------------------------------- +AES - AES Module +-------------------------------------------------------------------------- +*/ + +/* AES Module */ +typedef struct AES_struct +{ + register8_t CTRL; /* AES Control Register */ + register8_t STATUS; /* AES Status Register */ + register8_t STATE; /* AES State Register */ + register8_t KEY; /* AES Key Register */ + register8_t INTCTRL; /* AES Interrupt Control Register */ +} AES_t; + +/* Interrupt level */ +typedef enum AES_INTLVL_enum +{ + AES_INTLVL_OFF_gc = (0x00<<0), /* Interrupt Disabled */ + AES_INTLVL_LO_gc = (0x01<<0), /* Low Level */ + AES_INTLVL_MED_gc = (0x02<<0), /* Medium Level */ + AES_INTLVL_HI_gc = (0x03<<0), /* High Level */ +} AES_INTLVL_t; + + +/* +-------------------------------------------------------------------------- +CRC - Cyclic Redundancy Checker +-------------------------------------------------------------------------- +*/ + +/* Cyclic Redundancy Checker */ +typedef struct CRC_struct +{ + register8_t CTRL; /* Control Register */ + register8_t STATUS; /* Status Register */ + register8_t reserved_0x02; + register8_t DATAIN; /* Data Input */ + register8_t CHECKSUM0; /* Checksum byte 0 */ + register8_t CHECKSUM1; /* Checksum byte 1 */ + register8_t CHECKSUM2; /* Checksum byte 2 */ + register8_t CHECKSUM3; /* Checksum byte 3 */ +} CRC_t; + +/* Reset */ +typedef enum CRC_RESET_enum +{ + CRC_RESET_NO_gc = (0x00<<6), /* No Reset */ + CRC_RESET_RESET0_gc = (0x02<<6), /* Reset CRC with CHECKSUM to all zeros */ + CRC_RESET_RESET1_gc = (0x03<<6), /* Reset CRC with CHECKSUM to all ones */ +} CRC_RESET_t; + +/* Input Source */ +typedef enum CRC_SOURCE_enum +{ + CRC_SOURCE_DISABLE_gc = (0x00<<0), /* Disabled */ + CRC_SOURCE_IO_gc = (0x01<<0), /* I/O Interface */ + CRC_SOURCE_FLASH_gc = (0x02<<0), /* Flash */ + CRC_SOURCE_DMAC0_gc = (0x04<<0), /* DMAC Channel 0 */ + CRC_SOURCE_DMAC1_gc = (0x05<<0), /* DMAC Channel 1 */ + CRC_SOURCE_DMAC2_gc = (0x06<<0), /* DMAC Channel 2 */ + CRC_SOURCE_DMAC3_gc = (0x07<<0), /* DMAC Channel 3 */ +} CRC_SOURCE_t; + + +/* +-------------------------------------------------------------------------- +DMA - DMA Controller +-------------------------------------------------------------------------- +*/ + +/* DMA Channel */ +typedef struct DMA_CH_struct +{ + register8_t CTRLA; /* Channel Control */ + register8_t CTRLB; /* Channel Control */ + register8_t ADDRCTRL; /* Address Control */ + register8_t TRIGSRC; /* Channel Trigger Source */ + _WORDREGISTER(TRFCNT); /* Channel Block Transfer Count */ + register8_t REPCNT; /* Channel Repeat Count */ + register8_t reserved_0x07; + register8_t SRCADDR0; /* Channel Source Address 0 */ + register8_t SRCADDR1; /* Channel Source Address 1 */ + register8_t SRCADDR2; /* Channel Source Address 2 */ + register8_t reserved_0x0B; + register8_t DESTADDR0; /* Channel Destination Address 0 */ + register8_t DESTADDR1; /* Channel Destination Address 1 */ + register8_t DESTADDR2; /* Channel Destination Address 2 */ + register8_t reserved_0x0F; +} DMA_CH_t; + + +/* DMA Controller */ +typedef struct DMA_struct +{ + register8_t CTRL; /* Control */ + register8_t reserved_0x01; + register8_t reserved_0x02; + register8_t INTFLAGS; /* Transfer Interrupt Status */ + register8_t STATUS; /* Status */ + register8_t reserved_0x05; + _WORDREGISTER(TEMP); /* Temporary Register For 16/24-bit Access */ + register8_t reserved_0x08; + register8_t reserved_0x09; + register8_t reserved_0x0A; + register8_t reserved_0x0B; + register8_t reserved_0x0C; + register8_t reserved_0x0D; + register8_t reserved_0x0E; + register8_t reserved_0x0F; + DMA_CH_t CH0; /* DMA Channel 0 */ + DMA_CH_t CH1; /* DMA Channel 1 */ + DMA_CH_t CH2; /* DMA Channel 2 */ + DMA_CH_t CH3; /* DMA Channel 3 */ +} DMA_t; + +/* Burst mode */ +typedef enum DMA_CH_BURSTLEN_enum +{ + DMA_CH_BURSTLEN_1BYTE_gc = (0x00<<0), /* 1-byte burst mode */ + DMA_CH_BURSTLEN_2BYTE_gc = (0x01<<0), /* 2-byte burst mode */ + DMA_CH_BURSTLEN_4BYTE_gc = (0x02<<0), /* 4-byte burst mode */ + DMA_CH_BURSTLEN_8BYTE_gc = (0x03<<0), /* 8-byte burst mode */ +} DMA_CH_BURSTLEN_t; + +/* Source address reload mode */ +typedef enum DMA_CH_SRCRELOAD_enum +{ + DMA_CH_SRCRELOAD_NONE_gc = (0x00<<6), /* No reload */ + DMA_CH_SRCRELOAD_BLOCK_gc = (0x01<<6), /* Reload at end of block */ + DMA_CH_SRCRELOAD_BURST_gc = (0x02<<6), /* Reload at end of burst */ + DMA_CH_SRCRELOAD_TRANSACTION_gc = (0x03<<6), /* Reload at end of transaction */ +} DMA_CH_SRCRELOAD_t; + +/* Source addressing mode */ +typedef enum DMA_CH_SRCDIR_enum +{ + DMA_CH_SRCDIR_FIXED_gc = (0x00<<4), /* Fixed */ + DMA_CH_SRCDIR_INC_gc = (0x01<<4), /* Increment */ + DMA_CH_SRCDIR_DEC_gc = (0x02<<4), /* Decrement */ +} DMA_CH_SRCDIR_t; + +/* Destination adress reload mode */ +typedef enum DMA_CH_DESTRELOAD_enum +{ + DMA_CH_DESTRELOAD_NONE_gc = (0x00<<2), /* No reload */ + DMA_CH_DESTRELOAD_BLOCK_gc = (0x01<<2), /* Reload at end of block */ + DMA_CH_DESTRELOAD_BURST_gc = (0x02<<2), /* Reload at end of burst */ + DMA_CH_DESTRELOAD_TRANSACTION_gc = (0x03<<2), /* Reload at end of transaction */ +} DMA_CH_DESTRELOAD_t; + +/* Destination adressing mode */ +typedef enum DMA_CH_DESTDIR_enum +{ + DMA_CH_DESTDIR_FIXED_gc = (0x00<<0), /* Fixed */ + DMA_CH_DESTDIR_INC_gc = (0x01<<0), /* Increment */ + DMA_CH_DESTDIR_DEC_gc = (0x02<<0), /* Decrement */ +} DMA_CH_DESTDIR_t; + +/* Transfer trigger source */ +typedef enum DMA_CH_TRIGSRC_enum +{ + DMA_CH_TRIGSRC_OFF_gc = (0x00<<0), /* Off software triggers only */ + DMA_CH_TRIGSRC_EVSYS_CH0_gc = (0x01<<0), /* Event System Channel 0 */ + DMA_CH_TRIGSRC_EVSYS_CH1_gc = (0x02<<0), /* Event System Channel 1 */ + DMA_CH_TRIGSRC_EVSYS_CH2_gc = (0x03<<0), /* Event System Channel 2 */ + DMA_CH_TRIGSRC_ADCA_CH0_gc = (0x10<<0), /* ADCA Channel 0 */ + DMA_CH_TRIGSRC_ADCA_CH1_gc = (0x11<<0), /* ADCA Channel 1 */ + DMA_CH_TRIGSRC_ADCA_CH2_gc = (0x12<<0), /* ADCA Channel 2 */ + DMA_CH_TRIGSRC_ADCA_CH3_gc = (0x13<<0), /* ADCA Channel 3 */ + DMA_CH_TRIGSRC_ADCA_CH4_gc = (0x14<<0), /* ADCA Channel 0,1,2,3 combined */ + DMA_CH_TRIGSRC_DACA_CH0_gc = (0x15<<0), /* DACA Channel 0 */ + DMA_CH_TRIGSRC_DACA_CH1_gc = (0x16<<0), /* DACA Channel 1 */ + DMA_CH_TRIGSRC_ADCB_CH0_gc = (0x20<<0), /* ADCB Channel 0 */ + DMA_CH_TRIGSRC_ADCB_CH1_gc = (0x21<<0), /* ADCB Channel 1 */ + DMA_CH_TRIGSRC_ADCB_CH2_gc = (0x22<<0), /* ADCB Channel 2 */ + DMA_CH_TRIGSRC_ADCB_CH3_gc = (0x23<<0), /* ADCB Channel 3 */ + DMA_CH_TRIGSRC_ADCB_CH4_gc = (0x24<<0), /* ADCB Channel 0,1,2,3 combined */ + DMA_CH_TRIGSRC_DACB_CH0_gc = (0x25<<0), /* DACB Channel 0 */ + DMA_CH_TRIGSRC_DACB_CH1_gc = (0x26<<0), /* DACB Channel 1 */ + DMA_CH_TRIGSRC_TCC0_OVF_gc = (0x40<<0), /* Timer/Counter C0 Overflow */ + DMA_CH_TRIGSRC_TCC0_ERR_gc = (0x41<<0), /* Timer/Counter C0 Error */ + DMA_CH_TRIGSRC_TCC0_CCA_gc = (0x42<<0), /* Timer/Counter C0 Compare or Capture A */ + DMA_CH_TRIGSRC_TCC0_CCB_gc = (0x43<<0), /* Timer/Counter C0 Compare or Capture B */ + DMA_CH_TRIGSRC_TCC0_CCC_gc = (0x44<<0), /* Timer/Counter C0 Compare or Capture C */ + DMA_CH_TRIGSRC_TCC0_CCD_gc = (0x45<<0), /* Timer/Counter C0 Compare or Capture D */ + DMA_CH_TRIGSRC_TCC1_OVF_gc = (0x46<<0), /* Timer/Counter C1 Overflow */ + DMA_CH_TRIGSRC_TCC1_ERR_gc = (0x47<<0), /* Timer/Counter C1 Error */ + DMA_CH_TRIGSRC_TCC1_CCA_gc = (0x48<<0), /* Timer/Counter C1 Compare or Capture A */ + DMA_CH_TRIGSRC_TCC1_CCB_gc = (0x49<<0), /* Timer/Counter C1 Compare or Capture B */ + DMA_CH_TRIGSRC_SPIC_gc = (0x4A<<0), /* SPI C Transfer Complete */ + DMA_CH_TRIGSRC_USARTC0_RXC_gc = (0x4B<<0), /* USART C0 Receive Complete */ + DMA_CH_TRIGSRC_USARTC0_DRE_gc = (0x4C<<0), /* USART C0 Data Register Empty */ + DMA_CH_TRIGSRC_USARTC1_RXC_gc = (0x4E<<0), /* USART C1 Receive Complete */ + DMA_CH_TRIGSRC_USARTC1_DRE_gc = (0x4F<<0), /* USART C1 Data Register Empty */ + DMA_CH_TRIGSRC_TCD0_OVF_gc = (0x60<<0), /* Timer/Counter D0 Overflow */ + DMA_CH_TRIGSRC_TCD0_ERR_gc = (0x61<<0), /* Timer/Counter D0 Error */ + DMA_CH_TRIGSRC_TCD0_CCA_gc = (0x62<<0), /* Timer/Counter D0 Compare or Capture A */ + DMA_CH_TRIGSRC_TCD0_CCB_gc = (0x63<<0), /* Timer/Counter D0 Compare or Capture B */ + DMA_CH_TRIGSRC_TCD0_CCC_gc = (0x64<<0), /* Timer/Counter D0 Compare or Capture C */ + DMA_CH_TRIGSRC_TCD0_CCD_gc = (0x65<<0), /* Timer/Counter D0 Compare or Capture D */ + DMA_CH_TRIGSRC_TCD1_OVF_gc = (0x66<<0), /* Timer/Counter D1 Overflow */ + DMA_CH_TRIGSRC_TCD1_ERR_gc = (0x67<<0), /* Timer/Counter D1 Error */ + DMA_CH_TRIGSRC_TCD1_CCA_gc = (0x68<<0), /* Timer/Counter D1 Compare or Capture A */ + DMA_CH_TRIGSRC_TCD1_CCB_gc = (0x69<<0), /* Timer/Counter D1 Compare or Capture B */ + DMA_CH_TRIGSRC_SPID_gc = (0x6A<<0), /* SPI D Transfer Complete */ + DMA_CH_TRIGSRC_USARTD0_RXC_gc = (0x6B<<0), /* USART D0 Receive Complete */ + DMA_CH_TRIGSRC_USARTD0_DRE_gc = (0x6C<<0), /* USART D0 Data Register Empty */ + DMA_CH_TRIGSRC_USARTD1_RXC_gc = (0x6E<<0), /* USART D1 Receive Complete */ + DMA_CH_TRIGSRC_USARTD1_DRE_gc = (0x6F<<0), /* USART D1 Data Register Empty */ + DMA_CH_TRIGSRC_TCE0_OVF_gc = (0x80<<0), /* Timer/Counter E0 Overflow */ + DMA_CH_TRIGSRC_TCE0_ERR_gc = (0x81<<0), /* Timer/Counter E0 Error */ + DMA_CH_TRIGSRC_TCE0_CCA_gc = (0x82<<0), /* Timer/Counter E0 Compare or Capture A */ + DMA_CH_TRIGSRC_TCE0_CCB_gc = (0x83<<0), /* Timer/Counter E0 Compare or Capture B */ + DMA_CH_TRIGSRC_TCE0_CCC_gc = (0x84<<0), /* Timer/Counter E0 Compare or Capture C */ + DMA_CH_TRIGSRC_TCE0_CCD_gc = (0x85<<0), /* Timer/Counter E0 Compare or Capture D */ + DMA_CH_TRIGSRC_TCE1_OVF_gc = (0x86<<0), /* Timer/Counter E1 Overflow */ + DMA_CH_TRIGSRC_TCE1_ERR_gc = (0x87<<0), /* Timer/Counter E1 Error */ + DMA_CH_TRIGSRC_TCE1_CCA_gc = (0x88<<0), /* Timer/Counter E1 Compare or Capture A */ + DMA_CH_TRIGSRC_TCE1_CCB_gc = (0x89<<0), /* Timer/Counter E1 Compare or Capture B */ + DMA_CH_TRIGSRC_SPIE_gc = (0x8A<<0), /* SPI E Transfer Complete */ + DMA_CH_TRIGSRC_USARTE0_RXC_gc = (0x8B<<0), /* USART E0 Receive Complete */ + DMA_CH_TRIGSRC_USARTE0_DRE_gc = (0x8C<<0), /* USART E0 Data Register Empty */ + DMA_CH_TRIGSRC_USARTE1_RXC_gc = (0x8E<<0), /* USART E1 Receive Complete */ + DMA_CH_TRIGSRC_USARTE1_DRE_gc = (0x8F<<0), /* USART E1 Data Register Empty */ + DMA_CH_TRIGSRC_TCF0_OVF_gc = (0xA0<<0), /* Timer/Counter F0 Overflow */ + DMA_CH_TRIGSRC_TCF0_ERR_gc = (0xA1<<0), /* Timer/Counter F0 Error */ + DMA_CH_TRIGSRC_TCF0_CCA_gc = (0xA2<<0), /* Timer/Counter F0 Compare or Capture A */ + DMA_CH_TRIGSRC_TCF0_CCB_gc = (0xA3<<0), /* Timer/Counter F0 Compare or Capture B */ + DMA_CH_TRIGSRC_TCF0_CCC_gc = (0xA4<<0), /* Timer/Counter F0 Compare or Capture C */ + DMA_CH_TRIGSRC_TCF0_CCD_gc = (0xA5<<0), /* Timer/Counter F0 Compare or Capture D */ + DMA_CH_TRIGSRC_TCF1_OVF_gc = (0xA6<<0), /* Timer/Counter F1 Overflow */ + DMA_CH_TRIGSRC_TCF1_ERR_gc = (0xA7<<0), /* Timer/Counter F1 Error */ + DMA_CH_TRIGSRC_TCF1_CCA_gc = (0xA8<<0), /* Timer/Counter F1 Compare or Capture A */ + DMA_CH_TRIGSRC_TCF1_CCB_gc = (0xA9<<0), /* Timer/Counter F1 Compare or Capture B */ + DMA_CH_TRIGSRC_SPIF_gc = (0xAA<<0), /* SPI F Transfer Complete */ + DMA_CH_TRIGSRC_USARTF0_RXC_gc = (0xAB<<0), /* USART F0 Receive Complete */ + DMA_CH_TRIGSRC_USARTF0_DRE_gc = (0xAC<<0), /* USART F0 Data Register Empty */ + DMA_CH_TRIGSRC_USARTF1_RXC_gc = (0xAE<<0), /* USART F1 Receive Complete */ + DMA_CH_TRIGSRC_USARTF1_DRE_gc = (0xAF<<0), /* USART F1 Data Register Empty */ +} DMA_CH_TRIGSRC_t; + +/* Double buffering mode */ +typedef enum DMA_DBUFMODE_enum +{ + DMA_DBUFMODE_DISABLED_gc = (0x00<<2), /* Double buffering disabled */ + DMA_DBUFMODE_CH01_gc = (0x01<<2), /* Double buffering enabled on channel 0/1 */ + DMA_DBUFMODE_CH23_gc = (0x02<<2), /* Double buffering enabled on channel 2/3 */ + DMA_DBUFMODE_CH01CH23_gc = (0x03<<2), /* Double buffering enabled on ch. 0/1 and ch. 2/3 */ +} DMA_DBUFMODE_t; + +/* Priority mode */ +typedef enum DMA_PRIMODE_enum +{ + DMA_PRIMODE_RR0123_gc = (0x00<<0), /* Round Robin */ + DMA_PRIMODE_CH0RR123_gc = (0x01<<0), /* Channel 0 > Round Robin on channel 1/2/3 */ + DMA_PRIMODE_CH01RR23_gc = (0x02<<0), /* Channel 0 > channel 1 > Round Robin on channel 2/3 */ + DMA_PRIMODE_CH0123_gc = (0x03<<0), /* Channel 0 > channel 1 > channel 2 > channel 3 */ +} DMA_PRIMODE_t; + +/* Interrupt level */ +typedef enum DMA_CH_ERRINTLVL_enum +{ + DMA_CH_ERRINTLVL_OFF_gc = (0x00<<2), /* Interrupt disabled */ + DMA_CH_ERRINTLVL_LO_gc = (0x01<<2), /* Low level */ + DMA_CH_ERRINTLVL_MED_gc = (0x02<<2), /* Medium level */ + DMA_CH_ERRINTLVL_HI_gc = (0x03<<2), /* High level */ +} DMA_CH_ERRINTLVL_t; + +/* Interrupt level */ +typedef enum DMA_CH_TRNINTLVL_enum +{ + DMA_CH_TRNINTLVL_OFF_gc = (0x00<<0), /* Interrupt disabled */ + DMA_CH_TRNINTLVL_LO_gc = (0x01<<0), /* Low level */ + DMA_CH_TRNINTLVL_MED_gc = (0x02<<0), /* Medium level */ + DMA_CH_TRNINTLVL_HI_gc = (0x03<<0), /* High level */ +} DMA_CH_TRNINTLVL_t; + + +/* +-------------------------------------------------------------------------- +EVSYS - Event System +-------------------------------------------------------------------------- +*/ + +/* Event System */ +typedef struct EVSYS_struct +{ + register8_t CH0MUX; /* Event Channel 0 Multiplexer */ + register8_t CH1MUX; /* Event Channel 1 Multiplexer */ + register8_t CH2MUX; /* Event Channel 2 Multiplexer */ + register8_t CH3MUX; /* Event Channel 3 Multiplexer */ + register8_t CH4MUX; /* Event Channel 4 Multiplexer */ + register8_t CH5MUX; /* Event Channel 5 Multiplexer */ + register8_t CH6MUX; /* Event Channel 6 Multiplexer */ + register8_t CH7MUX; /* Event Channel 7 Multiplexer */ + register8_t CH0CTRL; /* Channel 0 Control Register */ + register8_t CH1CTRL; /* Channel 1 Control Register */ + register8_t CH2CTRL; /* Channel 2 Control Register */ + register8_t CH3CTRL; /* Channel 3 Control Register */ + register8_t CH4CTRL; /* Channel 4 Control Register */ + register8_t CH5CTRL; /* Channel 5 Control Register */ + register8_t CH6CTRL; /* Channel 6 Control Register */ + register8_t CH7CTRL; /* Channel 7 Control Register */ + register8_t STROBE; /* Event Strobe */ + register8_t DATA; /* Event Data */ +} EVSYS_t; + +/* Quadrature Decoder Index Recognition Mode */ +typedef enum EVSYS_QDIRM_enum +{ + EVSYS_QDIRM_00_gc = (0x00<<5), /* QDPH0 = 0, QDPH90 = 0 */ + EVSYS_QDIRM_01_gc = (0x01<<5), /* QDPH0 = 0, QDPH90 = 1 */ + EVSYS_QDIRM_10_gc = (0x02<<5), /* QDPH0 = 1, QDPH90 = 0 */ + EVSYS_QDIRM_11_gc = (0x03<<5), /* QDPH0 = 1, QDPH90 = 1 */ +} EVSYS_QDIRM_t; + +/* Digital filter coefficient */ +typedef enum EVSYS_DIGFILT_enum +{ + EVSYS_DIGFILT_1SAMPLE_gc = (0x00<<0), /* 1 SAMPLE */ + EVSYS_DIGFILT_2SAMPLES_gc = (0x01<<0), /* 2 SAMPLES */ + EVSYS_DIGFILT_3SAMPLES_gc = (0x02<<0), /* 3 SAMPLES */ + EVSYS_DIGFILT_4SAMPLES_gc = (0x03<<0), /* 4 SAMPLES */ + EVSYS_DIGFILT_5SAMPLES_gc = (0x04<<0), /* 5 SAMPLES */ + EVSYS_DIGFILT_6SAMPLES_gc = (0x05<<0), /* 6 SAMPLES */ + EVSYS_DIGFILT_7SAMPLES_gc = (0x06<<0), /* 7 SAMPLES */ + EVSYS_DIGFILT_8SAMPLES_gc = (0x07<<0), /* 8 SAMPLES */ +} EVSYS_DIGFILT_t; + +/* Event Channel multiplexer input selection */ +typedef enum EVSYS_CHMUX_enum +{ + EVSYS_CHMUX_OFF_gc = (0x00<<0), /* Off */ + EVSYS_CHMUX_RTC_OVF_gc = (0x08<<0), /* RTC Overflow */ + EVSYS_CHMUX_RTC_CMP_gc = (0x09<<0), /* RTC Compare Match */ + EVSYS_CHMUX_USB_gc = (0x0A<<0), /* USB Setup, SOF, CRC error and UNF/OVF */ + EVSYS_CHMUX_ACA_CH0_gc = (0x10<<0), /* Analog Comparator A Channel 0 */ + EVSYS_CHMUX_ACA_CH1_gc = (0x11<<0), /* Analog Comparator A Channel 1 */ + EVSYS_CHMUX_ACA_WIN_gc = (0x12<<0), /* Analog Comparator A Window */ + EVSYS_CHMUX_ACB_CH0_gc = (0x13<<0), /* Analog Comparator B Channel 0 */ + EVSYS_CHMUX_ACB_CH1_gc = (0x14<<0), /* Analog Comparator B Channel 1 */ + EVSYS_CHMUX_ACB_WIN_gc = (0x15<<0), /* Analog Comparator B Window */ + EVSYS_CHMUX_ADCA_CH0_gc = (0x20<<0), /* ADC A Channel 0 */ + EVSYS_CHMUX_ADCA_CH1_gc = (0x21<<0), /* ADC A Channel 1 */ + EVSYS_CHMUX_ADCA_CH2_gc = (0x22<<0), /* ADC A Channel 2 */ + EVSYS_CHMUX_ADCA_CH3_gc = (0x23<<0), /* ADC A Channel 3 */ + EVSYS_CHMUX_ADCB_CH0_gc = (0x24<<0), /* ADC B Channel 0 */ + EVSYS_CHMUX_ADCB_CH1_gc = (0x25<<0), /* ADC B Channel 1 */ + EVSYS_CHMUX_ADCB_CH2_gc = (0x26<<0), /* ADC B Channel 2 */ + EVSYS_CHMUX_ADCB_CH3_gc = (0x27<<0), /* ADC B Channel 3 */ + EVSYS_CHMUX_PORTA_PIN0_gc = (0x50<<0), /* Port A, Pin0 */ + EVSYS_CHMUX_PORTA_PIN1_gc = (0x51<<0), /* Port A, Pin1 */ + EVSYS_CHMUX_PORTA_PIN2_gc = (0x52<<0), /* Port A, Pin2 */ + EVSYS_CHMUX_PORTA_PIN3_gc = (0x53<<0), /* Port A, Pin3 */ + EVSYS_CHMUX_PORTA_PIN4_gc = (0x54<<0), /* Port A, Pin4 */ + EVSYS_CHMUX_PORTA_PIN5_gc = (0x55<<0), /* Port A, Pin5 */ + EVSYS_CHMUX_PORTA_PIN6_gc = (0x56<<0), /* Port A, Pin6 */ + EVSYS_CHMUX_PORTA_PIN7_gc = (0x57<<0), /* Port A, Pin7 */ + EVSYS_CHMUX_PORTB_PIN0_gc = (0x58<<0), /* Port B, Pin0 */ + EVSYS_CHMUX_PORTB_PIN1_gc = (0x59<<0), /* Port B, Pin1 */ + EVSYS_CHMUX_PORTB_PIN2_gc = (0x5A<<0), /* Port B, Pin2 */ + EVSYS_CHMUX_PORTB_PIN3_gc = (0x5B<<0), /* Port B, Pin3 */ + EVSYS_CHMUX_PORTB_PIN4_gc = (0x5C<<0), /* Port B, Pin4 */ + EVSYS_CHMUX_PORTB_PIN5_gc = (0x5D<<0), /* Port B, Pin5 */ + EVSYS_CHMUX_PORTB_PIN6_gc = (0x5E<<0), /* Port B, Pin6 */ + EVSYS_CHMUX_PORTB_PIN7_gc = (0x5F<<0), /* Port B, Pin7 */ + EVSYS_CHMUX_PORTC_PIN0_gc = (0x60<<0), /* Port C, Pin0 */ + EVSYS_CHMUX_PORTC_PIN1_gc = (0x61<<0), /* Port C, Pin1 */ + EVSYS_CHMUX_PORTC_PIN2_gc = (0x62<<0), /* Port C, Pin2 */ + EVSYS_CHMUX_PORTC_PIN3_gc = (0x63<<0), /* Port C, Pin3 */ + EVSYS_CHMUX_PORTC_PIN4_gc = (0x64<<0), /* Port C, Pin4 */ + EVSYS_CHMUX_PORTC_PIN5_gc = (0x65<<0), /* Port C, Pin5 */ + EVSYS_CHMUX_PORTC_PIN6_gc = (0x66<<0), /* Port C, Pin6 */ + EVSYS_CHMUX_PORTC_PIN7_gc = (0x67<<0), /* Port C, Pin7 */ + EVSYS_CHMUX_PORTD_PIN0_gc = (0x68<<0), /* Port D, Pin0 */ + EVSYS_CHMUX_PORTD_PIN1_gc = (0x69<<0), /* Port D, Pin1 */ + EVSYS_CHMUX_PORTD_PIN2_gc = (0x6A<<0), /* Port D, Pin2 */ + EVSYS_CHMUX_PORTD_PIN3_gc = (0x6B<<0), /* Port D, Pin3 */ + EVSYS_CHMUX_PORTD_PIN4_gc = (0x6C<<0), /* Port D, Pin4 */ + EVSYS_CHMUX_PORTD_PIN5_gc = (0x6D<<0), /* Port D, Pin5 */ + EVSYS_CHMUX_PORTD_PIN6_gc = (0x6E<<0), /* Port D, Pin6 */ + EVSYS_CHMUX_PORTD_PIN7_gc = (0x6F<<0), /* Port D, Pin7 */ + EVSYS_CHMUX_PORTE_PIN0_gc = (0x70<<0), /* Port E, Pin0 */ + EVSYS_CHMUX_PORTE_PIN1_gc = (0x71<<0), /* Port E, Pin1 */ + EVSYS_CHMUX_PORTE_PIN2_gc = (0x72<<0), /* Port E, Pin2 */ + EVSYS_CHMUX_PORTE_PIN3_gc = (0x73<<0), /* Port E, Pin3 */ + EVSYS_CHMUX_PORTE_PIN4_gc = (0x74<<0), /* Port E, Pin4 */ + EVSYS_CHMUX_PORTE_PIN5_gc = (0x75<<0), /* Port E, Pin5 */ + EVSYS_CHMUX_PORTE_PIN6_gc = (0x76<<0), /* Port E, Pin6 */ + EVSYS_CHMUX_PORTE_PIN7_gc = (0x77<<0), /* Port E, Pin7 */ + EVSYS_CHMUX_PORTF_PIN0_gc = (0x78<<0), /* Port F, Pin0 */ + EVSYS_CHMUX_PORTF_PIN1_gc = (0x79<<0), /* Port F, Pin1 */ + EVSYS_CHMUX_PORTF_PIN2_gc = (0x7A<<0), /* Port F, Pin2 */ + EVSYS_CHMUX_PORTF_PIN3_gc = (0x7B<<0), /* Port F, Pin3 */ + EVSYS_CHMUX_PORTF_PIN4_gc = (0x7C<<0), /* Port F, Pin4 */ + EVSYS_CHMUX_PORTF_PIN5_gc = (0x7D<<0), /* Port F, Pin5 */ + EVSYS_CHMUX_PORTF_PIN6_gc = (0x7E<<0), /* Port F, Pin6 */ + EVSYS_CHMUX_PORTF_PIN7_gc = (0x7F<<0), /* Port F, Pin7 */ + EVSYS_CHMUX_PRESCALER_1_gc = (0x80<<0), /* Prescaler, divide by 1 */ + EVSYS_CHMUX_PRESCALER_2_gc = (0x81<<0), /* Prescaler, divide by 2 */ + EVSYS_CHMUX_PRESCALER_4_gc = (0x82<<0), /* Prescaler, divide by 4 */ + EVSYS_CHMUX_PRESCALER_8_gc = (0x83<<0), /* Prescaler, divide by 8 */ + EVSYS_CHMUX_PRESCALER_16_gc = (0x84<<0), /* Prescaler, divide by 16 */ + EVSYS_CHMUX_PRESCALER_32_gc = (0x85<<0), /* Prescaler, divide by 32 */ + EVSYS_CHMUX_PRESCALER_64_gc = (0x86<<0), /* Prescaler, divide by 64 */ + EVSYS_CHMUX_PRESCALER_128_gc = (0x87<<0), /* Prescaler, divide by 128 */ + EVSYS_CHMUX_PRESCALER_256_gc = (0x88<<0), /* Prescaler, divide by 256 */ + EVSYS_CHMUX_PRESCALER_512_gc = (0x89<<0), /* Prescaler, divide by 512 */ + EVSYS_CHMUX_PRESCALER_1024_gc = (0x8A<<0), /* Prescaler, divide by 1024 */ + EVSYS_CHMUX_PRESCALER_2048_gc = (0x8B<<0), /* Prescaler, divide by 2048 */ + EVSYS_CHMUX_PRESCALER_4096_gc = (0x8C<<0), /* Prescaler, divide by 4096 */ + EVSYS_CHMUX_PRESCALER_8192_gc = (0x8D<<0), /* Prescaler, divide by 8192 */ + EVSYS_CHMUX_PRESCALER_16384_gc = (0x8E<<0), /* Prescaler, divide by 16384 */ + EVSYS_CHMUX_PRESCALER_32768_gc = (0x8F<<0), /* Prescaler, divide by 32768 */ + EVSYS_CHMUX_TCC0_OVF_gc = (0xC0<<0), /* Timer/Counter C0 Overflow */ + EVSYS_CHMUX_TCC0_ERR_gc = (0xC1<<0), /* Timer/Counter C0 Error */ + EVSYS_CHMUX_TCC0_CCA_gc = (0xC4<<0), /* Timer/Counter C0 Compare or Capture A */ + EVSYS_CHMUX_TCC0_CCB_gc = (0xC5<<0), /* Timer/Counter C0 Compare or Capture B */ + EVSYS_CHMUX_TCC0_CCC_gc = (0xC6<<0), /* Timer/Counter C0 Compare or Capture C */ + EVSYS_CHMUX_TCC0_CCD_gc = (0xC7<<0), /* Timer/Counter C0 Compare or Capture D */ + EVSYS_CHMUX_TCC1_OVF_gc = (0xC8<<0), /* Timer/Counter C1 Overflow */ + EVSYS_CHMUX_TCC1_ERR_gc = (0xC9<<0), /* Timer/Counter C1 Error */ + EVSYS_CHMUX_TCC1_CCA_gc = (0xCC<<0), /* Timer/Counter C1 Compare or Capture A */ + EVSYS_CHMUX_TCC1_CCB_gc = (0xCD<<0), /* Timer/Counter C1 Compare or Capture B */ + EVSYS_CHMUX_TCD0_OVF_gc = (0xD0<<0), /* Timer/Counter D0 Overflow */ + EVSYS_CHMUX_TCD0_ERR_gc = (0xD1<<0), /* Timer/Counter D0 Error */ + EVSYS_CHMUX_TCD0_CCA_gc = (0xD4<<0), /* Timer/Counter D0 Compare or Capture A */ + EVSYS_CHMUX_TCD0_CCB_gc = (0xD5<<0), /* Timer/Counter D0 Compare or Capture B */ + EVSYS_CHMUX_TCD0_CCC_gc = (0xD6<<0), /* Timer/Counter D0 Compare or Capture C */ + EVSYS_CHMUX_TCD0_CCD_gc = (0xD7<<0), /* Timer/Counter D0 Compare or Capture D */ + EVSYS_CHMUX_TCD1_OVF_gc = (0xD8<<0), /* Timer/Counter D1 Overflow */ + EVSYS_CHMUX_TCD1_ERR_gc = (0xD9<<0), /* Timer/Counter D1 Error */ + EVSYS_CHMUX_TCD1_CCA_gc = (0xDC<<0), /* Timer/Counter D1 Compare or Capture A */ + EVSYS_CHMUX_TCD1_CCB_gc = (0xDD<<0), /* Timer/Counter D1 Compare or Capture B */ + EVSYS_CHMUX_TCE0_OVF_gc = (0xE0<<0), /* Timer/Counter E0 Overflow */ + EVSYS_CHMUX_TCE0_ERR_gc = (0xE1<<0), /* Timer/Counter E0 Error */ + EVSYS_CHMUX_TCE0_CCA_gc = (0xE4<<0), /* Timer/Counter E0 Compare or Capture A */ + EVSYS_CHMUX_TCE0_CCB_gc = (0xE5<<0), /* Timer/Counter E0 Compare or Capture B */ + EVSYS_CHMUX_TCE0_CCC_gc = (0xE6<<0), /* Timer/Counter E0 Compare or Capture C */ + EVSYS_CHMUX_TCE0_CCD_gc = (0xE7<<0), /* Timer/Counter E0 Compare or Capture D */ + EVSYS_CHMUX_TCE1_OVF_gc = (0xE8<<0), /* Timer/Counter E1 Overflow */ + EVSYS_CHMUX_TCE1_ERR_gc = (0xE9<<0), /* Timer/Counter E1 Error */ + EVSYS_CHMUX_TCE1_CCA_gc = (0xEC<<0), /* Timer/Counter E1 Compare or Capture A */ + EVSYS_CHMUX_TCE1_CCB_gc = (0xED<<0), /* Timer/Counter E1 Compare or Capture B */ + EVSYS_CHMUX_TCF0_OVF_gc = (0xF0<<0), /* Timer/Counter F0 Overflow */ + EVSYS_CHMUX_TCF0_ERR_gc = (0xF1<<0), /* Timer/Counter F0 Error */ + EVSYS_CHMUX_TCF0_CCA_gc = (0xF4<<0), /* Timer/Counter F0 Compare or Capture A */ + EVSYS_CHMUX_TCF0_CCB_gc = (0xF5<<0), /* Timer/Counter F0 Compare or Capture B */ + EVSYS_CHMUX_TCF0_CCC_gc = (0xF6<<0), /* Timer/Counter F0 Compare or Capture C */ + EVSYS_CHMUX_TCF0_CCD_gc = (0xF7<<0), /* Timer/Counter F0 Compare or Capture D */ + EVSYS_CHMUX_TCF1_OVF_gc = (0xF8<<0), /* Timer/Counter F1 Overflow */ + EVSYS_CHMUX_TCF1_ERR_gc = (0xF9<<0), /* Timer/Counter F1 Error */ + EVSYS_CHMUX_TCF1_CCA_gc = (0xFC<<0), /* Timer/Counter F1 Compare or Capture A */ + EVSYS_CHMUX_TCF1_CCB_gc = (0xFD<<0), /* Timer/Counter F1 Compare or Capture B */ +} EVSYS_CHMUX_t; + + +/* +-------------------------------------------------------------------------- +NVM - Non Volatile Memory Controller +-------------------------------------------------------------------------- +*/ + +/* Non-volatile Memory Controller */ +typedef struct NVM_struct +{ + register8_t ADDR0; /* Address Register 0 */ + register8_t ADDR1; /* Address Register 1 */ + register8_t ADDR2; /* Address Register 2 */ + register8_t reserved_0x03; + register8_t DATA0; /* Data Register 0 */ + register8_t DATA1; /* Data Register 1 */ + register8_t DATA2; /* Data Register 2 */ + register8_t reserved_0x07; + register8_t reserved_0x08; + register8_t reserved_0x09; + register8_t CMD; /* Command */ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t INTCTRL; /* Interrupt Control */ + register8_t reserved_0x0E; + register8_t STATUS; /* Status */ + register8_t LOCK_BITS; /* Lock Bits (Changed from LOCKBITS to avoid avr-libc collision) */ +} NVM_t; + +/* NVM Command */ +typedef enum NVM_CMD_enum +{ + NVM_CMD_NO_OPERATION_gc = (0x00<<0), /* Noop/Ordinary LPM */ + NVM_CMD_READ_USER_SIG_ROW_gc = (0x01<<0), /* Read user signature row */ + NVM_CMD_READ_CALIB_ROW_gc = (0x02<<0), /* Read calibration row */ + NVM_CMD_READ_EEPROM_gc = (0x06<<0), /* Read EEPROM */ + NVM_CMD_READ_FUSES_gc = (0x07<<0), /* Read fuse byte */ + NVM_CMD_WRITE_LOCK_BITS_gc = (0x08<<0), /* Write lock bits */ + NVM_CMD_ERASE_USER_SIG_ROW_gc = (0x18<<0), /* Erase user signature row */ + NVM_CMD_WRITE_USER_SIG_ROW_gc = (0x1A<<0), /* Write user signature row */ + NVM_CMD_ERASE_APP_gc = (0x20<<0), /* Erase Application Section */ + NVM_CMD_ERASE_APP_PAGE_gc = (0x22<<0), /* Erase Application Section page */ + NVM_CMD_LOAD_FLASH_BUFFER_gc = (0x23<<0), /* Load Flash page buffer */ + NVM_CMD_WRITE_APP_PAGE_gc = (0x24<<0), /* Write Application Section page */ + NVM_CMD_ERASE_WRITE_APP_PAGE_gc = (0x25<<0), /* Erase-and-write Application Section page */ + NVM_CMD_ERASE_FLASH_BUFFER_gc = (0x26<<0), /* Erase/flush Flash page buffer */ + NVM_CMD_ERASE_BOOT_PAGE_gc = (0x2A<<0), /* Erase Boot Section page */ + NVM_CMD_ERASE_FLASH_PAGE_gc = (0x2B<<0), /* Erase Flash Page */ + NVM_CMD_WRITE_BOOT_PAGE_gc = (0x2C<<0), /* Write Boot Section page */ + NVM_CMD_ERASE_WRITE_BOOT_PAGE_gc = (0x2D<<0), /* Erase-and-write Boot Section page */ + NVM_CMD_WRITE_FLASH_PAGE_gc = (0x2E<<0), /* Write Flash Page */ + NVM_CMD_ERASE_WRITE_FLASH_PAGE_gc = (0x2F<<0), /* Erase-and-write Flash Page */ + NVM_CMD_ERASE_EEPROM_gc = (0x30<<0), /* Erase EEPROM */ + NVM_CMD_ERASE_EEPROM_PAGE_gc = (0x32<<0), /* Erase EEPROM page */ + NVM_CMD_LOAD_EEPROM_BUFFER_gc = (0x33<<0), /* Load EEPROM page buffer */ + NVM_CMD_WRITE_EEPROM_PAGE_gc = (0x34<<0), /* Write EEPROM page */ + NVM_CMD_ERASE_WRITE_EEPROM_PAGE_gc = (0x35<<0), /* Erase-and-write EEPROM page */ + NVM_CMD_ERASE_EEPROM_BUFFER_gc = (0x36<<0), /* Erase/flush EEPROM page buffer */ + NVM_CMD_APP_CRC_gc = (0x38<<0), /* Application section CRC */ + NVM_CMD_BOOT_CRC_gc = (0x39<<0), /* Boot Section CRC */ + NVM_CMD_FLASH_RANGE_CRC_gc = (0x3A<<0), /* Flash Range CRC */ + NVM_CMD_CHIP_ERASE_gc = (0x40<<0), /* Erase Chip */ + NVM_CMD_READ_NVM_gc = (0x43<<0), /* Read NVM */ + NVM_CMD_WRITE_FUSE_gc = (0x4C<<0), /* Write Fuse byte */ + NVM_CMD_ERASE_BOOT_gc = (0x68<<0), /* Erase Boot Section */ + NVM_CMD_FLASH_CRC_gc = (0x78<<0), /* Flash CRC */ +} NVM_CMD_t; + +/* SPM ready interrupt level */ +typedef enum NVM_SPMLVL_enum +{ + NVM_SPMLVL_OFF_gc = (0x00<<2), /* Interrupt disabled */ + NVM_SPMLVL_LO_gc = (0x01<<2), /* Low level */ + NVM_SPMLVL_MED_gc = (0x02<<2), /* Medium level */ + NVM_SPMLVL_HI_gc = (0x03<<2), /* High level */ +} NVM_SPMLVL_t; + +/* EEPROM ready interrupt level */ +typedef enum NVM_EELVL_enum +{ + NVM_EELVL_OFF_gc = (0x00<<0), /* Interrupt disabled */ + NVM_EELVL_LO_gc = (0x01<<0), /* Low level */ + NVM_EELVL_MED_gc = (0x02<<0), /* Medium level */ + NVM_EELVL_HI_gc = (0x03<<0), /* High level */ +} NVM_EELVL_t; + +/* Boot lock bits - boot setcion */ +typedef enum NVM_BLBB_enum +{ + NVM_BLBB_RWLOCK_gc = (0x00<<6), /* Read and write not allowed */ + NVM_BLBB_RLOCK_gc = (0x01<<6), /* Read not allowed */ + NVM_BLBB_WLOCK_gc = (0x02<<6), /* Write not allowed */ + NVM_BLBB_NOLOCK_gc = (0x03<<6), /* No locks */ +} NVM_BLBB_t; + +/* Boot lock bits - application section */ +typedef enum NVM_BLBA_enum +{ + NVM_BLBA_RWLOCK_gc = (0x00<<4), /* Read and write not allowed */ + NVM_BLBA_RLOCK_gc = (0x01<<4), /* Read not allowed */ + NVM_BLBA_WLOCK_gc = (0x02<<4), /* Write not allowed */ + NVM_BLBA_NOLOCK_gc = (0x03<<4), /* No locks */ +} NVM_BLBA_t; + +/* Boot lock bits - application table section */ +typedef enum NVM_BLBAT_enum +{ + NVM_BLBAT_RWLOCK_gc = (0x00<<2), /* Read and write not allowed */ + NVM_BLBAT_RLOCK_gc = (0x01<<2), /* Read not allowed */ + NVM_BLBAT_WLOCK_gc = (0x02<<2), /* Write not allowed */ + NVM_BLBAT_NOLOCK_gc = (0x03<<2), /* No locks */ +} NVM_BLBAT_t; + +/* Lock bits */ +typedef enum NVM_LB_enum +{ + NVM_LB_RWLOCK_gc = (0x00<<0), /* Read and write not allowed */ + NVM_LB_WLOCK_gc = (0x02<<0), /* Write not allowed */ + NVM_LB_NOLOCK_gc = (0x03<<0), /* No locks */ +} NVM_LB_t; + + +/* +-------------------------------------------------------------------------- +AC - Analog Comparator +-------------------------------------------------------------------------- +*/ + +/* Analog Comparator */ +typedef struct AC_struct +{ + register8_t AC0CTRL; /* Analog Comparator 0 Control */ + register8_t AC1CTRL; /* Analog Comparator 1 Control */ + register8_t AC0MUXCTRL; /* Analog Comparator 0 MUX Control */ + register8_t AC1MUXCTRL; /* Analog Comparator 1 MUX Control */ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t WINCTRL; /* Window Mode Control */ + register8_t STATUS; /* Status */ +} AC_t; + +/* Interrupt mode */ +typedef enum AC_INTMODE_enum +{ + AC_INTMODE_BOTHEDGES_gc = (0x00<<6), /* Interrupt on both edges */ + AC_INTMODE_FALLING_gc = (0x02<<6), /* Interrupt on falling edge */ + AC_INTMODE_RISING_gc = (0x03<<6), /* Interrupt on rising edge */ +} AC_INTMODE_t; + +/* Interrupt level */ +typedef enum AC_INTLVL_enum +{ + AC_INTLVL_OFF_gc = (0x00<<4), /* Interrupt disabled */ + AC_INTLVL_LO_gc = (0x01<<4), /* Low level */ + AC_INTLVL_MED_gc = (0x02<<4), /* Medium level */ + AC_INTLVL_HI_gc = (0x03<<4), /* High level */ +} AC_INTLVL_t; + +/* Hysteresis mode selection */ +typedef enum AC_HYSMODE_enum +{ + AC_HYSMODE_NO_gc = (0x00<<1), /* No hysteresis */ + AC_HYSMODE_SMALL_gc = (0x01<<1), /* Small hysteresis */ + AC_HYSMODE_LARGE_gc = (0x02<<1), /* Large hysteresis */ +} AC_HYSMODE_t; + +/* Positive input multiplexer selection */ +typedef enum AC_MUXPOS_enum +{ + AC_MUXPOS_PIN0_gc = (0x00<<3), /* Pin 0 */ + AC_MUXPOS_PIN1_gc = (0x01<<3), /* Pin 1 */ + AC_MUXPOS_PIN2_gc = (0x02<<3), /* Pin 2 */ + AC_MUXPOS_PIN3_gc = (0x03<<3), /* Pin 3 */ + AC_MUXPOS_PIN4_gc = (0x04<<3), /* Pin 4 */ + AC_MUXPOS_PIN5_gc = (0x05<<3), /* Pin 5 */ + AC_MUXPOS_PIN6_gc = (0x06<<3), /* Pin 6 */ + AC_MUXPOS_DAC_gc = (0x07<<3), /* DAC output */ +} AC_MUXPOS_t; + +/* Negative input multiplexer selection */ +typedef enum AC_MUXNEG_enum +{ + AC_MUXNEG_PIN0_gc = (0x00<<0), /* Pin 0 */ + AC_MUXNEG_PIN1_gc = (0x01<<0), /* Pin 1 */ + AC_MUXNEG_PIN3_gc = (0x02<<0), /* Pin 3 */ + AC_MUXNEG_PIN5_gc = (0x03<<0), /* Pin 5 */ + AC_MUXNEG_PIN7_gc = (0x04<<0), /* Pin 7 */ + AC_MUXNEG_DAC_gc = (0x05<<0), /* DAC output */ + AC_MUXNEG_BANDGAP_gc = (0x06<<0), /* Bandgap Reference */ + AC_MUXNEG_SCALER_gc = (0x07<<0), /* Internal voltage scaler */ +} AC_MUXNEG_t; + +/* Windows interrupt mode */ +typedef enum AC_WINTMODE_enum +{ + AC_WINTMODE_ABOVE_gc = (0x00<<2), /* Interrupt on above window */ + AC_WINTMODE_INSIDE_gc = (0x01<<2), /* Interrupt on inside window */ + AC_WINTMODE_BELOW_gc = (0x02<<2), /* Interrupt on below window */ + AC_WINTMODE_OUTSIDE_gc = (0x03<<2), /* Interrupt on outside window */ +} AC_WINTMODE_t; + +/* Window interrupt level */ +typedef enum AC_WINTLVL_enum +{ + AC_WINTLVL_OFF_gc = (0x00<<0), /* Interrupt disabled */ + AC_WINTLVL_LO_gc = (0x01<<0), /* Low priority */ + AC_WINTLVL_MED_gc = (0x02<<0), /* Medium priority */ + AC_WINTLVL_HI_gc = (0x03<<0), /* High priority */ +} AC_WINTLVL_t; + +/* Window mode state */ +typedef enum AC_WSTATE_enum +{ + AC_WSTATE_ABOVE_gc = (0x00<<6), /* Signal above window */ + AC_WSTATE_INSIDE_gc = (0x01<<6), /* Signal inside window */ + AC_WSTATE_BELOW_gc = (0x02<<6), /* Signal below window */ +} AC_WSTATE_t; + + +/* +-------------------------------------------------------------------------- +ADC - Analog/Digital Converter +-------------------------------------------------------------------------- +*/ + +/* ADC Channel */ +typedef struct ADC_CH_struct +{ + register8_t CTRL; /* Control Register */ + register8_t MUXCTRL; /* MUX Control */ + register8_t INTCTRL; /* Channel Interrupt Control Register */ + register8_t INTFLAGS; /* Interrupt Flags */ + _WORDREGISTER(RES); /* Channel Result */ + register8_t SCAN; /* Input Channel Scan */ + register8_t reserved_0x07; +} ADC_CH_t; + + +/* Analog-to-Digital Converter */ +typedef struct ADC_struct +{ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t REFCTRL; /* Reference Control */ + register8_t EVCTRL; /* Event Control */ + register8_t PRESCALER; /* Clock Prescaler */ + register8_t reserved_0x05; + register8_t INTFLAGS; /* Interrupt Flags */ + register8_t TEMP; /* Temporary Register */ + register8_t reserved_0x08; + register8_t reserved_0x09; + register8_t reserved_0x0A; + register8_t reserved_0x0B; + _WORDREGISTER(CAL); /* Calibration Value */ + register8_t reserved_0x0E; + register8_t reserved_0x0F; + _WORDREGISTER(CH0RES); /* Channel 0 Result */ + _WORDREGISTER(CH1RES); /* Channel 1 Result */ + _WORDREGISTER(CH2RES); /* Channel 2 Result */ + _WORDREGISTER(CH3RES); /* Channel 3 Result */ + _WORDREGISTER(CMP); /* Compare Value */ + register8_t reserved_0x1A; + register8_t reserved_0x1B; + register8_t reserved_0x1C; + register8_t reserved_0x1D; + register8_t reserved_0x1E; + register8_t reserved_0x1F; + ADC_CH_t CH0; /* ADC Channel 0 */ + ADC_CH_t CH1; /* ADC Channel 1 */ + ADC_CH_t CH2; /* ADC Channel 2 */ + ADC_CH_t CH3; /* ADC Channel 3 */ +} ADC_t; + +/* Positive input multiplexer selection */ +typedef enum ADC_CH_MUXPOS_enum +{ + ADC_CH_MUXPOS_PIN0_gc = (0x00<<3), /* Input pin 0 */ + ADC_CH_MUXPOS_PIN1_gc = (0x01<<3), /* Input pin 1 */ + ADC_CH_MUXPOS_PIN2_gc = (0x02<<3), /* Input pin 2 */ + ADC_CH_MUXPOS_PIN3_gc = (0x03<<3), /* Input pin 3 */ + ADC_CH_MUXPOS_PIN4_gc = (0x04<<3), /* Input pin 4 */ + ADC_CH_MUXPOS_PIN5_gc = (0x05<<3), /* Input pin 5 */ + ADC_CH_MUXPOS_PIN6_gc = (0x06<<3), /* Input pin 6 */ + ADC_CH_MUXPOS_PIN7_gc = (0x07<<3), /* Input pin 7 */ + ADC_CH_MUXPOS_PIN8_gc = (0x08<<3), /* Input pin 8 */ + ADC_CH_MUXPOS_PIN9_gc = (0x09<<3), /* Input pin 9 */ + ADC_CH_MUXPOS_PIN10_gc = (0x0A<<3), /* Input pin 10 */ + ADC_CH_MUXPOS_PIN11_gc = (0x0B<<3), /* Input pin 11 */ + ADC_CH_MUXPOS_PIN12_gc = (0x0C<<3), /* Input pin 12 */ + ADC_CH_MUXPOS_PIN13_gc = (0x0D<<3), /* Input pin 13 */ + ADC_CH_MUXPOS_PIN14_gc = (0x0E<<3), /* Input pin 14 */ + ADC_CH_MUXPOS_PIN15_gc = (0x0F<<3), /* Input pin 15 */ +} ADC_CH_MUXPOS_t; + +/* Internal input multiplexer selections */ +typedef enum ADC_CH_MUXINT_enum +{ + ADC_CH_MUXINT_TEMP_gc = (0x00<<3), /* Temperature Reference */ + ADC_CH_MUXINT_BANDGAP_gc = (0x01<<3), /* Bandgap Reference */ + ADC_CH_MUXINT_SCALEDVCC_gc = (0x02<<3), /* 1/10 scaled VCC */ + ADC_CH_MUXINT_DAC_gc = (0x03<<3), /* DAC output */ +} ADC_CH_MUXINT_t; + +/* Negative input multiplexer selection */ +typedef enum ADC_CH_MUXNEG_enum +{ + ADC_CH_MUXNEG_PIN0_gc = (0x00<<0), /* Input pin 0 (Input Mode = 2) */ + ADC_CH_MUXNEG_PIN1_gc = (0x01<<0), /* Input pin 1 (Input Mode = 2) */ + ADC_CH_MUXNEG_PIN2_gc = (0x02<<0), /* Input pin 2 (Input Mode = 2) */ + ADC_CH_MUXNEG_PIN3_gc = (0x03<<0), /* Input pin 3 (Input Mode = 2) */ + ADC_CH_MUXNEG_PIN4_gc = (0x00<<0), /* Input pin 4 (Input Mode = 3) */ + ADC_CH_MUXNEG_PIN5_gc = (0x01<<0), /* Input pin 5 (Input Mode = 3) */ + ADC_CH_MUXNEG_PIN6_gc = (0x02<<0), /* Input pin 6 (Input Mode = 3) */ + ADC_CH_MUXNEG_PIN7_gc = (0x03<<0), /* Input pin 7 (Input Mode = 3) */ + ADC_CH_MUXNEG_GND_MODE3_gc = (0x05<<0), /* PAD Ground (Input Mode = 2) */ + ADC_CH_MUXNEG_INTGND_MODE3_gc = (0x07<<0), /* Internal Ground (Input Mode = 2) */ + ADC_CH_MUXNEG_INTGND_MODE4_gc = (0x04<<0), /* Internal Ground (Input Mode = 3) */ + ADC_CH_MUXNEG_GND_MODE4_gc = (0x07<<0), /* PAD Ground (Input Mode = 3) */ +} ADC_CH_MUXNEG_t; + +/* Input mode */ +typedef enum ADC_CH_INPUTMODE_enum +{ + ADC_CH_INPUTMODE_INTERNAL_gc = (0x00<<0), /* Internal inputs, no gain */ + ADC_CH_INPUTMODE_SINGLEENDED_gc = (0x01<<0), /* Single-ended input, no gain */ + ADC_CH_INPUTMODE_DIFF_gc = (0x02<<0), /* Differential input, no gain */ + ADC_CH_INPUTMODE_DIFFWGAIN_gc = (0x03<<0), /* Differential input, with gain */ +} ADC_CH_INPUTMODE_t; + +/* Gain factor */ +typedef enum ADC_CH_GAIN_enum +{ + ADC_CH_GAIN_1X_gc = (0x00<<2), /* 1x gain */ + ADC_CH_GAIN_2X_gc = (0x01<<2), /* 2x gain */ + ADC_CH_GAIN_4X_gc = (0x02<<2), /* 4x gain */ + ADC_CH_GAIN_8X_gc = (0x03<<2), /* 8x gain */ + ADC_CH_GAIN_16X_gc = (0x04<<2), /* 16x gain */ + ADC_CH_GAIN_32X_gc = (0x05<<2), /* 32x gain */ + ADC_CH_GAIN_64X_gc = (0x06<<2), /* 64x gain */ + ADC_CH_GAIN_DIV2_gc = (0x07<<2), /* x/2 gain */ +} ADC_CH_GAIN_t; + +/* Conversion result resolution */ +typedef enum ADC_RESOLUTION_enum +{ + ADC_RESOLUTION_12BIT_gc = (0x00<<1), /* 12-bit right-adjusted result */ + ADC_RESOLUTION_8BIT_gc = (0x02<<1), /* 8-bit right-adjusted result */ + ADC_RESOLUTION_LEFT12BIT_gc = (0x03<<1), /* 12-bit left-adjusted result */ +} ADC_RESOLUTION_t; + +/* Current Limitation Mode */ +typedef enum ADC_CURRLIMIT_enum +{ + ADC_CURRLIMIT_NO_gc = (0x00<<5), /* No limit */ + ADC_CURRLIMIT_LOW_gc = (0x01<<5), /* Low current limit, max. sampling rate 1.5MSPS */ + ADC_CURRLIMIT_MED_gc = (0x02<<5), /* Medium current limit, max. sampling rate 1MSPS */ + ADC_CURRLIMIT_HIGH_gc = (0x03<<5), /* High current limit, max. sampling rate 0.5MSPS */ +} ADC_CURRLIMIT_t; + +/* Voltage reference selection */ +typedef enum ADC_REFSEL_enum +{ + ADC_REFSEL_INT1V_gc = (0x00<<4), /* Internal 1V */ + ADC_REFSEL_INTVCC_gc = (0x01<<4), /* Internal VCC / 1.6 */ + ADC_REFSEL_AREFA_gc = (0x02<<4), /* External reference on PORT A */ + ADC_REFSEL_AREFB_gc = (0x03<<4), /* External reference on PORT B */ + ADC_REFSEL_INTVCC2_gc = (0x04<<4), /* Internal VCC / 2 */ +} ADC_REFSEL_t; + +/* Channel sweep selection */ +typedef enum ADC_SWEEP_enum +{ + ADC_SWEEP_0_gc = (0x00<<6), /* ADC Channel 0 */ + ADC_SWEEP_01_gc = (0x01<<6), /* ADC Channel 0,1 */ + ADC_SWEEP_012_gc = (0x02<<6), /* ADC Channel 0,1,2 */ + ADC_SWEEP_0123_gc = (0x03<<6), /* ADC Channel 0,1,2,3 */ +} ADC_SWEEP_t; + +/* Event channel input selection */ +typedef enum ADC_EVSEL_enum +{ + ADC_EVSEL_0123_gc = (0x00<<3), /* Event Channel 0,1,2,3 */ + ADC_EVSEL_1234_gc = (0x01<<3), /* Event Channel 1,2,3,4 */ + ADC_EVSEL_2345_gc = (0x02<<3), /* Event Channel 2,3,4,5 */ + ADC_EVSEL_3456_gc = (0x03<<3), /* Event Channel 3,4,5,6 */ + ADC_EVSEL_4567_gc = (0x04<<3), /* Event Channel 4,5,6,7 */ + ADC_EVSEL_567_gc = (0x05<<3), /* Event Channel 5,6,7 */ + ADC_EVSEL_67_gc = (0x06<<3), /* Event Channel 6,7 */ + ADC_EVSEL_7_gc = (0x07<<3), /* Event Channel 7 */ +} ADC_EVSEL_t; + +/* Event action selection */ +typedef enum ADC_EVACT_enum +{ + ADC_EVACT_NONE_gc = (0x00<<0), /* No event action */ + ADC_EVACT_CH0_gc = (0x01<<0), /* First event triggers channel 0 */ + ADC_EVACT_CH01_gc = (0x02<<0), /* First two events trigger channel 0,1 */ + ADC_EVACT_CH012_gc = (0x03<<0), /* First three events trigger channel 0,1,2 */ + ADC_EVACT_CH0123_gc = (0x04<<0), /* Events trigger channel 0,1,2,3 */ + ADC_EVACT_SWEEP_gc = (0x05<<0), /* First event triggers sweep */ + ADC_EVACT_SYNCSWEEP_gc = (0x06<<0), /* The ADC is flushed and restarted for accurate timing */ +} ADC_EVACT_t; + +/* Interupt mode */ +typedef enum ADC_CH_INTMODE_enum +{ + ADC_CH_INTMODE_COMPLETE_gc = (0x00<<2), /* Interrupt on conversion complete */ + ADC_CH_INTMODE_BELOW_gc = (0x01<<2), /* Interrupt on result below compare value */ + ADC_CH_INTMODE_ABOVE_gc = (0x03<<2), /* Interrupt on result above compare value */ +} ADC_CH_INTMODE_t; + +/* Interrupt level */ +typedef enum ADC_CH_INTLVL_enum +{ + ADC_CH_INTLVL_OFF_gc = (0x00<<0), /* Interrupt disabled */ + ADC_CH_INTLVL_LO_gc = (0x01<<0), /* Low level */ + ADC_CH_INTLVL_MED_gc = (0x02<<0), /* Medium level */ + ADC_CH_INTLVL_HI_gc = (0x03<<0), /* High level */ +} ADC_CH_INTLVL_t; + +/* DMA request selection */ +typedef enum ADC_DMASEL_enum +{ + ADC_DMASEL_OFF_gc = (0x00<<6), /* Combined DMA request OFF */ + ADC_DMASEL_CH01_gc = (0x01<<6), /* ADC Channel 0 or 1 */ + ADC_DMASEL_CH012_gc = (0x02<<6), /* ADC Channel 0 or 1 or 2 */ + ADC_DMASEL_CH0123_gc = (0x03<<6), /* ADC Channel 0 or 1 or 2 or 3 */ +} ADC_DMASEL_t; + +/* Clock prescaler */ +typedef enum ADC_PRESCALER_enum +{ + ADC_PRESCALER_DIV4_gc = (0x00<<0), /* Divide clock by 4 */ + ADC_PRESCALER_DIV8_gc = (0x01<<0), /* Divide clock by 8 */ + ADC_PRESCALER_DIV16_gc = (0x02<<0), /* Divide clock by 16 */ + ADC_PRESCALER_DIV32_gc = (0x03<<0), /* Divide clock by 32 */ + ADC_PRESCALER_DIV64_gc = (0x04<<0), /* Divide clock by 64 */ + ADC_PRESCALER_DIV128_gc = (0x05<<0), /* Divide clock by 128 */ + ADC_PRESCALER_DIV256_gc = (0x06<<0), /* Divide clock by 256 */ + ADC_PRESCALER_DIV512_gc = (0x07<<0), /* Divide clock by 512 */ +} ADC_PRESCALER_t; + + +/* +-------------------------------------------------------------------------- +DAC - Digital/Analog Converter +-------------------------------------------------------------------------- +*/ + +/* Digital-to-Analog Converter */ +typedef struct DAC_struct +{ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t CTRLC; /* Control Register C */ + register8_t EVCTRL; /* Event Input Control */ + register8_t reserved_0x04; + register8_t STATUS; /* Status */ + register8_t reserved_0x06; + register8_t reserved_0x07; + register8_t CH0GAINCAL; /* Gain Calibration */ + register8_t CH0OFFSETCAL; /* Offset Calibration */ + register8_t CH1GAINCAL; /* Gain Calibration */ + register8_t CH1OFFSETCAL; /* Offset Calibration */ + register8_t reserved_0x0C; + register8_t reserved_0x0D; + register8_t reserved_0x0E; + register8_t reserved_0x0F; + register8_t reserved_0x10; + register8_t reserved_0x11; + register8_t reserved_0x12; + register8_t reserved_0x13; + register8_t reserved_0x14; + register8_t reserved_0x15; + register8_t reserved_0x16; + register8_t reserved_0x17; + _WORDREGISTER(CH0DATA); /* Channel 0 Data */ + _WORDREGISTER(CH1DATA); /* Channel 1 Data */ +} DAC_t; + +/* Output channel selection */ +typedef enum DAC_CHSEL_enum +{ + DAC_CHSEL_SINGLE_gc = (0x00<<5), /* Single channel operation (Channel 0 only) */ + DAC_CHSEL_SINGLE1_gc = (0x01<<5), /* Single channel operation (Channel 1 only) */ + DAC_CHSEL_DUAL_gc = (0x02<<5), /* Dual channel operation (Channel 0 and channel 1) */ +} DAC_CHSEL_t; + +/* Reference voltage selection */ +typedef enum DAC_REFSEL_enum +{ + DAC_REFSEL_INT1V_gc = (0x00<<3), /* Internal 1V */ + DAC_REFSEL_AVCC_gc = (0x01<<3), /* Analog supply voltage */ + DAC_REFSEL_AREFA_gc = (0x02<<3), /* External reference on AREF on PORTA */ + DAC_REFSEL_AREFB_gc = (0x03<<3), /* External reference on AREF on PORTB */ +} DAC_REFSEL_t; + +/* Event channel selection */ +typedef enum DAC_EVSEL_enum +{ + DAC_EVSEL_0_gc = (0x00<<0), /* Event Channel 0 */ + DAC_EVSEL_1_gc = (0x01<<0), /* Event Channel 1 */ + DAC_EVSEL_2_gc = (0x02<<0), /* Event Channel 2 */ + DAC_EVSEL_3_gc = (0x03<<0), /* Event Channel 3 */ + DAC_EVSEL_4_gc = (0x04<<0), /* Event Channel 4 */ + DAC_EVSEL_5_gc = (0x05<<0), /* Event Channel 5 */ + DAC_EVSEL_6_gc = (0x06<<0), /* Event Channel 6 */ + DAC_EVSEL_7_gc = (0x07<<0), /* Event Channel 7 */ +} DAC_EVSEL_t; + + +/* +-------------------------------------------------------------------------- +RTC - Real-Time Counter +-------------------------------------------------------------------------- +*/ + +/* Real-Time Counter */ +typedef struct RTC_struct +{ + register8_t CTRL; /* Control Register */ + register8_t STATUS; /* Status Register */ + register8_t INTCTRL; /* Interrupt Control Register */ + register8_t INTFLAGS; /* Interrupt Flags */ + register8_t TEMP; /* Temporary register */ + register8_t reserved_0x05; + register8_t reserved_0x06; + register8_t reserved_0x07; + _WORDREGISTER(CNT); /* Count Register */ + _WORDREGISTER(PER); /* Period Register */ + _WORDREGISTER(COMP); /* Compare Register */ +} RTC_t; + +/* Prescaler Factor */ +typedef enum RTC_PRESCALER_enum +{ + RTC_PRESCALER_OFF_gc = (0x00<<0), /* RTC Off */ + RTC_PRESCALER_DIV1_gc = (0x01<<0), /* RTC Clock */ + RTC_PRESCALER_DIV2_gc = (0x02<<0), /* RTC Clock / 2 */ + RTC_PRESCALER_DIV8_gc = (0x03<<0), /* RTC Clock / 8 */ + RTC_PRESCALER_DIV16_gc = (0x04<<0), /* RTC Clock / 16 */ + RTC_PRESCALER_DIV64_gc = (0x05<<0), /* RTC Clock / 64 */ + RTC_PRESCALER_DIV256_gc = (0x06<<0), /* RTC Clock / 256 */ + RTC_PRESCALER_DIV1024_gc = (0x07<<0), /* RTC Clock / 1024 */ +} RTC_PRESCALER_t; + +/* Compare Interrupt level */ +typedef enum RTC_COMPINTLVL_enum +{ + RTC_COMPINTLVL_OFF_gc = (0x00<<2), /* Interrupt Disabled */ + RTC_COMPINTLVL_LO_gc = (0x01<<2), /* Low Level */ + RTC_COMPINTLVL_MED_gc = (0x02<<2), /* Medium Level */ + RTC_COMPINTLVL_HI_gc = (0x03<<2), /* High Level */ +} RTC_COMPINTLVL_t; + +/* Overflow Interrupt level */ +typedef enum RTC_OVFINTLVL_enum +{ + RTC_OVFINTLVL_OFF_gc = (0x00<<0), /* Interrupt Disabled */ + RTC_OVFINTLVL_LO_gc = (0x01<<0), /* Low Level */ + RTC_OVFINTLVL_MED_gc = (0x02<<0), /* Medium Level */ + RTC_OVFINTLVL_HI_gc = (0x03<<0), /* High Level */ +} RTC_OVFINTLVL_t; + + +/* +-------------------------------------------------------------------------- +TWI - Two-Wire Interface +-------------------------------------------------------------------------- +*/ + +/* */ +typedef struct TWI_MASTER_struct +{ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t CTRLC; /* Control Register C */ + register8_t STATUS; /* Status Register */ + register8_t BAUD; /* Baurd Rate Control Register */ + register8_t ADDR; /* Address Register */ + register8_t DATA; /* Data Register */ +} TWI_MASTER_t; + + +/* */ +typedef struct TWI_SLAVE_struct +{ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t STATUS; /* Status Register */ + register8_t ADDR; /* Address Register */ + register8_t DATA; /* Data Register */ + register8_t ADDRMASK; /* Address Mask Register */ +} TWI_SLAVE_t; + + +/* Two-Wire Interface */ +typedef struct TWI_struct +{ + register8_t CTRL; /* TWI Common Control Register */ + TWI_MASTER_t MASTER; /* TWI master module */ + TWI_SLAVE_t SLAVE; /* TWI slave module */ +} TWI_t; + +/* SDA Hold Time */ +typedef enum TWI_SDAHOLD_enum +{ + TWI_SDAHOLD_OFF_gc = (0x00<<1), /* SDA Hold Time off */ + TWI_SDAHOLD_50NS_gc = (0x01<<1), /* SDA Hold Time 50 ns */ + TWI_SDAHOLD_300NS_gc = (0x02<<1), /* SDA Hold Time 300 ns */ + TWI_SDAHOLD_400NS_gc = (0x03<<1), /* SDA Hold Time 400 ns */ +} TWI_SDAHOLD_t; + +/* Master Interrupt Level */ +typedef enum TWI_MASTER_INTLVL_enum +{ + TWI_MASTER_INTLVL_OFF_gc = (0x00<<6), /* Interrupt Disabled */ + TWI_MASTER_INTLVL_LO_gc = (0x01<<6), /* Low Level */ + TWI_MASTER_INTLVL_MED_gc = (0x02<<6), /* Medium Level */ + TWI_MASTER_INTLVL_HI_gc = (0x03<<6), /* High Level */ +} TWI_MASTER_INTLVL_t; + +/* Inactive Timeout */ +typedef enum TWI_MASTER_TIMEOUT_enum +{ + TWI_MASTER_TIMEOUT_DISABLED_gc = (0x00<<2), /* Bus Timeout Disabled */ + TWI_MASTER_TIMEOUT_50US_gc = (0x01<<2), /* 50 Microseconds */ + TWI_MASTER_TIMEOUT_100US_gc = (0x02<<2), /* 100 Microseconds */ + TWI_MASTER_TIMEOUT_200US_gc = (0x03<<2), /* 200 Microseconds */ +} TWI_MASTER_TIMEOUT_t; + +/* Master Command */ +typedef enum TWI_MASTER_CMD_enum +{ + TWI_MASTER_CMD_NOACT_gc = (0x00<<0), /* No Action */ + TWI_MASTER_CMD_REPSTART_gc = (0x01<<0), /* Issue Repeated Start Condition */ + TWI_MASTER_CMD_RECVTRANS_gc = (0x02<<0), /* Receive or Transmit Data */ + TWI_MASTER_CMD_STOP_gc = (0x03<<0), /* Issue Stop Condition */ +} TWI_MASTER_CMD_t; + +/* Master Bus State */ +typedef enum TWI_MASTER_BUSSTATE_enum +{ + TWI_MASTER_BUSSTATE_UNKNOWN_gc = (0x00<<0), /* Unknown Bus State */ + TWI_MASTER_BUSSTATE_IDLE_gc = (0x01<<0), /* Bus is Idle */ + TWI_MASTER_BUSSTATE_OWNER_gc = (0x02<<0), /* This Module Controls The Bus */ + TWI_MASTER_BUSSTATE_BUSY_gc = (0x03<<0), /* The Bus is Busy */ +} TWI_MASTER_BUSSTATE_t; + +/* Slave Interrupt Level */ +typedef enum TWI_SLAVE_INTLVL_enum +{ + TWI_SLAVE_INTLVL_OFF_gc = (0x00<<6), /* Interrupt Disabled */ + TWI_SLAVE_INTLVL_LO_gc = (0x01<<6), /* Low Level */ + TWI_SLAVE_INTLVL_MED_gc = (0x02<<6), /* Medium Level */ + TWI_SLAVE_INTLVL_HI_gc = (0x03<<6), /* High Level */ +} TWI_SLAVE_INTLVL_t; + +/* Slave Command */ +typedef enum TWI_SLAVE_CMD_enum +{ + TWI_SLAVE_CMD_NOACT_gc = (0x00<<0), /* No Action */ + TWI_SLAVE_CMD_COMPTRANS_gc = (0x02<<0), /* Used To Complete a Transaction */ + TWI_SLAVE_CMD_RESPONSE_gc = (0x03<<0), /* Used in Response to Address/Data Interrupt */ +} TWI_SLAVE_CMD_t; + + +/* +-------------------------------------------------------------------------- +USB - USB +-------------------------------------------------------------------------- +*/ + +/* USB Endpoint */ +typedef struct USB_EP_struct +{ + register8_t STATUS; /* Endpoint Status */ + register8_t CTRL; /* Endpoint Control */ + _WORDREGISTER(CNT); /* USB Endpoint Counter */ + _WORDREGISTER(DATAPTR); /* Data Pointer */ + _WORDREGISTER(AUXDATA); /* Auxiliary Data */ +} USB_EP_t; + + +/* Universal Serial Bus */ +typedef struct USB_struct +{ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t STATUS; /* Status Register */ + register8_t ADDR; /* Address Register */ + register8_t FIFOWP; /* FIFO Write Pointer Register */ + register8_t FIFORP; /* FIFO Read Pointer Register */ + _WORDREGISTER(EPPTR); /* Endpoint Configuration Table Pointer */ + register8_t INTCTRLA; /* Interrupt Control Register A */ + register8_t INTCTRLB; /* Interrupt Control Register B */ + register8_t INTFLAGSACLR; /* Clear Interrupt Flag Register A */ + register8_t INTFLAGSASET; /* Set Interrupt Flag Register A */ + register8_t INTFLAGSBCLR; /* Clear Interrupt Flag Register B */ + register8_t INTFLAGSBSET; /* Set Interrupt Flag Register B */ + register8_t reserved_0x0E; + register8_t reserved_0x0F; + register8_t reserved_0x10; + register8_t reserved_0x11; + register8_t reserved_0x12; + register8_t reserved_0x13; + register8_t reserved_0x14; + register8_t reserved_0x15; + register8_t reserved_0x16; + register8_t reserved_0x17; + register8_t reserved_0x18; + register8_t reserved_0x19; + register8_t reserved_0x1A; + register8_t reserved_0x1B; + register8_t reserved_0x1C; + register8_t reserved_0x1D; + register8_t reserved_0x1E; + register8_t reserved_0x1F; + register8_t reserved_0x20; + register8_t reserved_0x21; + register8_t reserved_0x22; + register8_t reserved_0x23; + register8_t reserved_0x24; + register8_t reserved_0x25; + register8_t reserved_0x26; + register8_t reserved_0x27; + register8_t reserved_0x28; + register8_t reserved_0x29; + register8_t reserved_0x2A; + register8_t reserved_0x2B; + register8_t reserved_0x2C; + register8_t reserved_0x2D; + register8_t reserved_0x2E; + register8_t reserved_0x2F; + register8_t reserved_0x30; + register8_t reserved_0x31; + register8_t reserved_0x32; + register8_t reserved_0x33; + register8_t reserved_0x34; + register8_t reserved_0x35; + register8_t reserved_0x36; + register8_t reserved_0x37; + register8_t reserved_0x38; + register8_t reserved_0x39; + register8_t CAL0; /* Calibration Byte 0 */ + register8_t CAL1; /* Calibration Byte 1 */ +} USB_t; + + +/* USB Endpoint Table */ +typedef struct USB_EP_TABLE_struct +{ + USB_EP_t EP0OUT; /* Endpoint 0 */ + USB_EP_t EP0IN; /* Endpoint 0 */ + USB_EP_t EP1OUT; /* Endpoint 1 */ + USB_EP_t EP1IN; /* Endpoint 1 */ + USB_EP_t EP2OUT; /* Endpoint 2 */ + USB_EP_t EP2IN; /* Endpoint 2 */ + USB_EP_t EP3OUT; /* Endpoint 3 */ + USB_EP_t EP3IN; /* Endpoint 3 */ + USB_EP_t EP4OUT; /* Endpoint 4 */ + USB_EP_t EP4IN; /* Endpoint 4 */ + USB_EP_t EP5OUT; /* Endpoint 5 */ + USB_EP_t EP5IN; /* Endpoint 5 */ + USB_EP_t EP6OUT; /* Endpoint 6 */ + USB_EP_t EP6IN; /* Endpoint 6 */ + USB_EP_t EP7OUT; /* Endpoint 7 */ + USB_EP_t EP7IN; /* Endpoint 7 */ + USB_EP_t EP8OUT; /* Endpoint 8 */ + USB_EP_t EP8IN; /* Endpoint 8 */ + USB_EP_t EP9OUT; /* Endpoint 9 */ + USB_EP_t EP9IN; /* Endpoint 9 */ + USB_EP_t EP10OUT; /* Endpoint 10 */ + USB_EP_t EP10IN; /* Endpoint 10 */ + USB_EP_t EP11OUT; /* Endpoint 11 */ + USB_EP_t EP11IN; /* Endpoint 11 */ + USB_EP_t EP12OUT; /* Endpoint 12 */ + USB_EP_t EP12IN; /* Endpoint 12 */ + USB_EP_t EP13OUT; /* Endpoint 13 */ + USB_EP_t EP13IN; /* Endpoint 13 */ + USB_EP_t EP14OUT; /* Endpoint 14 */ + USB_EP_t EP14IN; /* Endpoint 14 */ + USB_EP_t EP15OUT; /* Endpoint 15 */ + USB_EP_t EP15IN; /* Endpoint 15 */ + register8_t reserved_0x100; + register8_t reserved_0x101; + register8_t reserved_0x102; + register8_t reserved_0x103; + register8_t reserved_0x104; + register8_t reserved_0x105; + register8_t reserved_0x106; + register8_t reserved_0x107; + register8_t reserved_0x108; + register8_t reserved_0x109; + register8_t reserved_0x10A; + register8_t reserved_0x10B; + register8_t reserved_0x10C; + register8_t reserved_0x10D; + register8_t reserved_0x10E; + register8_t reserved_0x10F; + register8_t FRAMENUML; /* Frame Number Low Byte */ + register8_t FRAMENUMH; /* Frame Number High Byte */ +} USB_EP_TABLE_t; + +/* Interrupt level */ +typedef enum USB_INTLVL_enum +{ + USB_INTLVL_OFF_gc = (0x00<<0), /* Interrupt disabled */ + USB_INTLVL_LO_gc = (0x01<<0), /* Low level */ + USB_INTLVL_MED_gc = (0x02<<0), /* Medium level */ + USB_INTLVL_HI_gc = (0x03<<0), /* High level */ +} USB_INTLVL_t; + +/* USB Endpoint Type */ +typedef enum USB_EP_TYPE_enum +{ + USB_EP_TYPE_DISABLE_gc = (0x00<<6), /* Endpoint Disabled */ + USB_EP_TYPE_CONTROL_gc = (0x01<<6), /* Control */ + USB_EP_TYPE_BULK_gc = (0x02<<6), /* Bulk/Interrupt */ + USB_EP_TYPE_ISOCHRONOUS_gc = (0x03<<6), /* Isochronous */ +} USB_EP_TYPE_t; + +/* USB Endpoint Buffersize */ +typedef enum USB_EP_BUFSIZE_enum +{ + USB_EP_BUFSIZE_8_gc = (0x00<<0), /* 8 bytes buffer size */ + USB_EP_BUFSIZE_16_gc = (0x01<<0), /* 16 bytes buffer size */ + USB_EP_BUFSIZE_32_gc = (0x02<<0), /* 32 bytes buffer size */ + USB_EP_BUFSIZE_64_gc = (0x03<<0), /* 64 bytes buffer size */ + USB_EP_BUFSIZE_128_gc = (0x04<<0), /* 128 bytes buffer size */ + USB_EP_BUFSIZE_256_gc = (0x05<<0), /* 256 bytes buffer size */ + USB_EP_BUFSIZE_512_gc = (0x06<<0), /* 512 bytes buffer size */ + USB_EP_BUFSIZE_1023_gc = (0x07<<0), /* 1023 bytes buffer size */ +} USB_EP_BUFSIZE_t; + + +/* +-------------------------------------------------------------------------- +PORT - I/O Port Configuration +-------------------------------------------------------------------------- +*/ + +/* I/O Ports */ +typedef struct PORT_struct +{ + register8_t DIR; /* I/O Port Data Direction */ + register8_t DIRSET; /* I/O Port Data Direction Set */ + register8_t DIRCLR; /* I/O Port Data Direction Clear */ + register8_t DIRTGL; /* I/O Port Data Direction Toggle */ + register8_t OUT; /* I/O Port Output */ + register8_t OUTSET; /* I/O Port Output Set */ + register8_t OUTCLR; /* I/O Port Output Clear */ + register8_t OUTTGL; /* I/O Port Output Toggle */ + register8_t IN; /* I/O port Input */ + register8_t INTCTRL; /* Interrupt Control Register */ + register8_t INT0MASK; /* Port Interrupt 0 Mask */ + register8_t INT1MASK; /* Port Interrupt 1 Mask */ + register8_t INTFLAGS; /* Interrupt Flag Register */ + register8_t reserved_0x0D; + register8_t REMAP; /* I/O Port Pin Remap Register */ + register8_t reserved_0x0F; + register8_t PIN0CTRL; /* Pin 0 Control Register */ + register8_t PIN1CTRL; /* Pin 1 Control Register */ + register8_t PIN2CTRL; /* Pin 2 Control Register */ + register8_t PIN3CTRL; /* Pin 3 Control Register */ + register8_t PIN4CTRL; /* Pin 4 Control Register */ + register8_t PIN5CTRL; /* Pin 5 Control Register */ + register8_t PIN6CTRL; /* Pin 6 Control Register */ + register8_t PIN7CTRL; /* Pin 7 Control Register */ +} PORT_t; + +/* Port Interrupt 0 Level */ +typedef enum PORT_INT0LVL_enum +{ + PORT_INT0LVL_OFF_gc = (0x00<<0), /* Interrupt Disabled */ + PORT_INT0LVL_LO_gc = (0x01<<0), /* Low Level */ + PORT_INT0LVL_MED_gc = (0x02<<0), /* Medium Level */ + PORT_INT0LVL_HI_gc = (0x03<<0), /* High Level */ +} PORT_INT0LVL_t; + +/* Port Interrupt 1 Level */ +typedef enum PORT_INT1LVL_enum +{ + PORT_INT1LVL_OFF_gc = (0x00<<2), /* Interrupt Disabled */ + PORT_INT1LVL_LO_gc = (0x01<<2), /* Low Level */ + PORT_INT1LVL_MED_gc = (0x02<<2), /* Medium Level */ + PORT_INT1LVL_HI_gc = (0x03<<2), /* High Level */ +} PORT_INT1LVL_t; + +/* Output/Pull Configuration */ +typedef enum PORT_OPC_enum +{ + PORT_OPC_TOTEM_gc = (0x00<<3), /* Totempole */ + PORT_OPC_BUSKEEPER_gc = (0x01<<3), /* Totempole w/ Bus keeper on Input and Output */ + PORT_OPC_PULLDOWN_gc = (0x02<<3), /* Totempole w/ Pull-down on Input */ + PORT_OPC_PULLUP_gc = (0x03<<3), /* Totempole w/ Pull-up on Input */ + PORT_OPC_WIREDOR_gc = (0x04<<3), /* Wired OR */ + PORT_OPC_WIREDAND_gc = (0x05<<3), /* Wired AND */ + PORT_OPC_WIREDORPULL_gc = (0x06<<3), /* Wired OR w/ Pull-down */ + PORT_OPC_WIREDANDPULL_gc = (0x07<<3), /* Wired AND w/ Pull-up */ +} PORT_OPC_t; + +/* Input/Sense Configuration */ +typedef enum PORT_ISC_enum +{ + PORT_ISC_BOTHEDGES_gc = (0x00<<0), /* Sense Both Edges */ + PORT_ISC_RISING_gc = (0x01<<0), /* Sense Rising Edge */ + PORT_ISC_FALLING_gc = (0x02<<0), /* Sense Falling Edge */ + PORT_ISC_LEVEL_gc = (0x03<<0), /* Sense Level (Transparent For Events) */ + PORT_ISC_INPUT_DISABLE_gc = (0x07<<0), /* Disable Digital Input Buffer */ +} PORT_ISC_t; + + +/* +-------------------------------------------------------------------------- +TC - 16-bit Timer/Counter With PWM +-------------------------------------------------------------------------- +*/ + +/* 16-bit Timer/Counter 0 */ +typedef struct TC0_struct +{ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t CTRLC; /* Control register C */ + register8_t CTRLD; /* Control Register D */ + register8_t CTRLE; /* Control Register E */ + register8_t reserved_0x05; + register8_t INTCTRLA; /* Interrupt Control Register A */ + register8_t INTCTRLB; /* Interrupt Control Register B */ + register8_t CTRLFCLR; /* Control Register F Clear */ + register8_t CTRLFSET; /* Control Register F Set */ + register8_t CTRLGCLR; /* Control Register G Clear */ + register8_t CTRLGSET; /* Control Register G Set */ + register8_t INTFLAGS; /* Interrupt Flag Register */ + register8_t reserved_0x0D; + register8_t reserved_0x0E; + register8_t TEMP; /* Temporary Register For 16-bit Access */ + register8_t reserved_0x10; + register8_t reserved_0x11; + register8_t reserved_0x12; + register8_t reserved_0x13; + register8_t reserved_0x14; + register8_t reserved_0x15; + register8_t reserved_0x16; + register8_t reserved_0x17; + register8_t reserved_0x18; + register8_t reserved_0x19; + register8_t reserved_0x1A; + register8_t reserved_0x1B; + register8_t reserved_0x1C; + register8_t reserved_0x1D; + register8_t reserved_0x1E; + register8_t reserved_0x1F; + _WORDREGISTER(CNT); /* Count */ + register8_t reserved_0x22; + register8_t reserved_0x23; + register8_t reserved_0x24; + register8_t reserved_0x25; + _WORDREGISTER(PER); /* Period */ + _WORDREGISTER(CCA); /* Compare or Capture A */ + _WORDREGISTER(CCB); /* Compare or Capture B */ + _WORDREGISTER(CCC); /* Compare or Capture C */ + _WORDREGISTER(CCD); /* Compare or Capture D */ + register8_t reserved_0x30; + register8_t reserved_0x31; + register8_t reserved_0x32; + register8_t reserved_0x33; + register8_t reserved_0x34; + register8_t reserved_0x35; + _WORDREGISTER(PERBUF); /* Period Buffer */ + _WORDREGISTER(CCABUF); /* Compare Or Capture A Buffer */ + _WORDREGISTER(CCBBUF); /* Compare Or Capture B Buffer */ + _WORDREGISTER(CCCBUF); /* Compare Or Capture C Buffer */ + _WORDREGISTER(CCDBUF); /* Compare Or Capture D Buffer */ +} TC0_t; + + +/* 16-bit Timer/Counter 1 */ +typedef struct TC1_struct +{ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t CTRLC; /* Control register C */ + register8_t CTRLD; /* Control Register D */ + register8_t CTRLE; /* Control Register E */ + register8_t reserved_0x05; + register8_t INTCTRLA; /* Interrupt Control Register A */ + register8_t INTCTRLB; /* Interrupt Control Register B */ + register8_t CTRLFCLR; /* Control Register F Clear */ + register8_t CTRLFSET; /* Control Register F Set */ + register8_t CTRLGCLR; /* Control Register G Clear */ + register8_t CTRLGSET; /* Control Register G Set */ + register8_t INTFLAGS; /* Interrupt Flag Register */ + register8_t reserved_0x0D; + register8_t reserved_0x0E; + register8_t TEMP; /* Temporary Register For 16-bit Access */ + register8_t reserved_0x10; + register8_t reserved_0x11; + register8_t reserved_0x12; + register8_t reserved_0x13; + register8_t reserved_0x14; + register8_t reserved_0x15; + register8_t reserved_0x16; + register8_t reserved_0x17; + register8_t reserved_0x18; + register8_t reserved_0x19; + register8_t reserved_0x1A; + register8_t reserved_0x1B; + register8_t reserved_0x1C; + register8_t reserved_0x1D; + register8_t reserved_0x1E; + register8_t reserved_0x1F; + _WORDREGISTER(CNT); /* Count */ + register8_t reserved_0x22; + register8_t reserved_0x23; + register8_t reserved_0x24; + register8_t reserved_0x25; + _WORDREGISTER(PER); /* Period */ + _WORDREGISTER(CCA); /* Compare or Capture A */ + _WORDREGISTER(CCB); /* Compare or Capture B */ + register8_t reserved_0x2C; + register8_t reserved_0x2D; + register8_t reserved_0x2E; + register8_t reserved_0x2F; + register8_t reserved_0x30; + register8_t reserved_0x31; + register8_t reserved_0x32; + register8_t reserved_0x33; + register8_t reserved_0x34; + register8_t reserved_0x35; + _WORDREGISTER(PERBUF); /* Period Buffer */ + _WORDREGISTER(CCABUF); /* Compare Or Capture A Buffer */ + _WORDREGISTER(CCBBUF); /* Compare Or Capture B Buffer */ +} TC1_t; + +/* Clock Selection */ +typedef enum TC_CLKSEL_enum +{ + TC_CLKSEL_OFF_gc = (0x00<<0), /* Timer Off */ + TC_CLKSEL_DIV1_gc = (0x01<<0), /* System Clock */ + TC_CLKSEL_DIV2_gc = (0x02<<0), /* System Clock / 2 */ + TC_CLKSEL_DIV4_gc = (0x03<<0), /* System Clock / 4 */ + TC_CLKSEL_DIV8_gc = (0x04<<0), /* System Clock / 8 */ + TC_CLKSEL_DIV64_gc = (0x05<<0), /* System Clock / 64 */ + TC_CLKSEL_DIV256_gc = (0x06<<0), /* System Clock / 256 */ + TC_CLKSEL_DIV1024_gc = (0x07<<0), /* System Clock / 1024 */ + TC_CLKSEL_EVCH0_gc = (0x08<<0), /* Event Channel 0 */ + TC_CLKSEL_EVCH1_gc = (0x09<<0), /* Event Channel 1 */ + TC_CLKSEL_EVCH2_gc = (0x0A<<0), /* Event Channel 2 */ + TC_CLKSEL_EVCH3_gc = (0x0B<<0), /* Event Channel 3 */ + TC_CLKSEL_EVCH4_gc = (0x0C<<0), /* Event Channel 4 */ + TC_CLKSEL_EVCH5_gc = (0x0D<<0), /* Event Channel 5 */ + TC_CLKSEL_EVCH6_gc = (0x0E<<0), /* Event Channel 6 */ + TC_CLKSEL_EVCH7_gc = (0x0F<<0), /* Event Channel 7 */ +} TC_CLKSEL_t; + +/* Waveform Generation Mode */ +typedef enum TC_WGMODE_enum +{ + TC_WGMODE_NORMAL_gc = (0x00<<0), /* Normal Mode */ + TC_WGMODE_FRQ_gc = (0x01<<0), /* Frequency Generation Mode */ + TC_WGMODE_SINGLESLOPE_gc = (0x03<<0), /* Single Slope */ + TC_WGMODE_SS_gc = (0x03<<0), /* Single Slope */ + TC_WGMODE_DSTOP_gc = (0x05<<0), /* Dual Slope, Update on TOP */ + TC_WGMODE_DS_T_gc = (0x05<<0), /* Dual Slope, Update on TOP */ + TC_WGMODE_DSBOTH_gc = (0x06<<0), /* Dual Slope, Update on both TOP and BOTTOM */ + TC_WGMODE_DS_TB_gc = (0x06<<0), /* Dual Slope, Update on both TOP and BOTTOM */ + TC_WGMODE_DSBOTTOM_gc = (0x07<<0), /* Dual Slope, Update on BOTTOM */ + TC_WGMODE_DS_B_gc = (0x07<<0), /* Dual Slope, Update on BOTTOM */ +} TC_WGMODE_t; + +/* Byte Mode */ +typedef enum TC_BYTEM_enum +{ + TC_BYTEM_NORMAL_gc = (0x00<<0), /* 16-bit mode */ + TC_BYTEM_BYTEMODE_gc = (0x01<<0), /* Timer/Counter operating in byte mode only */ + TC_BYTEM_SPLITMODE_gc = (0x02<<0), /* Timer/Counter split into two 8-bit Counters (TC2) */ +} TC_BYTEM_t; + +/* Event Action */ +typedef enum TC_EVACT_enum +{ + TC_EVACT_OFF_gc = (0x00<<5), /* No Event Action */ + TC_EVACT_CAPT_gc = (0x01<<5), /* Input Capture */ + TC_EVACT_UPDOWN_gc = (0x02<<5), /* Externally Controlled Up/Down Count */ + TC_EVACT_QDEC_gc = (0x03<<5), /* Quadrature Decode */ + TC_EVACT_RESTART_gc = (0x04<<5), /* Restart */ + TC_EVACT_FRQ_gc = (0x05<<5), /* Frequency Capture */ + TC_EVACT_PW_gc = (0x06<<5), /* Pulse-width Capture */ +} TC_EVACT_t; + +/* Event Selection */ +typedef enum TC_EVSEL_enum +{ + TC_EVSEL_OFF_gc = (0x00<<0), /* No Event Source */ + TC_EVSEL_CH0_gc = (0x08<<0), /* Event Channel 0 */ + TC_EVSEL_CH1_gc = (0x09<<0), /* Event Channel 1 */ + TC_EVSEL_CH2_gc = (0x0A<<0), /* Event Channel 2 */ + TC_EVSEL_CH3_gc = (0x0B<<0), /* Event Channel 3 */ + TC_EVSEL_CH4_gc = (0x0C<<0), /* Event Channel 4 */ + TC_EVSEL_CH5_gc = (0x0D<<0), /* Event Channel 5 */ + TC_EVSEL_CH6_gc = (0x0E<<0), /* Event Channel 6 */ + TC_EVSEL_CH7_gc = (0x0F<<0), /* Event Channel 7 */ +} TC_EVSEL_t; + +/* Error Interrupt Level */ +typedef enum TC_ERRINTLVL_enum +{ + TC_ERRINTLVL_OFF_gc = (0x00<<2), /* Interrupt Disabled */ + TC_ERRINTLVL_LO_gc = (0x01<<2), /* Low Level */ + TC_ERRINTLVL_MED_gc = (0x02<<2), /* Medium Level */ + TC_ERRINTLVL_HI_gc = (0x03<<2), /* High Level */ +} TC_ERRINTLVL_t; + +/* Overflow Interrupt Level */ +typedef enum TC_OVFINTLVL_enum +{ + TC_OVFINTLVL_OFF_gc = (0x00<<0), /* Interrupt Disabled */ + TC_OVFINTLVL_LO_gc = (0x01<<0), /* Low Level */ + TC_OVFINTLVL_MED_gc = (0x02<<0), /* Medium Level */ + TC_OVFINTLVL_HI_gc = (0x03<<0), /* High Level */ +} TC_OVFINTLVL_t; + +/* Compare or Capture D Interrupt Level */ +typedef enum TC_CCDINTLVL_enum +{ + TC_CCDINTLVL_OFF_gc = (0x00<<6), /* Interrupt Disabled */ + TC_CCDINTLVL_LO_gc = (0x01<<6), /* Low Level */ + TC_CCDINTLVL_MED_gc = (0x02<<6), /* Medium Level */ + TC_CCDINTLVL_HI_gc = (0x03<<6), /* High Level */ +} TC_CCDINTLVL_t; + +/* Compare or Capture C Interrupt Level */ +typedef enum TC_CCCINTLVL_enum +{ + TC_CCCINTLVL_OFF_gc = (0x00<<4), /* Interrupt Disabled */ + TC_CCCINTLVL_LO_gc = (0x01<<4), /* Low Level */ + TC_CCCINTLVL_MED_gc = (0x02<<4), /* Medium Level */ + TC_CCCINTLVL_HI_gc = (0x03<<4), /* High Level */ +} TC_CCCINTLVL_t; + +/* Compare or Capture B Interrupt Level */ +typedef enum TC_CCBINTLVL_enum +{ + TC_CCBINTLVL_OFF_gc = (0x00<<2), /* Interrupt Disabled */ + TC_CCBINTLVL_LO_gc = (0x01<<2), /* Low Level */ + TC_CCBINTLVL_MED_gc = (0x02<<2), /* Medium Level */ + TC_CCBINTLVL_HI_gc = (0x03<<2), /* High Level */ +} TC_CCBINTLVL_t; + +/* Compare or Capture A Interrupt Level */ +typedef enum TC_CCAINTLVL_enum +{ + TC_CCAINTLVL_OFF_gc = (0x00<<0), /* Interrupt Disabled */ + TC_CCAINTLVL_LO_gc = (0x01<<0), /* Low Level */ + TC_CCAINTLVL_MED_gc = (0x02<<0), /* Medium Level */ + TC_CCAINTLVL_HI_gc = (0x03<<0), /* High Level */ +} TC_CCAINTLVL_t; + +/* Timer/Counter Command */ +typedef enum TC_CMD_enum +{ + TC_CMD_NONE_gc = (0x00<<2), /* No Command */ + TC_CMD_UPDATE_gc = (0x01<<2), /* Force Update */ + TC_CMD_RESTART_gc = (0x02<<2), /* Force Restart */ + TC_CMD_RESET_gc = (0x03<<2), /* Force Hard Reset */ +} TC_CMD_t; + + +/* +-------------------------------------------------------------------------- +TC2 - 16-bit Timer/Counter type 2 +-------------------------------------------------------------------------- +*/ + +/* 16-bit Timer/Counter type 2 */ +typedef struct TC2_struct +{ + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t CTRLC; /* Control register C */ + register8_t reserved_0x03; + register8_t CTRLE; /* Control Register E */ + register8_t reserved_0x05; + register8_t INTCTRLA; /* Interrupt Control Register A */ + register8_t INTCTRLB; /* Interrupt Control Register B */ + register8_t reserved_0x08; + register8_t CTRLF; /* Control Register F */ + register8_t reserved_0x0A; + register8_t reserved_0x0B; + register8_t INTFLAGS; /* Interrupt Flag Register */ + register8_t reserved_0x0D; + register8_t reserved_0x0E; + register8_t reserved_0x0F; + register8_t reserved_0x10; + register8_t reserved_0x11; + register8_t reserved_0x12; + register8_t reserved_0x13; + register8_t reserved_0x14; + register8_t reserved_0x15; + register8_t reserved_0x16; + register8_t reserved_0x17; + register8_t reserved_0x18; + register8_t reserved_0x19; + register8_t reserved_0x1A; + register8_t reserved_0x1B; + register8_t reserved_0x1C; + register8_t reserved_0x1D; + register8_t reserved_0x1E; + register8_t reserved_0x1F; + register8_t LCNT; /* Low Byte Count */ + register8_t HCNT; /* High Byte Count */ + register8_t reserved_0x22; + register8_t reserved_0x23; + register8_t reserved_0x24; + register8_t reserved_0x25; + register8_t LPER; /* Low Byte Period */ + register8_t HPER; /* High Byte Period */ + register8_t LCMPA; /* Low Byte Compare A */ + register8_t HCMPA; /* High Byte Compare A */ + register8_t LCMPB; /* Low Byte Compare B */ + register8_t HCMPB; /* High Byte Compare B */ + register8_t LCMPC; /* Low Byte Compare C */ + register8_t HCMPC; /* High Byte Compare C */ + register8_t LCMPD; /* Low Byte Compare D */ + register8_t HCMPD; /* High Byte Compare D */ +} TC2_t; + +/* Clock Selection */ +typedef enum TC2_CLKSEL_enum +{ + TC2_CLKSEL_OFF_gc = (0x00<<0), /* Timer Off */ + TC2_CLKSEL_DIV1_gc = (0x01<<0), /* System Clock */ + TC2_CLKSEL_DIV2_gc = (0x02<<0), /* System Clock / 2 */ + TC2_CLKSEL_DIV4_gc = (0x03<<0), /* System Clock / 4 */ + TC2_CLKSEL_DIV8_gc = (0x04<<0), /* System Clock / 8 */ + TC2_CLKSEL_DIV64_gc = (0x05<<0), /* System Clock / 64 */ + TC2_CLKSEL_DIV256_gc = (0x06<<0), /* System Clock / 256 */ + TC2_CLKSEL_DIV1024_gc = (0x07<<0), /* System Clock / 1024 */ + TC2_CLKSEL_EVCH0_gc = (0x08<<0), /* Event Channel 0 */ + TC2_CLKSEL_EVCH1_gc = (0x09<<0), /* Event Channel 1 */ + TC2_CLKSEL_EVCH2_gc = (0x0A<<0), /* Event Channel 2 */ + TC2_CLKSEL_EVCH3_gc = (0x0B<<0), /* Event Channel 3 */ + TC2_CLKSEL_EVCH4_gc = (0x0C<<0), /* Event Channel 4 */ + TC2_CLKSEL_EVCH5_gc = (0x0D<<0), /* Event Channel 5 */ + TC2_CLKSEL_EVCH6_gc = (0x0E<<0), /* Event Channel 6 */ + TC2_CLKSEL_EVCH7_gc = (0x0F<<0), /* Event Channel 7 */ +} TC2_CLKSEL_t; + +/* Byte Mode */ +typedef enum TC2_BYTEM_enum +{ + TC2_BYTEM_NORMAL_gc = (0x00<<0), /* 16-bit mode */ + TC2_BYTEM_BYTEMODE_gc = (0x01<<0), /* Timer/Counter operating in byte mode only (TC2) */ + TC2_BYTEM_SPLITMODE_gc = (0x02<<0), /* Timer/Counter split into two 8-bit Counters */ +} TC2_BYTEM_t; + +/* High Byte Underflow Interrupt Level */ +typedef enum TC2_HUNFINTLVL_enum +{ + TC2_HUNFINTLVL_OFF_gc = (0x00<<2), /* Interrupt Disabled */ + TC2_HUNFINTLVL_LO_gc = (0x01<<2), /* Low Level */ + TC2_HUNFINTLVL_MED_gc = (0x02<<2), /* Medium Level */ + TC2_HUNFINTLVL_HI_gc = (0x03<<2), /* High Level */ +} TC2_HUNFINTLVL_t; + +/* Low Byte Underflow Interrupt Level */ +typedef enum TC2_LUNFINTLVL_enum +{ + TC2_LUNFINTLVL_OFF_gc = (0x00<<0), /* Interrupt Disabled */ + TC2_LUNFINTLVL_LO_gc = (0x01<<0), /* Low Level */ + TC2_LUNFINTLVL_MED_gc = (0x02<<0), /* Medium Level */ + TC2_LUNFINTLVL_HI_gc = (0x03<<0), /* High Level */ +} TC2_LUNFINTLVL_t; + +/* Low Byte Compare D Interrupt Level */ +typedef enum TC2_LCMPDINTLVL_enum +{ + TC2_LCMPDINTLVL_OFF_gc = (0x00<<6), /* Interrupt Disabled */ + TC2_LCMPDINTLVL_LO_gc = (0x01<<6), /* Low Level */ + TC2_LCMPDINTLVL_MED_gc = (0x02<<6), /* Medium Level */ + TC2_LCMPDINTLVL_HI_gc = (0x03<<6), /* High Level */ +} TC2_LCMPDINTLVL_t; + +/* Low Byte Compare C Interrupt Level */ +typedef enum TC2_LCMPCINTLVL_enum +{ + TC2_LCMPCINTLVL_OFF_gc = (0x00<<4), /* Interrupt Disabled */ + TC2_LCMPCINTLVL_LO_gc = (0x01<<4), /* Low Level */ + TC2_LCMPCINTLVL_MED_gc = (0x02<<4), /* Medium Level */ + TC2_LCMPCINTLVL_HI_gc = (0x03<<4), /* High Level */ +} TC2_LCMPCINTLVL_t; + +/* Low Byte Compare B Interrupt Level */ +typedef enum TC2_LCMPBINTLVL_enum +{ + TC2_LCMPBINTLVL_OFF_gc = (0x00<<2), /* Interrupt Disabled */ + TC2_LCMPBINTLVL_LO_gc = (0x01<<2), /* Low Level */ + TC2_LCMPBINTLVL_MED_gc = (0x02<<2), /* Medium Level */ + TC2_LCMPBINTLVL_HI_gc = (0x03<<2), /* High Level */ +} TC2_LCMPBINTLVL_t; + +/* Low Byte Compare A Interrupt Level */ +typedef enum TC2_LCMPAINTLVL_enum +{ + TC2_LCMPAINTLVL_OFF_gc = (0x00<<0), /* Interrupt Disabled */ + TC2_LCMPAINTLVL_LO_gc = (0x01<<0), /* Low Level */ + TC2_LCMPAINTLVL_MED_gc = (0x02<<0), /* Medium Level */ + TC2_LCMPAINTLVL_HI_gc = (0x03<<0), /* High Level */ +} TC2_LCMPAINTLVL_t; + +/* Timer/Counter Command */ +typedef enum TC2_CMD_enum +{ + TC2_CMD_NONE_gc = (0x00<<2), /* No Command */ + TC2_CMD_RESTART_gc = (0x02<<2), /* Force Restart */ + TC2_CMD_RESET_gc = (0x03<<2), /* Force Hard Reset */ +} TC2_CMD_t; + +/* Timer/Counter Command */ +typedef enum TC2_CMDEN_enum +{ + TC2_CMDEN_LOW_gc = (0x01<<0), /* Low Byte Timer/Counter */ + TC2_CMDEN_HIGH_gc = (0x02<<0), /* High Byte Timer/Counter */ + TC2_CMDEN_BOTH_gc = (0x03<<0), /* Both Low Byte and High Byte Timer/Counters */ +} TC2_CMDEN_t; + + +/* +-------------------------------------------------------------------------- +AWEX - Timer/Counter Advanced Waveform Extension +-------------------------------------------------------------------------- +*/ + +/* Advanced Waveform Extension */ +typedef struct AWEX_struct +{ + register8_t CTRL; /* Control Register */ + register8_t reserved_0x01; + register8_t FDEMASK; /* Fault Detection Event Mask */ + register8_t FDCTRL; /* Fault Detection Control Register */ + register8_t STATUS; /* Status Register */ + register8_t STATUSSET; /* Status Set Register */ + register8_t DTBOTH; /* Dead Time Both Sides */ + register8_t DTBOTHBUF; /* Dead Time Both Sides Buffer */ + register8_t DTLS; /* Dead Time Low Side */ + register8_t DTHS; /* Dead Time High Side */ + register8_t DTLSBUF; /* Dead Time Low Side Buffer */ + register8_t DTHSBUF; /* Dead Time High Side Buffer */ + register8_t OUTOVEN; /* Output Override Enable */ +} AWEX_t; + +/* Fault Detect Action */ +typedef enum AWEX_FDACT_enum +{ + AWEX_FDACT_NONE_gc = (0x00<<0), /* No Fault Protection */ + AWEX_FDACT_CLEAROE_gc = (0x01<<0), /* Clear Output Enable Bits */ + AWEX_FDACT_CLEARDIR_gc = (0x03<<0), /* Clear I/O Port Direction Bits */ +} AWEX_FDACT_t; + + +/* +-------------------------------------------------------------------------- +HIRES - Timer/Counter High-Resolution Extension +-------------------------------------------------------------------------- +*/ + +/* High-Resolution Extension */ +typedef struct HIRES_struct +{ + register8_t CTRLA; /* Control Register */ +} HIRES_t; + +/* High Resolution Enable */ +typedef enum HIRES_HREN_enum +{ + HIRES_HREN_NONE_gc = (0x00<<0), /* No Fault Protection */ + HIRES_HREN_TC0_gc = (0x01<<0), /* Enable High Resolution on Timer/Counter 0 */ + HIRES_HREN_TC1_gc = (0x02<<0), /* Enable High Resolution on Timer/Counter 1 */ + HIRES_HREN_BOTH_gc = (0x03<<0), /* Enable High Resolution both Timer/Counters */ +} HIRES_HREN_t; + + +/* +-------------------------------------------------------------------------- +USART - Universal Asynchronous Receiver-Transmitter +-------------------------------------------------------------------------- +*/ + +/* Universal Synchronous/Asynchronous Receiver/Transmitter */ +typedef struct USART_struct +{ + register8_t DATA; /* Data Register */ + register8_t STATUS; /* Status Register */ + register8_t reserved_0x02; + register8_t CTRLA; /* Control Register A */ + register8_t CTRLB; /* Control Register B */ + register8_t CTRLC; /* Control Register C */ + register8_t BAUDCTRLA; /* Baud Rate Control Register A */ + register8_t BAUDCTRLB; /* Baud Rate Control Register B */ +} USART_t; + +/* Receive Complete Interrupt level */ +typedef enum USART_RXCINTLVL_enum +{ + USART_RXCINTLVL_OFF_gc = (0x00<<4), /* Interrupt Disabled */ + USART_RXCINTLVL_LO_gc = (0x01<<4), /* Low Level */ + USART_RXCINTLVL_MED_gc = (0x02<<4), /* Medium Level */ + USART_RXCINTLVL_HI_gc = (0x03<<4), /* High Level */ +} USART_RXCINTLVL_t; + +/* Transmit Complete Interrupt level */ +typedef enum USART_TXCINTLVL_enum +{ + USART_TXCINTLVL_OFF_gc = (0x00<<2), /* Interrupt Disabled */ + USART_TXCINTLVL_LO_gc = (0x01<<2), /* Low Level */ + USART_TXCINTLVL_MED_gc = (0x02<<2), /* Medium Level */ + USART_TXCINTLVL_HI_gc = (0x03<<2), /* High Level */ +} USART_TXCINTLVL_t; + +/* Data Register Empty Interrupt level */ +typedef enum USART_DREINTLVL_enum +{ + USART_DREINTLVL_OFF_gc = (0x00<<0), /* Interrupt Disabled */ + USART_DREINTLVL_LO_gc = (0x01<<0), /* Low Level */ + USART_DREINTLVL_MED_gc = (0x02<<0), /* Medium Level */ + USART_DREINTLVL_HI_gc = (0x03<<0), /* High Level */ +} USART_DREINTLVL_t; + +/* Character Size */ +typedef enum USART_CHSIZE_enum +{ + USART_CHSIZE_5BIT_gc = (0x00<<0), /* Character size: 5 bit */ + USART_CHSIZE_6BIT_gc = (0x01<<0), /* Character size: 6 bit */ + USART_CHSIZE_7BIT_gc = (0x02<<0), /* Character size: 7 bit */ + USART_CHSIZE_8BIT_gc = (0x03<<0), /* Character size: 8 bit */ + USART_CHSIZE_9BIT_gc = (0x07<<0), /* Character size: 9 bit */ +} USART_CHSIZE_t; + +/* Communication Mode */ +typedef enum USART_CMODE_enum +{ + USART_CMODE_ASYNCHRONOUS_gc = (0x00<<6), /* Asynchronous Mode */ + USART_CMODE_SYNCHRONOUS_gc = (0x01<<6), /* Synchronous Mode */ + USART_CMODE_IRDA_gc = (0x02<<6), /* IrDA Mode */ + USART_CMODE_MSPI_gc = (0x03<<6), /* Master SPI Mode */ +} USART_CMODE_t; + +/* Parity Mode */ +typedef enum USART_PMODE_enum +{ + USART_PMODE_DISABLED_gc = (0x00<<4), /* No Parity */ + USART_PMODE_EVEN_gc = (0x02<<4), /* Even Parity */ + USART_PMODE_ODD_gc = (0x03<<4), /* Odd Parity */ +} USART_PMODE_t; + + +/* +-------------------------------------------------------------------------- +SPI - Serial Peripheral Interface +-------------------------------------------------------------------------- +*/ + +/* Serial Peripheral Interface */ +typedef struct SPI_struct +{ + register8_t CTRL; /* Control Register */ + register8_t INTCTRL; /* Interrupt Control Register */ + register8_t STATUS; /* Status Register */ + register8_t DATA; /* Data Register */ +} SPI_t; + +/* SPI Mode */ +typedef enum SPI_MODE_enum +{ + SPI_MODE_0_gc = (0x00<<2), /* SPI Mode 0 */ + SPI_MODE_1_gc = (0x01<<2), /* SPI Mode 1 */ + SPI_MODE_2_gc = (0x02<<2), /* SPI Mode 2 */ + SPI_MODE_3_gc = (0x03<<2), /* SPI Mode 3 */ +} SPI_MODE_t; + +/* Prescaler setting */ +typedef enum SPI_PRESCALER_enum +{ + SPI_PRESCALER_DIV4_gc = (0x00<<0), /* System Clock / 4 */ + SPI_PRESCALER_DIV16_gc = (0x01<<0), /* System Clock / 16 */ + SPI_PRESCALER_DIV64_gc = (0x02<<0), /* System Clock / 64 */ + SPI_PRESCALER_DIV128_gc = (0x03<<0), /* System Clock / 128 */ +} SPI_PRESCALER_t; + +/* Interrupt level */ +typedef enum SPI_INTLVL_enum +{ + SPI_INTLVL_OFF_gc = (0x00<<0), /* Interrupt Disabled */ + SPI_INTLVL_LO_gc = (0x01<<0), /* Low Level */ + SPI_INTLVL_MED_gc = (0x02<<0), /* Medium Level */ + SPI_INTLVL_HI_gc = (0x03<<0), /* High Level */ +} SPI_INTLVL_t; + + +/* +-------------------------------------------------------------------------- +IRCOM - IR Communication Module +-------------------------------------------------------------------------- +*/ + +/* IR Communication Module */ +typedef struct IRCOM_struct +{ + register8_t CTRL; /* Control Register */ + register8_t TXPLCTRL; /* IrDA Transmitter Pulse Length Control Register */ + register8_t RXPLCTRL; /* IrDA Receiver Pulse Length Control Register */ +} IRCOM_t; + +/* Event channel selection */ +typedef enum IRDA_EVSEL_enum +{ + IRDA_EVSEL_OFF_gc = (0x00<<0), /* No Event Source */ + IRDA_EVSEL_0_gc = (0x08<<0), /* Event Channel 0 */ + IRDA_EVSEL_1_gc = (0x09<<0), /* Event Channel 1 */ + IRDA_EVSEL_2_gc = (0x0A<<0), /* Event Channel 2 */ + IRDA_EVSEL_3_gc = (0x0B<<0), /* Event Channel 3 */ + IRDA_EVSEL_4_gc = (0x0C<<0), /* Event Channel 4 */ + IRDA_EVSEL_5_gc = (0x0D<<0), /* Event Channel 5 */ + IRDA_EVSEL_6_gc = (0x0E<<0), /* Event Channel 6 */ + IRDA_EVSEL_7_gc = (0x0F<<0), /* Event Channel 7 */ +} IRDA_EVSEL_t; + + +/* +-------------------------------------------------------------------------- +FUSE - Fuses and Lockbits +-------------------------------------------------------------------------- +*/ + +/* Fuses */ +typedef struct NVM_FUSES_struct +{ + register8_t FUSEBYTE0; /* JTAG User ID */ + register8_t FUSEBYTE1; /* Watchdog Configuration */ + register8_t FUSEBYTE2; /* Reset Configuration */ + register8_t reserved_0x03; + register8_t FUSEBYTE4; /* Start-up Configuration */ + register8_t FUSEBYTE5; /* EESAVE and BOD Level */ +} NVM_FUSES_t; + +/* Boot Loader Section Reset Vector */ +typedef enum BOOTRST_enum +{ + BOOTRST_BOOTLDR_gc = (0x00<<6), /* Boot Loader Reset */ + BOOTRST_APPLICATION_gc = (0x01<<6), /* Application Reset */ +} BOOTRST_t; + +/* Timer Oscillator pin location */ +typedef enum TOSCSEL_enum +{ + TOSCSEL_ALTERNATE_gc = (0x00<<5), /* TOSC1 / TOSC2 on separate pins */ + TOSCSEL_XTAL_gc = (0x01<<5), /* TOSC1 / TOSC2 shared with XTAL1 / XTAL2 */ +} TOSCSEL_t; + +/* BOD operation */ +typedef enum BOD_enum +{ + BOD_SAMPLED_gc = (0x01<<0), /* BOD enabled in sampled mode */ + BOD_CONTINUOUS_gc = (0x02<<0), /* BOD enabled continuously */ + BOD_DISABLED_gc = (0x03<<0), /* BOD Disabled */ +} BOD_t; + +/* BOD operation */ +typedef enum BODACT_enum +{ + BODACT_SAMPLED_gc = (0x01<<4), /* BOD enabled in sampled mode */ + BODACT_CONTINUOUS_gc = (0x02<<4), /* BOD enabled continuously */ + BODACT_DISABLED_gc = (0x03<<4), /* BOD Disabled */ +} BODACT_t; + +/* Watchdog (Window) Timeout Period */ +typedef enum WD_enum +{ + WD_8CLK_gc = (0x00<<4), /* 8 cycles (8ms @ 3.3V) */ + WD_16CLK_gc = (0x01<<4), /* 16 cycles (16ms @ 3.3V) */ + WD_32CLK_gc = (0x02<<4), /* 32 cycles (32ms @ 3.3V) */ + WD_64CLK_gc = (0x03<<4), /* 64 cycles (64ms @ 3.3V) */ + WD_128CLK_gc = (0x04<<4), /* 128 cycles (0.125s @ 3.3V) */ + WD_256CLK_gc = (0x05<<4), /* 256 cycles (0.25s @ 3.3V) */ + WD_512CLK_gc = (0x06<<4), /* 512 cycles (0.5s @ 3.3V) */ + WD_1KCLK_gc = (0x07<<4), /* 1K cycles (1s @ 3.3V) */ + WD_2KCLK_gc = (0x08<<4), /* 2K cycles (2s @ 3.3V) */ + WD_4KCLK_gc = (0x09<<4), /* 4K cycles (4s @ 3.3V) */ + WD_8KCLK_gc = (0x0A<<4), /* 8K cycles (8s @ 3.3V) */ +} WD_t; + +/* Watchdog (Window) Timeout Period */ +typedef enum WDP_enum +{ + WDP_8CLK_gc = (0x00<<0), /* 8 cycles (8ms @ 3.3V) */ + WDP_16CLK_gc = (0x01<<0), /* 16 cycles (16ms @ 3.3V) */ + WDP_32CLK_gc = (0x02<<0), /* 32 cycles (32ms @ 3.3V) */ + WDP_64CLK_gc = (0x03<<0), /* 64 cycles (64ms @ 3.3V) */ + WDP_128CLK_gc = (0x04<<0), /* 128 cycles (0.125s @ 3.3V) */ + WDP_256CLK_gc = (0x05<<0), /* 256 cycles (0.25s @ 3.3V) */ + WDP_512CLK_gc = (0x06<<0), /* 512 cycles (0.5s @ 3.3V) */ + WDP_1KCLK_gc = (0x07<<0), /* 1K cycles (1s @ 3.3V) */ + WDP_2KCLK_gc = (0x08<<0), /* 2K cycles (2s @ 3.3V) */ + WDP_4KCLK_gc = (0x09<<0), /* 4K cycles (4s @ 3.3V) */ + WDP_8KCLK_gc = (0x0A<<0), /* 8K cycles (8s @ 3.3V) */ +} WDP_t; + +/* Start-up Time */ +typedef enum SUT_enum +{ + SUT_0MS_gc = (0x03<<2), /* 0 ms */ + SUT_4MS_gc = (0x01<<2), /* 4 ms */ + SUT_64MS_gc = (0x00<<2), /* 64 ms */ +} SUT_t; + +/* Brownout Detection Voltage Level */ +typedef enum BODLVL_enum +{ + BODLVL_1V6_gc = (0x07<<0), /* 1.6 V */ + BODLVL_1V8_gc = (0x06<<0), /* 1.8 V */ + BODLVL_2V0_gc = (0x05<<0), /* 2.0 V */ + BODLVL_2V2_gc = (0x04<<0), /* 2.2 V */ + BODLVL_2V4_gc = (0x03<<0), /* 2.4 V */ + BODLVL_2V6_gc = (0x02<<0), /* 2.6 V */ + BODLVL_2V8_gc = (0x01<<0), /* 2.8 V */ + BODLVL_3V0_gc = (0x00<<0), /* 3.0 V */ +} BODLVL_t; + + +/* +-------------------------------------------------------------------------- +LOCKBIT - Fuses and Lockbits +-------------------------------------------------------------------------- +*/ + +/* Lock Bits */ +typedef struct NVM_LOCKBITS_struct +{ + register8_t LOCK_BITS; /* Lock Bits (Changed from LOCKBITS to avoid avr-libc collision) */ +} NVM_LOCKBITS_t; + +/* Boot lock bits - boot setcion */ +typedef enum FUSE_BLBB_enum +{ + FUSE_BLBB_RWLOCK_gc = (0x00<<6), /* Read and write not allowed */ + FUSE_BLBB_RLOCK_gc = (0x01<<6), /* Read not allowed */ + FUSE_BLBB_WLOCK_gc = (0x02<<6), /* Write not allowed */ + FUSE_BLBB_NOLOCK_gc = (0x03<<6), /* No locks */ +} FUSE_BLBB_t; + +/* Boot lock bits - application section */ +typedef enum FUSE_BLBA_enum +{ + FUSE_BLBA_RWLOCK_gc = (0x00<<4), /* Read and write not allowed */ + FUSE_BLBA_RLOCK_gc = (0x01<<4), /* Read not allowed */ + FUSE_BLBA_WLOCK_gc = (0x02<<4), /* Write not allowed */ + FUSE_BLBA_NOLOCK_gc = (0x03<<4), /* No locks */ +} FUSE_BLBA_t; + +/* Boot lock bits - application table section */ +typedef enum FUSE_BLBAT_enum +{ + FUSE_BLBAT_RWLOCK_gc = (0x00<<2), /* Read and write not allowed */ + FUSE_BLBAT_RLOCK_gc = (0x01<<2), /* Read not allowed */ + FUSE_BLBAT_WLOCK_gc = (0x02<<2), /* Write not allowed */ + FUSE_BLBAT_NOLOCK_gc = (0x03<<2), /* No locks */ +} FUSE_BLBAT_t; + +/* Lock bits */ +typedef enum FUSE_LB_enum +{ + FUSE_LB_RWLOCK_gc = (0x00<<0), /* Read and write not allowed */ + FUSE_LB_WLOCK_gc = (0x02<<0), /* Write not allowed */ + FUSE_LB_NOLOCK_gc = (0x03<<0), /* No locks */ +} FUSE_LB_t; + + +/* +-------------------------------------------------------------------------- +SIGROW - Signature Row +-------------------------------------------------------------------------- +*/ + +/* Production Signatures */ +typedef struct NVM_PROD_SIGNATURES_struct +{ + register8_t RCOSC2M; /* RCOSC 2 MHz Calibration Value B */ + register8_t RCOSC2MA; /* RCOSC 2 MHz Calibration Value A */ + register8_t RCOSC32K; /* RCOSC 32.768 kHz Calibration Value */ + register8_t RCOSC32M; /* RCOSC 32 MHz Calibration Value B */ + register8_t RCOSC32MA; /* RCOSC 32 MHz Calibration Value A */ + register8_t reserved_0x05; + register8_t reserved_0x06; + register8_t reserved_0x07; + register8_t LOTNUM0; /* Lot Number Byte 0, ASCII */ + register8_t LOTNUM1; /* Lot Number Byte 1, ASCII */ + register8_t LOTNUM2; /* Lot Number Byte 2, ASCII */ + register8_t LOTNUM3; /* Lot Number Byte 3, ASCII */ + register8_t LOTNUM4; /* Lot Number Byte 4, ASCII */ + register8_t LOTNUM5; /* Lot Number Byte 5, ASCII */ + register8_t reserved_0x0E; + register8_t reserved_0x0F; + register8_t WAFNUM; /* Wafer Number */ + register8_t reserved_0x11; + register8_t COORDX0; /* Wafer Coordinate X Byte 0 */ + register8_t COORDX1; /* Wafer Coordinate X Byte 1 */ + register8_t COORDY0; /* Wafer Coordinate Y Byte 0 */ + register8_t COORDY1; /* Wafer Coordinate Y Byte 1 */ + register8_t reserved_0x16; + register8_t reserved_0x17; + register8_t reserved_0x18; + register8_t reserved_0x19; + register8_t USBCAL0; /* USB Calibration Byte 0 */ + register8_t USBCAL1; /* USB Calibration Byte 1 */ + register8_t USBRCOSC; /* USB RCOSC Calibration Value B */ + register8_t USBRCOSCA; /* USB RCOSC Calibration Value A */ + register8_t reserved_0x1E; + register8_t reserved_0x1F; + register8_t ADCACAL0; /* ADCA Calibration Byte 0 */ + register8_t ADCACAL1; /* ADCA Calibration Byte 1 */ + register8_t reserved_0x22; + register8_t reserved_0x23; + register8_t ADCBCAL0; /* ADCB Calibration Byte 0 */ + register8_t ADCBCAL1; /* ADCB Calibration Byte 1 */ + register8_t reserved_0x26; + register8_t reserved_0x27; + register8_t reserved_0x28; + register8_t reserved_0x29; + register8_t reserved_0x2A; + register8_t reserved_0x2B; + register8_t reserved_0x2C; + register8_t reserved_0x2D; + register8_t TEMPSENSE0; /* Temperature Sensor Calibration Byte 0 */ + register8_t TEMPSENSE1; /* Temperature Sensor Calibration Byte 1 */ + register8_t DACA0OFFCAL; /* DACA0 Calibration Byte 0 */ + register8_t DACA0GAINCAL; /* DACA0 Calibration Byte 1 */ + register8_t DACB0OFFCAL; /* DACB0 Calibration Byte 0 */ + register8_t DACB0GAINCAL; /* DACB0 Calibration Byte 1 */ + register8_t DACA1OFFCAL; /* DACA1 Calibration Byte 0 */ + register8_t DACA1GAINCAL; /* DACA1 Calibration Byte 1 */ + register8_t DACB1OFFCAL; /* DACB1 Calibration Byte 0 */ + register8_t DACB1GAINCAL; /* DACB1 Calibration Byte 1 */ + register8_t reserved_0x38; + register8_t reserved_0x39; + register8_t reserved_0x3A; + register8_t reserved_0x3B; + register8_t reserved_0x3C; + register8_t reserved_0x3D; + register8_t reserved_0x3E; + register8_t reserved_0x3F; + register8_t reserved_0x40; + register8_t reserved_0x41; + register8_t reserved_0x42; + register8_t reserved_0x43; + register8_t reserved_0x44; + register8_t reserved_0x45; + register8_t reserved_0x46; + register8_t reserved_0x47; +} NVM_PROD_SIGNATURES_t; + +/* +========================================================================== +IO Module Instances. Mapped to memory. +========================================================================== +*/ + +#define VPORT0 (*(VPORT_t *)(io_mem + 0x0010)) /* Virtual Port */ +#define VPORT1 (*(VPORT_t *)(io_mem + 0x0014)) /* Virtual Port */ +#define VPORT2 (*(VPORT_t *)(io_mem + 0x0018)) /* Virtual Port */ +#define VPORT3 (*(VPORT_t *)(io_mem + 0x001C)) /* Virtual Port */ +#define OCD (*(OCD_t *)(io_mem + 0x002E)) /* On-Chip Debug System */ +#define CLK (*(CLK_t *)(io_mem + 0x0040)) /* Clock System */ +#define SLEEP (*(SLEEP_t *)(io_mem + 0x0048)) /* Sleep Controller */ +#define OSC (*(OSC_t *)(io_mem + 0x0050)) /* Oscillator */ +#define DFLLRC32M (*(DFLL_t *)(io_mem + 0x0060)) /* DFLL */ +#define DFLLRC2M (*(DFLL_t *)(io_mem + 0x0068)) /* DFLL */ +#define PR (*(PR_t *)(io_mem + 0x0070)) /* Power Reduction */ +#define RST (*(RST_t *)(io_mem + 0x0078)) /* Reset */ +#define WDT (*(WDT_t *)(io_mem + 0x0080)) /* Watch-Dog Timer */ +#define MCU (*(MCU_t *)(io_mem + 0x0090)) /* MCU Control */ +#define PMIC (*(PMIC_t *)(io_mem + 0x00A0)) /* Programmable Multi-level Interrupt Controller */ +#define PORTCFG (*(PORTCFG_t *)(io_mem + 0x00B0)) /* I/O port Configuration */ +#define AES (*(AES_t *)(io_mem + 0x00C0)) /* AES Module */ +#define CRC (*(CRC_t *)(io_mem + 0x00D0)) /* Cyclic Redundancy Checker */ +#define DMA (*(DMA_t *)(io_mem + 0x0100)) /* DMA Controller */ +#define EVSYS (*(EVSYS_t *)(io_mem + 0x0180)) /* Event System */ +#define NVM (*(NVM_t *)(io_mem + 0x01C0)) /* Non-volatile Memory Controller */ +#define ADCA (*(ADC_t *)(io_mem + 0x0200)) /* Analog-to-Digital Converter */ +#define ADCB (*(ADC_t *)(io_mem + 0x0240)) /* Analog-to-Digital Converter */ +#define DACB (*(DAC_t *)(io_mem + 0x0320)) /* Digital-to-Analog Converter */ +#define ACA (*(AC_t *)(io_mem + 0x0380)) /* Analog Comparator */ +#define ACB (*(AC_t *)(io_mem + 0x0390)) /* Analog Comparator */ +#define RTC (*(RTC_t *)(io_mem + 0x0400)) /* Real-Time Counter */ +#define TWIC (*(TWI_t *)(io_mem + 0x0480)) /* Two-Wire Interface */ +#define TWIE (*(TWI_t *)(io_mem + 0x04A0)) /* Two-Wire Interface */ +#define USB (*(USB_t *)(io_mem + 0x04C0)) /* Universal Serial Bus */ +#define PORTA (*(PORT_t *)(io_mem + 0x0600)) /* I/O Ports */ +#define PORTB (*(PORT_t *)(io_mem + 0x0620)) /* I/O Ports */ +#define PORTC (*(PORT_t *)(io_mem + 0x0640)) /* I/O Ports */ +#define PORTD (*(PORT_t *)(io_mem + 0x0660)) /* I/O Ports */ +#define PORTE (*(PORT_t *)(io_mem + 0x0680)) /* I/O Ports */ +#define PORTF (*(PORT_t *)(io_mem + 0x06A0)) /* I/O Ports */ +#define PORTR (*(PORT_t *)(io_mem + 0x07E0)) /* I/O Ports */ +#define TCC0 (*(TC0_t *)(io_mem + 0x0800)) /* 16-bit Timer/Counter 0 */ +#define TCC2 (*(TC2_t *)(io_mem + 0x0800)) /* 16-bit Timer/Counter type 2 */ +#define TCC1 (*(TC1_t *)(io_mem + 0x0840)) /* 16-bit Timer/Counter 1 */ +#define AWEXC (*(AWEX_t *)(io_mem + 0x0880)) /* Advanced Waveform Extension */ +#define HIRESC (*(HIRES_t *)(io_mem + 0x0890)) /* High-Resolution Extension */ +#define USARTC0 (*(USART_t *)(io_mem + 0x08A0)) /* Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTC1 (*(USART_t *)(io_mem + 0x08B0)) /* Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define SPIC (*(SPI_t *)(io_mem + 0x08C0)) /* Serial Peripheral Interface */ +#define IRCOM (*(IRCOM_t *)(io_mem + 0x08F8)) /* IR Communication Module */ +#define TCD0 (*(TC0_t *)(io_mem + 0x0900)) /* 16-bit Timer/Counter 0 */ +#define TCD2 (*(TC2_t *)(io_mem + 0x0900)) /* 16-bit Timer/Counter type 2 */ +#define TCD1 (*(TC1_t *)(io_mem + 0x0940)) /* 16-bit Timer/Counter 1 */ +#define HIRESD (*(HIRES_t *)(io_mem + 0x0990)) /* High-Resolution Extension */ +#define USARTD0 (*(USART_t *)(io_mem + 0x09A0)) /* Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTD1 (*(USART_t *)(io_mem + 0x09B0)) /* Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define SPID (*(SPI_t *)(io_mem + 0x09C0)) /* Serial Peripheral Interface */ +#define TCE0 (*(TC0_t *)(io_mem + 0x0A00)) /* 16-bit Timer/Counter 0 */ +#define TCE2 (*(TC2_t *)(io_mem + 0x0A00)) /* 16-bit Timer/Counter type 2 */ +#define TCE1 (*(TC1_t *)(io_mem + 0x0A40)) /* 16-bit Timer/Counter 1 */ +#define AWEXE (*(AWEX_t *)(io_mem + 0x0A80)) /* Advanced Waveform Extension */ +#define HIRESE (*(HIRES_t *)(io_mem + 0x0A90)) /* High-Resolution Extension */ +#define USARTE0 (*(USART_t *)(io_mem + 0x0AA0)) /* Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTE1 (*(USART_t *)(io_mem + 0x0AB0)) /* Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define SPIE (*(SPI_t *)(io_mem + 0x0AC0)) /* Serial Peripheral Interface */ +#define TCF0 (*(TC0_t *)(io_mem + 0x0B00)) /* 16-bit Timer/Counter 0 */ +#define TCF2 (*(TC2_t *)(io_mem + 0x0B00)) /* 16-bit Timer/Counter type 2 */ +#define HIRESF (*(HIRES_t *)(io_mem + 0x0B90)) /* High-Resolution Extension */ +#define USARTF0 (*(USART_t *)(io_mem + 0x0BA0)) /* Universal Synchronous/Asynchronous Receiver/Transmitter */ + + +#endif /* !defined (__ASSEMBLER__) */ + + +/* ========== Flattened fully qualified IO register names ========== */ + +/* GPIO - General Purpose IO Registers */ +#define GPIO_GPIOR0 _SFR_MEM8(0x0000) +#define GPIO_GPIOR1 _SFR_MEM8(0x0001) +#define GPIO_GPIOR2 _SFR_MEM8(0x0002) +#define GPIO_GPIOR3 _SFR_MEM8(0x0003) +#define GPIO_GPIOR4 _SFR_MEM8(0x0004) +#define GPIO_GPIOR5 _SFR_MEM8(0x0005) +#define GPIO_GPIOR6 _SFR_MEM8(0x0006) +#define GPIO_GPIOR7 _SFR_MEM8(0x0007) +#define GPIO_GPIOR8 _SFR_MEM8(0x0008) +#define GPIO_GPIOR9 _SFR_MEM8(0x0009) +#define GPIO_GPIORA _SFR_MEM8(0x000A) +#define GPIO_GPIORB _SFR_MEM8(0x000B) +#define GPIO_GPIORC _SFR_MEM8(0x000C) +#define GPIO_GPIORD _SFR_MEM8(0x000D) +#define GPIO_GPIORE _SFR_MEM8(0x000E) +#define GPIO_GPIORF _SFR_MEM8(0x000F) + +/* Deprecated */ +#define GPIO_GPIO0 _SFR_MEM8(0x0000) +#define GPIO_GPIO1 _SFR_MEM8(0x0001) +#define GPIO_GPIO2 _SFR_MEM8(0x0002) +#define GPIO_GPIO3 _SFR_MEM8(0x0003) +#define GPIO_GPIO4 _SFR_MEM8(0x0004) +#define GPIO_GPIO5 _SFR_MEM8(0x0005) +#define GPIO_GPIO6 _SFR_MEM8(0x0006) +#define GPIO_GPIO7 _SFR_MEM8(0x0007) +#define GPIO_GPIO8 _SFR_MEM8(0x0008) +#define GPIO_GPIO9 _SFR_MEM8(0x0009) +#define GPIO_GPIOA _SFR_MEM8(0x000A) +#define GPIO_GPIOB _SFR_MEM8(0x000B) +#define GPIO_GPIOC _SFR_MEM8(0x000C) +#define GPIO_GPIOD _SFR_MEM8(0x000D) +#define GPIO_GPIOE _SFR_MEM8(0x000E) +#define GPIO_GPIOF _SFR_MEM8(0x000F) + +/* NVM_FUSES - Fuses */ +#define FUSE_FUSEBYTE0 _SFR_MEM8(0x0000) +#define FUSE_FUSEBYTE1 _SFR_MEM8(0x0001) +#define FUSE_FUSEBYTE2 _SFR_MEM8(0x0002) +#define FUSE_FUSEBYTE4 _SFR_MEM8(0x0004) +#define FUSE_FUSEBYTE5 _SFR_MEM8(0x0005) + +/* NVM_LOCKBITS - Lock Bits */ +#define LOCKBIT_LOCKBITS _SFR_MEM8(0x0000) + +/* NVM_PROD_SIGNATURES - Production Signatures */ +#define PRODSIGNATURES_RCOSC2M _SFR_MEM8(0x0000) +#define PRODSIGNATURES_RCOSC2MA _SFR_MEM8(0x0001) +#define PRODSIGNATURES_RCOSC32K _SFR_MEM8(0x0002) +#define PRODSIGNATURES_RCOSC32M _SFR_MEM8(0x0003) +#define PRODSIGNATURES_RCOSC32MA _SFR_MEM8(0x0004) +#define PRODSIGNATURES_LOTNUM0 _SFR_MEM8(0x0008) +#define PRODSIGNATURES_LOTNUM1 _SFR_MEM8(0x0009) +#define PRODSIGNATURES_LOTNUM2 _SFR_MEM8(0x000A) +#define PRODSIGNATURES_LOTNUM3 _SFR_MEM8(0x000B) +#define PRODSIGNATURES_LOTNUM4 _SFR_MEM8(0x000C) +#define PRODSIGNATURES_LOTNUM5 _SFR_MEM8(0x000D) +#define PRODSIGNATURES_WAFNUM _SFR_MEM8(0x0010) +#define PRODSIGNATURES_COORDX0 _SFR_MEM8(0x0012) +#define PRODSIGNATURES_COORDX1 _SFR_MEM8(0x0013) +#define PRODSIGNATURES_COORDY0 _SFR_MEM8(0x0014) +#define PRODSIGNATURES_COORDY1 _SFR_MEM8(0x0015) +#define PRODSIGNATURES_USBCAL0 _SFR_MEM8(0x001A) +#define PRODSIGNATURES_USBCAL1 _SFR_MEM8(0x001B) +#define PRODSIGNATURES_USBRCOSC _SFR_MEM8(0x001C) +#define PRODSIGNATURES_USBRCOSCA _SFR_MEM8(0x001D) +#define PRODSIGNATURES_ADCACAL0 _SFR_MEM8(0x0020) +#define PRODSIGNATURES_ADCACAL1 _SFR_MEM8(0x0021) +#define PRODSIGNATURES_ADCBCAL0 _SFR_MEM8(0x0024) +#define PRODSIGNATURES_ADCBCAL1 _SFR_MEM8(0x0025) +#define PRODSIGNATURES_TEMPSENSE0 _SFR_MEM8(0x002E) +#define PRODSIGNATURES_TEMPSENSE1 _SFR_MEM8(0x002F) +#define PRODSIGNATURES_DACA0OFFCAL _SFR_MEM8(0x0030) +#define PRODSIGNATURES_DACA0GAINCAL _SFR_MEM8(0x0031) +#define PRODSIGNATURES_DACB0OFFCAL _SFR_MEM8(0x0032) +#define PRODSIGNATURES_DACB0GAINCAL _SFR_MEM8(0x0033) +#define PRODSIGNATURES_DACA1OFFCAL _SFR_MEM8(0x0034) +#define PRODSIGNATURES_DACA1GAINCAL _SFR_MEM8(0x0035) +#define PRODSIGNATURES_DACB1OFFCAL _SFR_MEM8(0x0036) +#define PRODSIGNATURES_DACB1GAINCAL _SFR_MEM8(0x0037) + +/* VPORT - Virtual Port */ +#define VPORT0_DIR _SFR_MEM8(0x0010) +#define VPORT0_OUT _SFR_MEM8(0x0011) +#define VPORT0_IN _SFR_MEM8(0x0012) +#define VPORT0_INTFLAGS _SFR_MEM8(0x0013) + +/* VPORT - Virtual Port */ +#define VPORT1_DIR _SFR_MEM8(0x0014) +#define VPORT1_OUT _SFR_MEM8(0x0015) +#define VPORT1_IN _SFR_MEM8(0x0016) +#define VPORT1_INTFLAGS _SFR_MEM8(0x0017) + +/* VPORT - Virtual Port */ +#define VPORT2_DIR _SFR_MEM8(0x0018) +#define VPORT2_OUT _SFR_MEM8(0x0019) +#define VPORT2_IN _SFR_MEM8(0x001A) +#define VPORT2_INTFLAGS _SFR_MEM8(0x001B) + +/* VPORT - Virtual Port */ +#define VPORT3_DIR _SFR_MEM8(0x001C) +#define VPORT3_OUT _SFR_MEM8(0x001D) +#define VPORT3_IN _SFR_MEM8(0x001E) +#define VPORT3_INTFLAGS _SFR_MEM8(0x001F) + +/* OCD - On-Chip Debug System */ +#define OCD_OCDR0 _SFR_MEM8(0x002E) +#define OCD_OCDR1 _SFR_MEM8(0x002F) + +/* CPU - CPU registers */ +#define CPU_CCP _SFR_MEM8(0x0034) +#define CPU_RAMPD _SFR_MEM8(0x0038) +#define CPU_RAMPX _SFR_MEM8(0x0039) +#define CPU_RAMPY _SFR_MEM8(0x003A) +#define CPU_RAMPZ _SFR_MEM8(0x003B) +#define CPU_EIND _SFR_MEM8(0x003C) +#define CPU_SPL _SFR_MEM8(0x003D) +#define CPU_SPH _SFR_MEM8(0x003E) +#define CPU_SREG _SFR_MEM8(0x003F) + +/* CLK - Clock System */ +#define CLK_CTRL _SFR_MEM8(0x0040) +#define CLK_PSCTRL _SFR_MEM8(0x0041) +#define CLK_LOCK _SFR_MEM8(0x0042) +#define CLK_RTCCTRL _SFR_MEM8(0x0043) +#define CLK_USBCTRL _SFR_MEM8(0x0044) + +/* SLEEP - Sleep Controller */ +#define SLEEP_CTRL _SFR_MEM8(0x0048) + +/* OSC - Oscillator */ +#define OSC_CTRL _SFR_MEM8(0x0050) +#define OSC_STATUS _SFR_MEM8(0x0051) +#define OSC_XOSCCTRL _SFR_MEM8(0x0052) +#define OSC_XOSCFAIL _SFR_MEM8(0x0053) +#define OSC_RC32KCAL _SFR_MEM8(0x0054) +#define OSC_PLLCTRL _SFR_MEM8(0x0055) +#define OSC_DFLLCTRL _SFR_MEM8(0x0056) + +/* DFLL - DFLL */ +#define DFLLRC32M_CTRL _SFR_MEM8(0x0060) +#define DFLLRC32M_CALA _SFR_MEM8(0x0062) +#define DFLLRC32M_CALB _SFR_MEM8(0x0063) +#define DFLLRC32M_COMP0 _SFR_MEM8(0x0064) +#define DFLLRC32M_COMP1 _SFR_MEM8(0x0065) +#define DFLLRC32M_COMP2 _SFR_MEM8(0x0066) + +/* DFLL - DFLL */ +#define DFLLRC2M_CTRL _SFR_MEM8(0x0068) +#define DFLLRC2M_CALA _SFR_MEM8(0x006A) +#define DFLLRC2M_CALB _SFR_MEM8(0x006B) +#define DFLLRC2M_COMP0 _SFR_MEM8(0x006C) +#define DFLLRC2M_COMP1 _SFR_MEM8(0x006D) +#define DFLLRC2M_COMP2 _SFR_MEM8(0x006E) + +/* PR - Power Reduction */ +#define PR_PRGEN _SFR_MEM8(0x0070) +#define PR_PRPA _SFR_MEM8(0x0071) +#define PR_PRPB _SFR_MEM8(0x0072) +#define PR_PRPC _SFR_MEM8(0x0073) +#define PR_PRPD _SFR_MEM8(0x0074) +#define PR_PRPE _SFR_MEM8(0x0075) +#define PR_PRPF _SFR_MEM8(0x0076) + +/* RST - Reset */ +#define RST_STATUS _SFR_MEM8(0x0078) +#define RST_CTRL _SFR_MEM8(0x0079) + +/* WDT - Watch-Dog Timer */ +#define WDT_CTRL _SFR_MEM8(0x0080) +#define WDT_WINCTRL _SFR_MEM8(0x0081) +#define WDT_STATUS _SFR_MEM8(0x0082) + +/* MCU - MCU Control */ +#define MCU_DEVID0 _SFR_MEM8(0x0090) +#define MCU_DEVID1 _SFR_MEM8(0x0091) +#define MCU_DEVID2 _SFR_MEM8(0x0092) +#define MCU_REVID _SFR_MEM8(0x0093) +#define MCU_JTAGUID _SFR_MEM8(0x0094) +#define MCU_MCUCR _SFR_MEM8(0x0096) +#define MCU_ANAINIT _SFR_MEM8(0x0097) +#define MCU_EVSYSLOCK _SFR_MEM8(0x0098) +#define MCU_AWEXLOCK _SFR_MEM8(0x0099) + +/* PMIC - Programmable Multi-level Interrupt Controller */ +#define PMIC_STATUS _SFR_MEM8(0x00A0) +#define PMIC_INTPRI _SFR_MEM8(0x00A1) +#define PMIC_CTRL _SFR_MEM8(0x00A2) + +/* PORTCFG - I/O port Configuration */ +#define PORTCFG_MPCMASK _SFR_MEM8(0x00B0) +#define PORTCFG_VPCTRLA _SFR_MEM8(0x00B2) +#define PORTCFG_VPCTRLB _SFR_MEM8(0x00B3) +#define PORTCFG_CLKEVOUT _SFR_MEM8(0x00B4) +#define PORTCFG_EBIOUT _SFR_MEM8(0x00B5) +#define PORTCFG_EVOUTSEL _SFR_MEM8(0x00B6) + +/* AES - AES Module */ +#define AES_CTRL _SFR_MEM8(0x00C0) +#define AES_STATUS _SFR_MEM8(0x00C1) +#define AES_STATE _SFR_MEM8(0x00C2) +#define AES_KEY _SFR_MEM8(0x00C3) +#define AES_INTCTRL _SFR_MEM8(0x00C4) + +/* CRC - Cyclic Redundancy Checker */ +#define CRC_CTRL _SFR_MEM8(0x00D0) +#define CRC_STATUS _SFR_MEM8(0x00D1) +#define CRC_DATAIN _SFR_MEM8(0x00D3) +#define CRC_CHECKSUM0 _SFR_MEM8(0x00D4) +#define CRC_CHECKSUM1 _SFR_MEM8(0x00D5) +#define CRC_CHECKSUM2 _SFR_MEM8(0x00D6) +#define CRC_CHECKSUM3 _SFR_MEM8(0x00D7) + +/* DMA - DMA Controller */ +#define DMA_CTRL _SFR_MEM8(0x0100) +#define DMA_INTFLAGS _SFR_MEM8(0x0103) +#define DMA_STATUS _SFR_MEM8(0x0104) +#define DMA_TEMP _SFR_MEM16(0x0106) +#define DMA_CH0_CTRLA _SFR_MEM8(0x0110) +#define DMA_CH0_CTRLB _SFR_MEM8(0x0111) +#define DMA_CH0_ADDRCTRL _SFR_MEM8(0x0112) +#define DMA_CH0_TRIGSRC _SFR_MEM8(0x0113) +#define DMA_CH0_TRFCNT _SFR_MEM16(0x0114) +#define DMA_CH0_REPCNT _SFR_MEM8(0x0116) +#define DMA_CH0_SRCADDR0 _SFR_MEM8(0x0118) +#define DMA_CH0_SRCADDR1 _SFR_MEM8(0x0119) +#define DMA_CH0_SRCADDR2 _SFR_MEM8(0x011A) +#define DMA_CH0_DESTADDR0 _SFR_MEM8(0x011C) +#define DMA_CH0_DESTADDR1 _SFR_MEM8(0x011D) +#define DMA_CH0_DESTADDR2 _SFR_MEM8(0x011E) +#define DMA_CH1_CTRLA _SFR_MEM8(0x0120) +#define DMA_CH1_CTRLB _SFR_MEM8(0x0121) +#define DMA_CH1_ADDRCTRL _SFR_MEM8(0x0122) +#define DMA_CH1_TRIGSRC _SFR_MEM8(0x0123) +#define DMA_CH1_TRFCNT _SFR_MEM16(0x0124) +#define DMA_CH1_REPCNT _SFR_MEM8(0x0126) +#define DMA_CH1_SRCADDR0 _SFR_MEM8(0x0128) +#define DMA_CH1_SRCADDR1 _SFR_MEM8(0x0129) +#define DMA_CH1_SRCADDR2 _SFR_MEM8(0x012A) +#define DMA_CH1_DESTADDR0 _SFR_MEM8(0x012C) +#define DMA_CH1_DESTADDR1 _SFR_MEM8(0x012D) +#define DMA_CH1_DESTADDR2 _SFR_MEM8(0x012E) +#define DMA_CH2_CTRLA _SFR_MEM8(0x0130) +#define DMA_CH2_CTRLB _SFR_MEM8(0x0131) +#define DMA_CH2_ADDRCTRL _SFR_MEM8(0x0132) +#define DMA_CH2_TRIGSRC _SFR_MEM8(0x0133) +#define DMA_CH2_TRFCNT _SFR_MEM16(0x0134) +#define DMA_CH2_REPCNT _SFR_MEM8(0x0136) +#define DMA_CH2_SRCADDR0 _SFR_MEM8(0x0138) +#define DMA_CH2_SRCADDR1 _SFR_MEM8(0x0139) +#define DMA_CH2_SRCADDR2 _SFR_MEM8(0x013A) +#define DMA_CH2_DESTADDR0 _SFR_MEM8(0x013C) +#define DMA_CH2_DESTADDR1 _SFR_MEM8(0x013D) +#define DMA_CH2_DESTADDR2 _SFR_MEM8(0x013E) +#define DMA_CH3_CTRLA _SFR_MEM8(0x0140) +#define DMA_CH3_CTRLB _SFR_MEM8(0x0141) +#define DMA_CH3_ADDRCTRL _SFR_MEM8(0x0142) +#define DMA_CH3_TRIGSRC _SFR_MEM8(0x0143) +#define DMA_CH3_TRFCNT _SFR_MEM16(0x0144) +#define DMA_CH3_REPCNT _SFR_MEM8(0x0146) +#define DMA_CH3_SRCADDR0 _SFR_MEM8(0x0148) +#define DMA_CH3_SRCADDR1 _SFR_MEM8(0x0149) +#define DMA_CH3_SRCADDR2 _SFR_MEM8(0x014A) +#define DMA_CH3_DESTADDR0 _SFR_MEM8(0x014C) +#define DMA_CH3_DESTADDR1 _SFR_MEM8(0x014D) +#define DMA_CH3_DESTADDR2 _SFR_MEM8(0x014E) + +/* EVSYS - Event System */ +#define EVSYS_CH0MUX _SFR_MEM8(0x0180) +#define EVSYS_CH1MUX _SFR_MEM8(0x0181) +#define EVSYS_CH2MUX _SFR_MEM8(0x0182) +#define EVSYS_CH3MUX _SFR_MEM8(0x0183) +#define EVSYS_CH4MUX _SFR_MEM8(0x0184) +#define EVSYS_CH5MUX _SFR_MEM8(0x0185) +#define EVSYS_CH6MUX _SFR_MEM8(0x0186) +#define EVSYS_CH7MUX _SFR_MEM8(0x0187) +#define EVSYS_CH0CTRL _SFR_MEM8(0x0188) +#define EVSYS_CH1CTRL _SFR_MEM8(0x0189) +#define EVSYS_CH2CTRL _SFR_MEM8(0x018A) +#define EVSYS_CH3CTRL _SFR_MEM8(0x018B) +#define EVSYS_CH4CTRL _SFR_MEM8(0x018C) +#define EVSYS_CH5CTRL _SFR_MEM8(0x018D) +#define EVSYS_CH6CTRL _SFR_MEM8(0x018E) +#define EVSYS_CH7CTRL _SFR_MEM8(0x018F) +#define EVSYS_STROBE _SFR_MEM8(0x0190) +#define EVSYS_DATA _SFR_MEM8(0x0191) + +/* NVM - Non-volatile Memory Controller */ +#define NVM_ADDR0 _SFR_MEM8(0x01C0) +#define NVM_ADDR1 _SFR_MEM8(0x01C1) +#define NVM_ADDR2 _SFR_MEM8(0x01C2) +#define NVM_DATA0 _SFR_MEM8(0x01C4) +#define NVM_DATA1 _SFR_MEM8(0x01C5) +#define NVM_DATA2 _SFR_MEM8(0x01C6) +#define NVM_CMD _SFR_MEM8(0x01CA) +#define NVM_CTRLA _SFR_MEM8(0x01CB) +#define NVM_CTRLB _SFR_MEM8(0x01CC) +#define NVM_INTCTRL _SFR_MEM8(0x01CD) +#define NVM_STATUS _SFR_MEM8(0x01CF) +#define NVM_LOCKBITS _SFR_MEM8(0x01D0) + +/* ADC - Analog-to-Digital Converter */ +#define ADCA_CTRLA _SFR_MEM8(0x0200) +#define ADCA_CTRLB _SFR_MEM8(0x0201) +#define ADCA_REFCTRL _SFR_MEM8(0x0202) +#define ADCA_EVCTRL _SFR_MEM8(0x0203) +#define ADCA_PRESCALER _SFR_MEM8(0x0204) +#define ADCA_INTFLAGS _SFR_MEM8(0x0206) +#define ADCA_TEMP _SFR_MEM8(0x0207) +#define ADCA_CAL _SFR_MEM16(0x020C) +#define ADCA_CH0RES _SFR_MEM16(0x0210) +#define ADCA_CH1RES _SFR_MEM16(0x0212) +#define ADCA_CH2RES _SFR_MEM16(0x0214) +#define ADCA_CH3RES _SFR_MEM16(0x0216) +#define ADCA_CMP _SFR_MEM16(0x0218) +#define ADCA_CH0_CTRL _SFR_MEM8(0x0220) +#define ADCA_CH0_MUXCTRL _SFR_MEM8(0x0221) +#define ADCA_CH0_INTCTRL _SFR_MEM8(0x0222) +#define ADCA_CH0_INTFLAGS _SFR_MEM8(0x0223) +#define ADCA_CH0_RES _SFR_MEM16(0x0224) +#define ADCA_CH0_SCAN _SFR_MEM8(0x0226) +#define ADCA_CH1_CTRL _SFR_MEM8(0x0228) +#define ADCA_CH1_MUXCTRL _SFR_MEM8(0x0229) +#define ADCA_CH1_INTCTRL _SFR_MEM8(0x022A) +#define ADCA_CH1_INTFLAGS _SFR_MEM8(0x022B) +#define ADCA_CH1_RES _SFR_MEM16(0x022C) +#define ADCA_CH1_SCAN _SFR_MEM8(0x022E) +#define ADCA_CH2_CTRL _SFR_MEM8(0x0230) +#define ADCA_CH2_MUXCTRL _SFR_MEM8(0x0231) +#define ADCA_CH2_INTCTRL _SFR_MEM8(0x0232) +#define ADCA_CH2_INTFLAGS _SFR_MEM8(0x0233) +#define ADCA_CH2_RES _SFR_MEM16(0x0234) +#define ADCA_CH2_SCAN _SFR_MEM8(0x0236) +#define ADCA_CH3_CTRL _SFR_MEM8(0x0238) +#define ADCA_CH3_MUXCTRL _SFR_MEM8(0x0239) +#define ADCA_CH3_INTCTRL _SFR_MEM8(0x023A) +#define ADCA_CH3_INTFLAGS _SFR_MEM8(0x023B) +#define ADCA_CH3_RES _SFR_MEM16(0x023C) +#define ADCA_CH3_SCAN _SFR_MEM8(0x023E) + +/* ADC - Analog-to-Digital Converter */ +#define ADCB_CTRLA _SFR_MEM8(0x0240) +#define ADCB_CTRLB _SFR_MEM8(0x0241) +#define ADCB_REFCTRL _SFR_MEM8(0x0242) +#define ADCB_EVCTRL _SFR_MEM8(0x0243) +#define ADCB_PRESCALER _SFR_MEM8(0x0244) +#define ADCB_INTFLAGS _SFR_MEM8(0x0246) +#define ADCB_TEMP _SFR_MEM8(0x0247) +#define ADCB_CAL _SFR_MEM16(0x024C) +#define ADCB_CH0RES _SFR_MEM16(0x0250) +#define ADCB_CH1RES _SFR_MEM16(0x0252) +#define ADCB_CH2RES _SFR_MEM16(0x0254) +#define ADCB_CH3RES _SFR_MEM16(0x0256) +#define ADCB_CMP _SFR_MEM16(0x0258) +#define ADCB_CH0_CTRL _SFR_MEM8(0x0260) +#define ADCB_CH0_MUXCTRL _SFR_MEM8(0x0261) +#define ADCB_CH0_INTCTRL _SFR_MEM8(0x0262) +#define ADCB_CH0_INTFLAGS _SFR_MEM8(0x0263) +#define ADCB_CH0_RES _SFR_MEM16(0x0264) +#define ADCB_CH0_SCAN _SFR_MEM8(0x0266) +#define ADCB_CH1_CTRL _SFR_MEM8(0x0268) +#define ADCB_CH1_MUXCTRL _SFR_MEM8(0x0269) +#define ADCB_CH1_INTCTRL _SFR_MEM8(0x026A) +#define ADCB_CH1_INTFLAGS _SFR_MEM8(0x026B) +#define ADCB_CH1_RES _SFR_MEM16(0x026C) +#define ADCB_CH1_SCAN _SFR_MEM8(0x026E) +#define ADCB_CH2_CTRL _SFR_MEM8(0x0270) +#define ADCB_CH2_MUXCTRL _SFR_MEM8(0x0271) +#define ADCB_CH2_INTCTRL _SFR_MEM8(0x0272) +#define ADCB_CH2_INTFLAGS _SFR_MEM8(0x0273) +#define ADCB_CH2_RES _SFR_MEM16(0x0274) +#define ADCB_CH2_SCAN _SFR_MEM8(0x0276) +#define ADCB_CH3_CTRL _SFR_MEM8(0x0278) +#define ADCB_CH3_MUXCTRL _SFR_MEM8(0x0279) +#define ADCB_CH3_INTCTRL _SFR_MEM8(0x027A) +#define ADCB_CH3_INTFLAGS _SFR_MEM8(0x027B) +#define ADCB_CH3_RES _SFR_MEM16(0x027C) +#define ADCB_CH3_SCAN _SFR_MEM8(0x027E) + +/* DAC - Digital-to-Analog Converter */ +#define DACB_CTRLA _SFR_MEM8(0x0320) +#define DACB_CTRLB _SFR_MEM8(0x0321) +#define DACB_CTRLC _SFR_MEM8(0x0322) +#define DACB_EVCTRL _SFR_MEM8(0x0323) +#define DACB_STATUS _SFR_MEM8(0x0325) +#define DACB_CH0GAINCAL _SFR_MEM8(0x0328) +#define DACB_CH0OFFSETCAL _SFR_MEM8(0x0329) +#define DACB_CH1GAINCAL _SFR_MEM8(0x032A) +#define DACB_CH1OFFSETCAL _SFR_MEM8(0x032B) +#define DACB_CH0DATA _SFR_MEM16(0x0338) +#define DACB_CH1DATA _SFR_MEM16(0x033A) + +/* AC - Analog Comparator */ +#define ACA_AC0CTRL _SFR_MEM8(0x0380) +#define ACA_AC1CTRL _SFR_MEM8(0x0381) +#define ACA_AC0MUXCTRL _SFR_MEM8(0x0382) +#define ACA_AC1MUXCTRL _SFR_MEM8(0x0383) +#define ACA_CTRLA _SFR_MEM8(0x0384) +#define ACA_CTRLB _SFR_MEM8(0x0385) +#define ACA_WINCTRL _SFR_MEM8(0x0386) +#define ACA_STATUS _SFR_MEM8(0x0387) + +/* AC - Analog Comparator */ +#define ACB_AC0CTRL _SFR_MEM8(0x0390) +#define ACB_AC1CTRL _SFR_MEM8(0x0391) +#define ACB_AC0MUXCTRL _SFR_MEM8(0x0392) +#define ACB_AC1MUXCTRL _SFR_MEM8(0x0393) +#define ACB_CTRLA _SFR_MEM8(0x0394) +#define ACB_CTRLB _SFR_MEM8(0x0395) +#define ACB_WINCTRL _SFR_MEM8(0x0396) +#define ACB_STATUS _SFR_MEM8(0x0397) + +/* RTC - Real-Time Counter */ +#define RTC_CTRL _SFR_MEM8(0x0400) +#define RTC_STATUS _SFR_MEM8(0x0401) +#define RTC_INTCTRL _SFR_MEM8(0x0402) +#define RTC_INTFLAGS _SFR_MEM8(0x0403) +#define RTC_TEMP _SFR_MEM8(0x0404) +#define RTC_CNT _SFR_MEM16(0x0408) +#define RTC_PER _SFR_MEM16(0x040A) +#define RTC_COMP _SFR_MEM16(0x040C) + +/* TWI - Two-Wire Interface */ +#define TWIC_CTRL _SFR_MEM8(0x0480) +#define TWIC_MASTER_CTRLA _SFR_MEM8(0x0481) +#define TWIC_MASTER_CTRLB _SFR_MEM8(0x0482) +#define TWIC_MASTER_CTRLC _SFR_MEM8(0x0483) +#define TWIC_MASTER_STATUS _SFR_MEM8(0x0484) +#define TWIC_MASTER_BAUD _SFR_MEM8(0x0485) +#define TWIC_MASTER_ADDR _SFR_MEM8(0x0486) +#define TWIC_MASTER_DATA _SFR_MEM8(0x0487) +#define TWIC_SLAVE_CTRLA _SFR_MEM8(0x0488) +#define TWIC_SLAVE_CTRLB _SFR_MEM8(0x0489) +#define TWIC_SLAVE_STATUS _SFR_MEM8(0x048A) +#define TWIC_SLAVE_ADDR _SFR_MEM8(0x048B) +#define TWIC_SLAVE_DATA _SFR_MEM8(0x048C) +#define TWIC_SLAVE_ADDRMASK _SFR_MEM8(0x048D) + +/* TWI - Two-Wire Interface */ +#define TWIE_CTRL _SFR_MEM8(0x04A0) +#define TWIE_MASTER_CTRLA _SFR_MEM8(0x04A1) +#define TWIE_MASTER_CTRLB _SFR_MEM8(0x04A2) +#define TWIE_MASTER_CTRLC _SFR_MEM8(0x04A3) +#define TWIE_MASTER_STATUS _SFR_MEM8(0x04A4) +#define TWIE_MASTER_BAUD _SFR_MEM8(0x04A5) +#define TWIE_MASTER_ADDR _SFR_MEM8(0x04A6) +#define TWIE_MASTER_DATA _SFR_MEM8(0x04A7) +#define TWIE_SLAVE_CTRLA _SFR_MEM8(0x04A8) +#define TWIE_SLAVE_CTRLB _SFR_MEM8(0x04A9) +#define TWIE_SLAVE_STATUS _SFR_MEM8(0x04AA) +#define TWIE_SLAVE_ADDR _SFR_MEM8(0x04AB) +#define TWIE_SLAVE_DATA _SFR_MEM8(0x04AC) +#define TWIE_SLAVE_ADDRMASK _SFR_MEM8(0x04AD) + +/* USB - Universal Serial Bus */ +#define USB_CTRLA _SFR_MEM8(0x04C0) +#define USB_CTRLB _SFR_MEM8(0x04C1) +#define USB_STATUS _SFR_MEM8(0x04C2) +#define USB_ADDR _SFR_MEM8(0x04C3) +#define USB_FIFOWP _SFR_MEM8(0x04C4) +#define USB_FIFORP _SFR_MEM8(0x04C5) +#define USB_EPPTR _SFR_MEM16(0x04C6) +#define USB_INTCTRLA _SFR_MEM8(0x04C8) +#define USB_INTCTRLB _SFR_MEM8(0x04C9) +#define USB_INTFLAGSACLR _SFR_MEM8(0x04CA) +#define USB_INTFLAGSASET _SFR_MEM8(0x04CB) +#define USB_INTFLAGSBCLR _SFR_MEM8(0x04CC) +#define USB_INTFLAGSBSET _SFR_MEM8(0x04CD) +#define USB_CAL0 _SFR_MEM8(0x04FA) +#define USB_CAL1 _SFR_MEM8(0x04FB) + +/* PORT - I/O Ports */ +#define PORTA_DIR _SFR_MEM8(0x0600) +#define PORTA_DIRSET _SFR_MEM8(0x0601) +#define PORTA_DIRCLR _SFR_MEM8(0x0602) +#define PORTA_DIRTGL _SFR_MEM8(0x0603) +#define PORTA_OUT _SFR_MEM8(0x0604) +#define PORTA_OUTSET _SFR_MEM8(0x0605) +#define PORTA_OUTCLR _SFR_MEM8(0x0606) +#define PORTA_OUTTGL _SFR_MEM8(0x0607) +#define PORTA_IN _SFR_MEM8(0x0608) +#define PORTA_INTCTRL _SFR_MEM8(0x0609) +#define PORTA_INT0MASK _SFR_MEM8(0x060A) +#define PORTA_INT1MASK _SFR_MEM8(0x060B) +#define PORTA_INTFLAGS _SFR_MEM8(0x060C) +#define PORTA_REMAP _SFR_MEM8(0x060E) +#define PORTA_PIN0CTRL _SFR_MEM8(0x0610) +#define PORTA_PIN1CTRL _SFR_MEM8(0x0611) +#define PORTA_PIN2CTRL _SFR_MEM8(0x0612) +#define PORTA_PIN3CTRL _SFR_MEM8(0x0613) +#define PORTA_PIN4CTRL _SFR_MEM8(0x0614) +#define PORTA_PIN5CTRL _SFR_MEM8(0x0615) +#define PORTA_PIN6CTRL _SFR_MEM8(0x0616) +#define PORTA_PIN7CTRL _SFR_MEM8(0x0617) + +/* PORT - I/O Ports */ +#define PORTB_DIR _SFR_MEM8(0x0620) +#define PORTB_DIRSET _SFR_MEM8(0x0621) +#define PORTB_DIRCLR _SFR_MEM8(0x0622) +#define PORTB_DIRTGL _SFR_MEM8(0x0623) +#define PORTB_OUT _SFR_MEM8(0x0624) +#define PORTB_OUTSET _SFR_MEM8(0x0625) +#define PORTB_OUTCLR _SFR_MEM8(0x0626) +#define PORTB_OUTTGL _SFR_MEM8(0x0627) +#define PORTB_IN _SFR_MEM8(0x0628) +#define PORTB_INTCTRL _SFR_MEM8(0x0629) +#define PORTB_INT0MASK _SFR_MEM8(0x062A) +#define PORTB_INT1MASK _SFR_MEM8(0x062B) +#define PORTB_INTFLAGS _SFR_MEM8(0x062C) +#define PORTB_REMAP _SFR_MEM8(0x062E) +#define PORTB_PIN0CTRL _SFR_MEM8(0x0630) +#define PORTB_PIN1CTRL _SFR_MEM8(0x0631) +#define PORTB_PIN2CTRL _SFR_MEM8(0x0632) +#define PORTB_PIN3CTRL _SFR_MEM8(0x0633) +#define PORTB_PIN4CTRL _SFR_MEM8(0x0634) +#define PORTB_PIN5CTRL _SFR_MEM8(0x0635) +#define PORTB_PIN6CTRL _SFR_MEM8(0x0636) +#define PORTB_PIN7CTRL _SFR_MEM8(0x0637) + +/* PORT - I/O Ports */ +#define PORTC_DIR _SFR_MEM8(0x0640) +#define PORTC_DIRSET _SFR_MEM8(0x0641) +#define PORTC_DIRCLR _SFR_MEM8(0x0642) +#define PORTC_DIRTGL _SFR_MEM8(0x0643) +#define PORTC_OUT _SFR_MEM8(0x0644) +#define PORTC_OUTSET _SFR_MEM8(0x0645) +#define PORTC_OUTCLR _SFR_MEM8(0x0646) +#define PORTC_OUTTGL _SFR_MEM8(0x0647) +#define PORTC_IN _SFR_MEM8(0x0648) +#define PORTC_INTCTRL _SFR_MEM8(0x0649) +#define PORTC_INT0MASK _SFR_MEM8(0x064A) +#define PORTC_INT1MASK _SFR_MEM8(0x064B) +#define PORTC_INTFLAGS _SFR_MEM8(0x064C) +#define PORTC_REMAP _SFR_MEM8(0x064E) +#define PORTC_PIN0CTRL _SFR_MEM8(0x0650) +#define PORTC_PIN1CTRL _SFR_MEM8(0x0651) +#define PORTC_PIN2CTRL _SFR_MEM8(0x0652) +#define PORTC_PIN3CTRL _SFR_MEM8(0x0653) +#define PORTC_PIN4CTRL _SFR_MEM8(0x0654) +#define PORTC_PIN5CTRL _SFR_MEM8(0x0655) +#define PORTC_PIN6CTRL _SFR_MEM8(0x0656) +#define PORTC_PIN7CTRL _SFR_MEM8(0x0657) + +/* PORT - I/O Ports */ +#define PORTD_DIR _SFR_MEM8(0x0660) +#define PORTD_DIRSET _SFR_MEM8(0x0661) +#define PORTD_DIRCLR _SFR_MEM8(0x0662) +#define PORTD_DIRTGL _SFR_MEM8(0x0663) +#define PORTD_OUT _SFR_MEM8(0x0664) +#define PORTD_OUTSET _SFR_MEM8(0x0665) +#define PORTD_OUTCLR _SFR_MEM8(0x0666) +#define PORTD_OUTTGL _SFR_MEM8(0x0667) +#define PORTD_IN _SFR_MEM8(0x0668) +#define PORTD_INTCTRL _SFR_MEM8(0x0669) +#define PORTD_INT0MASK _SFR_MEM8(0x066A) +#define PORTD_INT1MASK _SFR_MEM8(0x066B) +#define PORTD_INTFLAGS _SFR_MEM8(0x066C) +#define PORTD_REMAP _SFR_MEM8(0x066E) +#define PORTD_PIN0CTRL _SFR_MEM8(0x0670) +#define PORTD_PIN1CTRL _SFR_MEM8(0x0671) +#define PORTD_PIN2CTRL _SFR_MEM8(0x0672) +#define PORTD_PIN3CTRL _SFR_MEM8(0x0673) +#define PORTD_PIN4CTRL _SFR_MEM8(0x0674) +#define PORTD_PIN5CTRL _SFR_MEM8(0x0675) +#define PORTD_PIN6CTRL _SFR_MEM8(0x0676) +#define PORTD_PIN7CTRL _SFR_MEM8(0x0677) + +/* PORT - I/O Ports */ +#define PORTE_DIR _SFR_MEM8(0x0680) +#define PORTE_DIRSET _SFR_MEM8(0x0681) +#define PORTE_DIRCLR _SFR_MEM8(0x0682) +#define PORTE_DIRTGL _SFR_MEM8(0x0683) +#define PORTE_OUT _SFR_MEM8(0x0684) +#define PORTE_OUTSET _SFR_MEM8(0x0685) +#define PORTE_OUTCLR _SFR_MEM8(0x0686) +#define PORTE_OUTTGL _SFR_MEM8(0x0687) +#define PORTE_IN _SFR_MEM8(0x0688) +#define PORTE_INTCTRL _SFR_MEM8(0x0689) +#define PORTE_INT0MASK _SFR_MEM8(0x068A) +#define PORTE_INT1MASK _SFR_MEM8(0x068B) +#define PORTE_INTFLAGS _SFR_MEM8(0x068C) +#define PORTE_REMAP _SFR_MEM8(0x068E) +#define PORTE_PIN0CTRL _SFR_MEM8(0x0690) +#define PORTE_PIN1CTRL _SFR_MEM8(0x0691) +#define PORTE_PIN2CTRL _SFR_MEM8(0x0692) +#define PORTE_PIN3CTRL _SFR_MEM8(0x0693) +#define PORTE_PIN4CTRL _SFR_MEM8(0x0694) +#define PORTE_PIN5CTRL _SFR_MEM8(0x0695) +#define PORTE_PIN6CTRL _SFR_MEM8(0x0696) +#define PORTE_PIN7CTRL _SFR_MEM8(0x0697) + +/* PORT - I/O Ports */ +#define PORTF_DIR _SFR_MEM8(0x06A0) +#define PORTF_DIRSET _SFR_MEM8(0x06A1) +#define PORTF_DIRCLR _SFR_MEM8(0x06A2) +#define PORTF_DIRTGL _SFR_MEM8(0x06A3) +#define PORTF_OUT _SFR_MEM8(0x06A4) +#define PORTF_OUTSET _SFR_MEM8(0x06A5) +#define PORTF_OUTCLR _SFR_MEM8(0x06A6) +#define PORTF_OUTTGL _SFR_MEM8(0x06A7) +#define PORTF_IN _SFR_MEM8(0x06A8) +#define PORTF_INTCTRL _SFR_MEM8(0x06A9) +#define PORTF_INT0MASK _SFR_MEM8(0x06AA) +#define PORTF_INT1MASK _SFR_MEM8(0x06AB) +#define PORTF_INTFLAGS _SFR_MEM8(0x06AC) +#define PORTF_REMAP _SFR_MEM8(0x06AE) +#define PORTF_PIN0CTRL _SFR_MEM8(0x06B0) +#define PORTF_PIN1CTRL _SFR_MEM8(0x06B1) +#define PORTF_PIN2CTRL _SFR_MEM8(0x06B2) +#define PORTF_PIN3CTRL _SFR_MEM8(0x06B3) +#define PORTF_PIN4CTRL _SFR_MEM8(0x06B4) +#define PORTF_PIN5CTRL _SFR_MEM8(0x06B5) +#define PORTF_PIN6CTRL _SFR_MEM8(0x06B6) +#define PORTF_PIN7CTRL _SFR_MEM8(0x06B7) + +/* PORT - I/O Ports */ +#define PORTR_DIR _SFR_MEM8(0x07E0) +#define PORTR_DIRSET _SFR_MEM8(0x07E1) +#define PORTR_DIRCLR _SFR_MEM8(0x07E2) +#define PORTR_DIRTGL _SFR_MEM8(0x07E3) +#define PORTR_OUT _SFR_MEM8(0x07E4) +#define PORTR_OUTSET _SFR_MEM8(0x07E5) +#define PORTR_OUTCLR _SFR_MEM8(0x07E6) +#define PORTR_OUTTGL _SFR_MEM8(0x07E7) +#define PORTR_IN _SFR_MEM8(0x07E8) +#define PORTR_INTCTRL _SFR_MEM8(0x07E9) +#define PORTR_INT0MASK _SFR_MEM8(0x07EA) +#define PORTR_INT1MASK _SFR_MEM8(0x07EB) +#define PORTR_INTFLAGS _SFR_MEM8(0x07EC) +#define PORTR_REMAP _SFR_MEM8(0x07EE) +#define PORTR_PIN0CTRL _SFR_MEM8(0x07F0) +#define PORTR_PIN1CTRL _SFR_MEM8(0x07F1) +#define PORTR_PIN2CTRL _SFR_MEM8(0x07F2) +#define PORTR_PIN3CTRL _SFR_MEM8(0x07F3) +#define PORTR_PIN4CTRL _SFR_MEM8(0x07F4) +#define PORTR_PIN5CTRL _SFR_MEM8(0x07F5) +#define PORTR_PIN6CTRL _SFR_MEM8(0x07F6) +#define PORTR_PIN7CTRL _SFR_MEM8(0x07F7) + +/* TC0 - 16-bit Timer/Counter 0 */ +#define TCC0_CTRLA _SFR_MEM8(0x0800) +#define TCC0_CTRLB _SFR_MEM8(0x0801) +#define TCC0_CTRLC _SFR_MEM8(0x0802) +#define TCC0_CTRLD _SFR_MEM8(0x0803) +#define TCC0_CTRLE _SFR_MEM8(0x0804) +#define TCC0_INTCTRLA _SFR_MEM8(0x0806) +#define TCC0_INTCTRLB _SFR_MEM8(0x0807) +#define TCC0_CTRLFCLR _SFR_MEM8(0x0808) +#define TCC0_CTRLFSET _SFR_MEM8(0x0809) +#define TCC0_CTRLGCLR _SFR_MEM8(0x080A) +#define TCC0_CTRLGSET _SFR_MEM8(0x080B) +#define TCC0_INTFLAGS _SFR_MEM8(0x080C) +#define TCC0_TEMP _SFR_MEM8(0x080F) +#define TCC0_CNT _SFR_MEM16(0x0820) +#define TCC0_PER _SFR_MEM16(0x0826) +#define TCC0_CCA _SFR_MEM16(0x0828) +#define TCC0_CCB _SFR_MEM16(0x082A) +#define TCC0_CCC _SFR_MEM16(0x082C) +#define TCC0_CCD _SFR_MEM16(0x082E) +#define TCC0_PERBUF _SFR_MEM16(0x0836) +#define TCC0_CCABUF _SFR_MEM16(0x0838) +#define TCC0_CCBBUF _SFR_MEM16(0x083A) +#define TCC0_CCCBUF _SFR_MEM16(0x083C) +#define TCC0_CCDBUF _SFR_MEM16(0x083E) + +/* TC2 - 16-bit Timer/Counter type 2 */ +#define TCC2_CTRLA _SFR_MEM8(0x0800) +#define TCC2_CTRLB _SFR_MEM8(0x0801) +#define TCC2_CTRLC _SFR_MEM8(0x0802) +#define TCC2_CTRLE _SFR_MEM8(0x0804) +#define TCC2_INTCTRLA _SFR_MEM8(0x0806) +#define TCC2_INTCTRLB _SFR_MEM8(0x0807) +#define TCC2_CTRLF _SFR_MEM8(0x0809) +#define TCC2_INTFLAGS _SFR_MEM8(0x080C) +#define TCC2_LCNT _SFR_MEM8(0x0820) +#define TCC2_HCNT _SFR_MEM8(0x0821) +#define TCC2_LPER _SFR_MEM8(0x0826) +#define TCC2_HPER _SFR_MEM8(0x0827) +#define TCC2_LCMPA _SFR_MEM8(0x0828) +#define TCC2_HCMPA _SFR_MEM8(0x0829) +#define TCC2_LCMPB _SFR_MEM8(0x082A) +#define TCC2_HCMPB _SFR_MEM8(0x082B) +#define TCC2_LCMPC _SFR_MEM8(0x082C) +#define TCC2_HCMPC _SFR_MEM8(0x082D) +#define TCC2_LCMPD _SFR_MEM8(0x082E) +#define TCC2_HCMPD _SFR_MEM8(0x082F) + +/* TC1 - 16-bit Timer/Counter 1 */ +#define TCC1_CTRLA _SFR_MEM8(0x0840) +#define TCC1_CTRLB _SFR_MEM8(0x0841) +#define TCC1_CTRLC _SFR_MEM8(0x0842) +#define TCC1_CTRLD _SFR_MEM8(0x0843) +#define TCC1_CTRLE _SFR_MEM8(0x0844) +#define TCC1_INTCTRLA _SFR_MEM8(0x0846) +#define TCC1_INTCTRLB _SFR_MEM8(0x0847) +#define TCC1_CTRLFCLR _SFR_MEM8(0x0848) +#define TCC1_CTRLFSET _SFR_MEM8(0x0849) +#define TCC1_CTRLGCLR _SFR_MEM8(0x084A) +#define TCC1_CTRLGSET _SFR_MEM8(0x084B) +#define TCC1_INTFLAGS _SFR_MEM8(0x084C) +#define TCC1_TEMP _SFR_MEM8(0x084F) +#define TCC1_CNT _SFR_MEM16(0x0860) +#define TCC1_PER _SFR_MEM16(0x0866) +#define TCC1_CCA _SFR_MEM16(0x0868) +#define TCC1_CCB _SFR_MEM16(0x086A) +#define TCC1_PERBUF _SFR_MEM16(0x0876) +#define TCC1_CCABUF _SFR_MEM16(0x0878) +#define TCC1_CCBBUF _SFR_MEM16(0x087A) + +/* AWEX - Advanced Waveform Extension */ +#define AWEXC_CTRL _SFR_MEM8(0x0880) +#define AWEXC_FDEMASK _SFR_MEM8(0x0882) +#define AWEXC_FDCTRL _SFR_MEM8(0x0883) +#define AWEXC_STATUS _SFR_MEM8(0x0884) +#define AWEXC_STATUSSET _SFR_MEM8(0x0885) +#define AWEXC_DTBOTH _SFR_MEM8(0x0886) +#define AWEXC_DTBOTHBUF _SFR_MEM8(0x0887) +#define AWEXC_DTLS _SFR_MEM8(0x0888) +#define AWEXC_DTHS _SFR_MEM8(0x0889) +#define AWEXC_DTLSBUF _SFR_MEM8(0x088A) +#define AWEXC_DTHSBUF _SFR_MEM8(0x088B) +#define AWEXC_OUTOVEN _SFR_MEM8(0x088C) + +/* HIRES - High-Resolution Extension */ +#define HIRESC_CTRLA _SFR_MEM8(0x0890) + +/* USART - Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTC0_DATA _SFR_MEM8(0x08A0) +#define USARTC0_STATUS _SFR_MEM8(0x08A1) +#define USARTC0_CTRLA _SFR_MEM8(0x08A3) +#define USARTC0_CTRLB _SFR_MEM8(0x08A4) +#define USARTC0_CTRLC _SFR_MEM8(0x08A5) +#define USARTC0_BAUDCTRLA _SFR_MEM8(0x08A6) +#define USARTC0_BAUDCTRLB _SFR_MEM8(0x08A7) + +/* USART - Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTC1_DATA _SFR_MEM8(0x08B0) +#define USARTC1_STATUS _SFR_MEM8(0x08B1) +#define USARTC1_CTRLA _SFR_MEM8(0x08B3) +#define USARTC1_CTRLB _SFR_MEM8(0x08B4) +#define USARTC1_CTRLC _SFR_MEM8(0x08B5) +#define USARTC1_BAUDCTRLA _SFR_MEM8(0x08B6) +#define USARTC1_BAUDCTRLB _SFR_MEM8(0x08B7) + +/* SPI - Serial Peripheral Interface */ +#define SPIC_CTRL _SFR_MEM8(0x08C0) +#define SPIC_INTCTRL _SFR_MEM8(0x08C1) +#define SPIC_STATUS _SFR_MEM8(0x08C2) +#define SPIC_DATA _SFR_MEM8(0x08C3) + +/* IRCOM - IR Communication Module */ +#define IRCOM_CTRL _SFR_MEM8(0x08F8) +#define IRCOM_TXPLCTRL _SFR_MEM8(0x08F9) +#define IRCOM_RXPLCTRL _SFR_MEM8(0x08FA) + +/* TC0 - 16-bit Timer/Counter 0 */ +#define TCD0_CTRLA _SFR_MEM8(0x0900) +#define TCD0_CTRLB _SFR_MEM8(0x0901) +#define TCD0_CTRLC _SFR_MEM8(0x0902) +#define TCD0_CTRLD _SFR_MEM8(0x0903) +#define TCD0_CTRLE _SFR_MEM8(0x0904) +#define TCD0_INTCTRLA _SFR_MEM8(0x0906) +#define TCD0_INTCTRLB _SFR_MEM8(0x0907) +#define TCD0_CTRLFCLR _SFR_MEM8(0x0908) +#define TCD0_CTRLFSET _SFR_MEM8(0x0909) +#define TCD0_CTRLGCLR _SFR_MEM8(0x090A) +#define TCD0_CTRLGSET _SFR_MEM8(0x090B) +#define TCD0_INTFLAGS _SFR_MEM8(0x090C) +#define TCD0_TEMP _SFR_MEM8(0x090F) +#define TCD0_CNT _SFR_MEM16(0x0920) +#define TCD0_PER _SFR_MEM16(0x0926) +#define TCD0_CCA _SFR_MEM16(0x0928) +#define TCD0_CCB _SFR_MEM16(0x092A) +#define TCD0_CCC _SFR_MEM16(0x092C) +#define TCD0_CCD _SFR_MEM16(0x092E) +#define TCD0_PERBUF _SFR_MEM16(0x0936) +#define TCD0_CCABUF _SFR_MEM16(0x0938) +#define TCD0_CCBBUF _SFR_MEM16(0x093A) +#define TCD0_CCCBUF _SFR_MEM16(0x093C) +#define TCD0_CCDBUF _SFR_MEM16(0x093E) + +/* TC2 - 16-bit Timer/Counter type 2 */ +#define TCD2_CTRLA _SFR_MEM8(0x0900) +#define TCD2_CTRLB _SFR_MEM8(0x0901) +#define TCD2_CTRLC _SFR_MEM8(0x0902) +#define TCD2_CTRLE _SFR_MEM8(0x0904) +#define TCD2_INTCTRLA _SFR_MEM8(0x0906) +#define TCD2_INTCTRLB _SFR_MEM8(0x0907) +#define TCD2_CTRLF _SFR_MEM8(0x0909) +#define TCD2_INTFLAGS _SFR_MEM8(0x090C) +#define TCD2_LCNT _SFR_MEM8(0x0920) +#define TCD2_HCNT _SFR_MEM8(0x0921) +#define TCD2_LPER _SFR_MEM8(0x0926) +#define TCD2_HPER _SFR_MEM8(0x0927) +#define TCD2_LCMPA _SFR_MEM8(0x0928) +#define TCD2_HCMPA _SFR_MEM8(0x0929) +#define TCD2_LCMPB _SFR_MEM8(0x092A) +#define TCD2_HCMPB _SFR_MEM8(0x092B) +#define TCD2_LCMPC _SFR_MEM8(0x092C) +#define TCD2_HCMPC _SFR_MEM8(0x092D) +#define TCD2_LCMPD _SFR_MEM8(0x092E) +#define TCD2_HCMPD _SFR_MEM8(0x092F) + +/* TC1 - 16-bit Timer/Counter 1 */ +#define TCD1_CTRLA _SFR_MEM8(0x0940) +#define TCD1_CTRLB _SFR_MEM8(0x0941) +#define TCD1_CTRLC _SFR_MEM8(0x0942) +#define TCD1_CTRLD _SFR_MEM8(0x0943) +#define TCD1_CTRLE _SFR_MEM8(0x0944) +#define TCD1_INTCTRLA _SFR_MEM8(0x0946) +#define TCD1_INTCTRLB _SFR_MEM8(0x0947) +#define TCD1_CTRLFCLR _SFR_MEM8(0x0948) +#define TCD1_CTRLFSET _SFR_MEM8(0x0949) +#define TCD1_CTRLGCLR _SFR_MEM8(0x094A) +#define TCD1_CTRLGSET _SFR_MEM8(0x094B) +#define TCD1_INTFLAGS _SFR_MEM8(0x094C) +#define TCD1_TEMP _SFR_MEM8(0x094F) +#define TCD1_CNT _SFR_MEM16(0x0960) +#define TCD1_PER _SFR_MEM16(0x0966) +#define TCD1_CCA _SFR_MEM16(0x0968) +#define TCD1_CCB _SFR_MEM16(0x096A) +#define TCD1_PERBUF _SFR_MEM16(0x0976) +#define TCD1_CCABUF _SFR_MEM16(0x0978) +#define TCD1_CCBBUF _SFR_MEM16(0x097A) + +/* HIRES - High-Resolution Extension */ +#define HIRESD_CTRLA _SFR_MEM8(0x0990) + +/* USART - Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTD0_DATA _SFR_MEM8(0x09A0) +#define USARTD0_STATUS _SFR_MEM8(0x09A1) +#define USARTD0_CTRLA _SFR_MEM8(0x09A3) +#define USARTD0_CTRLB _SFR_MEM8(0x09A4) +#define USARTD0_CTRLC _SFR_MEM8(0x09A5) +#define USARTD0_BAUDCTRLA _SFR_MEM8(0x09A6) +#define USARTD0_BAUDCTRLB _SFR_MEM8(0x09A7) + +/* USART - Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTD1_DATA _SFR_MEM8(0x09B0) +#define USARTD1_STATUS _SFR_MEM8(0x09B1) +#define USARTD1_CTRLA _SFR_MEM8(0x09B3) +#define USARTD1_CTRLB _SFR_MEM8(0x09B4) +#define USARTD1_CTRLC _SFR_MEM8(0x09B5) +#define USARTD1_BAUDCTRLA _SFR_MEM8(0x09B6) +#define USARTD1_BAUDCTRLB _SFR_MEM8(0x09B7) + +/* SPI - Serial Peripheral Interface */ +#define SPID_CTRL _SFR_MEM8(0x09C0) +#define SPID_INTCTRL _SFR_MEM8(0x09C1) +#define SPID_STATUS _SFR_MEM8(0x09C2) +#define SPID_DATA _SFR_MEM8(0x09C3) + +/* TC0 - 16-bit Timer/Counter 0 */ +#define TCE0_CTRLA _SFR_MEM8(0x0A00) +#define TCE0_CTRLB _SFR_MEM8(0x0A01) +#define TCE0_CTRLC _SFR_MEM8(0x0A02) +#define TCE0_CTRLD _SFR_MEM8(0x0A03) +#define TCE0_CTRLE _SFR_MEM8(0x0A04) +#define TCE0_INTCTRLA _SFR_MEM8(0x0A06) +#define TCE0_INTCTRLB _SFR_MEM8(0x0A07) +#define TCE0_CTRLFCLR _SFR_MEM8(0x0A08) +#define TCE0_CTRLFSET _SFR_MEM8(0x0A09) +#define TCE0_CTRLGCLR _SFR_MEM8(0x0A0A) +#define TCE0_CTRLGSET _SFR_MEM8(0x0A0B) +#define TCE0_INTFLAGS _SFR_MEM8(0x0A0C) +#define TCE0_TEMP _SFR_MEM8(0x0A0F) +#define TCE0_CNT _SFR_MEM16(0x0A20) +#define TCE0_PER _SFR_MEM16(0x0A26) +#define TCE0_CCA _SFR_MEM16(0x0A28) +#define TCE0_CCB _SFR_MEM16(0x0A2A) +#define TCE0_CCC _SFR_MEM16(0x0A2C) +#define TCE0_CCD _SFR_MEM16(0x0A2E) +#define TCE0_PERBUF _SFR_MEM16(0x0A36) +#define TCE0_CCABUF _SFR_MEM16(0x0A38) +#define TCE0_CCBBUF _SFR_MEM16(0x0A3A) +#define TCE0_CCCBUF _SFR_MEM16(0x0A3C) +#define TCE0_CCDBUF _SFR_MEM16(0x0A3E) + +/* TC2 - 16-bit Timer/Counter type 2 */ +#define TCE2_CTRLA _SFR_MEM8(0x0A00) +#define TCE2_CTRLB _SFR_MEM8(0x0A01) +#define TCE2_CTRLC _SFR_MEM8(0x0A02) +#define TCE2_CTRLE _SFR_MEM8(0x0A04) +#define TCE2_INTCTRLA _SFR_MEM8(0x0A06) +#define TCE2_INTCTRLB _SFR_MEM8(0x0A07) +#define TCE2_CTRLF _SFR_MEM8(0x0A09) +#define TCE2_INTFLAGS _SFR_MEM8(0x0A0C) +#define TCE2_LCNT _SFR_MEM8(0x0A20) +#define TCE2_HCNT _SFR_MEM8(0x0A21) +#define TCE2_LPER _SFR_MEM8(0x0A26) +#define TCE2_HPER _SFR_MEM8(0x0A27) +#define TCE2_LCMPA _SFR_MEM8(0x0A28) +#define TCE2_HCMPA _SFR_MEM8(0x0A29) +#define TCE2_LCMPB _SFR_MEM8(0x0A2A) +#define TCE2_HCMPB _SFR_MEM8(0x0A2B) +#define TCE2_LCMPC _SFR_MEM8(0x0A2C) +#define TCE2_HCMPC _SFR_MEM8(0x0A2D) +#define TCE2_LCMPD _SFR_MEM8(0x0A2E) +#define TCE2_HCMPD _SFR_MEM8(0x0A2F) + +/* TC1 - 16-bit Timer/Counter 1 */ +#define TCE1_CTRLA _SFR_MEM8(0x0A40) +#define TCE1_CTRLB _SFR_MEM8(0x0A41) +#define TCE1_CTRLC _SFR_MEM8(0x0A42) +#define TCE1_CTRLD _SFR_MEM8(0x0A43) +#define TCE1_CTRLE _SFR_MEM8(0x0A44) +#define TCE1_INTCTRLA _SFR_MEM8(0x0A46) +#define TCE1_INTCTRLB _SFR_MEM8(0x0A47) +#define TCE1_CTRLFCLR _SFR_MEM8(0x0A48) +#define TCE1_CTRLFSET _SFR_MEM8(0x0A49) +#define TCE1_CTRLGCLR _SFR_MEM8(0x0A4A) +#define TCE1_CTRLGSET _SFR_MEM8(0x0A4B) +#define TCE1_INTFLAGS _SFR_MEM8(0x0A4C) +#define TCE1_TEMP _SFR_MEM8(0x0A4F) +#define TCE1_CNT _SFR_MEM16(0x0A60) +#define TCE1_PER _SFR_MEM16(0x0A66) +#define TCE1_CCA _SFR_MEM16(0x0A68) +#define TCE1_CCB _SFR_MEM16(0x0A6A) +#define TCE1_PERBUF _SFR_MEM16(0x0A76) +#define TCE1_CCABUF _SFR_MEM16(0x0A78) +#define TCE1_CCBBUF _SFR_MEM16(0x0A7A) + +/* AWEX - Advanced Waveform Extension */ +#define AWEXE_CTRL _SFR_MEM8(0x0A80) +#define AWEXE_FDEMASK _SFR_MEM8(0x0A82) +#define AWEXE_FDCTRL _SFR_MEM8(0x0A83) +#define AWEXE_STATUS _SFR_MEM8(0x0A84) +#define AWEXE_STATUSSET _SFR_MEM8(0x0A85) +#define AWEXE_DTBOTH _SFR_MEM8(0x0A86) +#define AWEXE_DTBOTHBUF _SFR_MEM8(0x0A87) +#define AWEXE_DTLS _SFR_MEM8(0x0A88) +#define AWEXE_DTHS _SFR_MEM8(0x0A89) +#define AWEXE_DTLSBUF _SFR_MEM8(0x0A8A) +#define AWEXE_DTHSBUF _SFR_MEM8(0x0A8B) +#define AWEXE_OUTOVEN _SFR_MEM8(0x0A8C) + +/* HIRES - High-Resolution Extension */ +#define HIRESE_CTRLA _SFR_MEM8(0x0A90) + +/* USART - Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTE0_DATA _SFR_MEM8(0x0AA0) +#define USARTE0_STATUS _SFR_MEM8(0x0AA1) +#define USARTE0_CTRLA _SFR_MEM8(0x0AA3) +#define USARTE0_CTRLB _SFR_MEM8(0x0AA4) +#define USARTE0_CTRLC _SFR_MEM8(0x0AA5) +#define USARTE0_BAUDCTRLA _SFR_MEM8(0x0AA6) +#define USARTE0_BAUDCTRLB _SFR_MEM8(0x0AA7) + +/* USART - Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTE1_DATA _SFR_MEM8(0x0AB0) +#define USARTE1_STATUS _SFR_MEM8(0x0AB1) +#define USARTE1_CTRLA _SFR_MEM8(0x0AB3) +#define USARTE1_CTRLB _SFR_MEM8(0x0AB4) +#define USARTE1_CTRLC _SFR_MEM8(0x0AB5) +#define USARTE1_BAUDCTRLA _SFR_MEM8(0x0AB6) +#define USARTE1_BAUDCTRLB _SFR_MEM8(0x0AB7) + +/* SPI - Serial Peripheral Interface */ +#define SPIE_CTRL _SFR_MEM8(0x0AC0) +#define SPIE_INTCTRL _SFR_MEM8(0x0AC1) +#define SPIE_STATUS _SFR_MEM8(0x0AC2) +#define SPIE_DATA _SFR_MEM8(0x0AC3) + +/* TC0 - 16-bit Timer/Counter 0 */ +#define TCF0_CTRLA _SFR_MEM8(0x0B00) +#define TCF0_CTRLB _SFR_MEM8(0x0B01) +#define TCF0_CTRLC _SFR_MEM8(0x0B02) +#define TCF0_CTRLD _SFR_MEM8(0x0B03) +#define TCF0_CTRLE _SFR_MEM8(0x0B04) +#define TCF0_INTCTRLA _SFR_MEM8(0x0B06) +#define TCF0_INTCTRLB _SFR_MEM8(0x0B07) +#define TCF0_CTRLFCLR _SFR_MEM8(0x0B08) +#define TCF0_CTRLFSET _SFR_MEM8(0x0B09) +#define TCF0_CTRLGCLR _SFR_MEM8(0x0B0A) +#define TCF0_CTRLGSET _SFR_MEM8(0x0B0B) +#define TCF0_INTFLAGS _SFR_MEM8(0x0B0C) +#define TCF0_TEMP _SFR_MEM8(0x0B0F) +#define TCF0_CNT _SFR_MEM16(0x0B20) +#define TCF0_PER _SFR_MEM16(0x0B26) +#define TCF0_CCA _SFR_MEM16(0x0B28) +#define TCF0_CCB _SFR_MEM16(0x0B2A) +#define TCF0_CCC _SFR_MEM16(0x0B2C) +#define TCF0_CCD _SFR_MEM16(0x0B2E) +#define TCF0_PERBUF _SFR_MEM16(0x0B36) +#define TCF0_CCABUF _SFR_MEM16(0x0B38) +#define TCF0_CCBBUF _SFR_MEM16(0x0B3A) +#define TCF0_CCCBUF _SFR_MEM16(0x0B3C) +#define TCF0_CCDBUF _SFR_MEM16(0x0B3E) + +/* TC2 - 16-bit Timer/Counter type 2 */ +#define TCF2_CTRLA _SFR_MEM8(0x0B00) +#define TCF2_CTRLB _SFR_MEM8(0x0B01) +#define TCF2_CTRLC _SFR_MEM8(0x0B02) +#define TCF2_CTRLE _SFR_MEM8(0x0B04) +#define TCF2_INTCTRLA _SFR_MEM8(0x0B06) +#define TCF2_INTCTRLB _SFR_MEM8(0x0B07) +#define TCF2_CTRLF _SFR_MEM8(0x0B09) +#define TCF2_INTFLAGS _SFR_MEM8(0x0B0C) +#define TCF2_LCNT _SFR_MEM8(0x0B20) +#define TCF2_HCNT _SFR_MEM8(0x0B21) +#define TCF2_LPER _SFR_MEM8(0x0B26) +#define TCF2_HPER _SFR_MEM8(0x0B27) +#define TCF2_LCMPA _SFR_MEM8(0x0B28) +#define TCF2_HCMPA _SFR_MEM8(0x0B29) +#define TCF2_LCMPB _SFR_MEM8(0x0B2A) +#define TCF2_HCMPB _SFR_MEM8(0x0B2B) +#define TCF2_LCMPC _SFR_MEM8(0x0B2C) +#define TCF2_HCMPC _SFR_MEM8(0x0B2D) +#define TCF2_LCMPD _SFR_MEM8(0x0B2E) +#define TCF2_HCMPD _SFR_MEM8(0x0B2F) + +/* HIRES - High-Resolution Extension */ +#define HIRESF_CTRLA _SFR_MEM8(0x0B90) + +/* USART - Universal Synchronous/Asynchronous Receiver/Transmitter */ +#define USARTF0_DATA _SFR_MEM8(0x0BA0) +#define USARTF0_STATUS _SFR_MEM8(0x0BA1) +#define USARTF0_CTRLA _SFR_MEM8(0x0BA3) +#define USARTF0_CTRLB _SFR_MEM8(0x0BA4) +#define USARTF0_CTRLC _SFR_MEM8(0x0BA5) +#define USARTF0_BAUDCTRLA _SFR_MEM8(0x0BA6) +#define USARTF0_BAUDCTRLB _SFR_MEM8(0x0BA7) + + + +/*================== Bitfield Definitions ================== */ + +/* VPORT - Virtual Ports */ +/* VPORT.INTFLAGS bit masks and bit positions */ +#define VPORT_INT1IF_bm 0x02 /* Port Interrupt 1 Flag bit mask. */ +#define VPORT_INT1IF_bp 1 /* Port Interrupt 1 Flag bit position. */ + +#define VPORT_INT0IF_bm 0x01 /* Port Interrupt 0 Flag bit mask. */ +#define VPORT_INT0IF_bp 0 /* Port Interrupt 0 Flag bit position. */ + +/* XOCD - On-Chip Debug System */ +/* OCD.OCDR0 bit masks and bit positions */ +#define OCD_OCDRD_gm 0xFF /* OCDR Dirty group mask. */ +#define OCD_OCDRD_gp 0 /* OCDR Dirty group position. */ +#define OCD_OCDRD0_bm (1<<0) /* OCDR Dirty bit 0 mask. */ +#define OCD_OCDRD0_bp 0 /* OCDR Dirty bit 0 position. */ +#define OCD_OCDRD1_bm (1<<1) /* OCDR Dirty bit 1 mask. */ +#define OCD_OCDRD1_bp 1 /* OCDR Dirty bit 1 position. */ +#define OCD_OCDRD2_bm (1<<2) /* OCDR Dirty bit 2 mask. */ +#define OCD_OCDRD2_bp 2 /* OCDR Dirty bit 2 position. */ +#define OCD_OCDRD3_bm (1<<3) /* OCDR Dirty bit 3 mask. */ +#define OCD_OCDRD3_bp 3 /* OCDR Dirty bit 3 position. */ +#define OCD_OCDRD4_bm (1<<4) /* OCDR Dirty bit 4 mask. */ +#define OCD_OCDRD4_bp 4 /* OCDR Dirty bit 4 position. */ +#define OCD_OCDRD5_bm (1<<5) /* OCDR Dirty bit 5 mask. */ +#define OCD_OCDRD5_bp 5 /* OCDR Dirty bit 5 position. */ +#define OCD_OCDRD6_bm (1<<6) /* OCDR Dirty bit 6 mask. */ +#define OCD_OCDRD6_bp 6 /* OCDR Dirty bit 6 position. */ +#define OCD_OCDRD7_bm (1<<7) /* OCDR Dirty bit 7 mask. */ +#define OCD_OCDRD7_bp 7 /* OCDR Dirty bit 7 position. */ + +/* OCD.OCDR1 bit masks and bit positions */ +/* OCD_OCDRD Predefined. */ +/* OCD_OCDRD Predefined. */ + +/* CPU - CPU */ +/* CPU.CCP bit masks and bit positions */ +#define CPU_CCP_gm 0xFF /* CCP signature group mask. */ +#define CPU_CCP_gp 0 /* CCP signature group position. */ +#define CPU_CCP0_bm (1<<0) /* CCP signature bit 0 mask. */ +#define CPU_CCP0_bp 0 /* CCP signature bit 0 position. */ +#define CPU_CCP1_bm (1<<1) /* CCP signature bit 1 mask. */ +#define CPU_CCP1_bp 1 /* CCP signature bit 1 position. */ +#define CPU_CCP2_bm (1<<2) /* CCP signature bit 2 mask. */ +#define CPU_CCP2_bp 2 /* CCP signature bit 2 position. */ +#define CPU_CCP3_bm (1<<3) /* CCP signature bit 3 mask. */ +#define CPU_CCP3_bp 3 /* CCP signature bit 3 position. */ +#define CPU_CCP4_bm (1<<4) /* CCP signature bit 4 mask. */ +#define CPU_CCP4_bp 4 /* CCP signature bit 4 position. */ +#define CPU_CCP5_bm (1<<5) /* CCP signature bit 5 mask. */ +#define CPU_CCP5_bp 5 /* CCP signature bit 5 position. */ +#define CPU_CCP6_bm (1<<6) /* CCP signature bit 6 mask. */ +#define CPU_CCP6_bp 6 /* CCP signature bit 6 position. */ +#define CPU_CCP7_bm (1<<7) /* CCP signature bit 7 mask. */ +#define CPU_CCP7_bp 7 /* CCP signature bit 7 position. */ + +/* CPU.SREG bit masks and bit positions */ +#define CPU_I_bm 0x80 /* Global Interrupt Enable Flag bit mask. */ +#define CPU_I_bp 7 /* Global Interrupt Enable Flag bit position. */ + +#define CPU_T_bm 0x40 /* Transfer Bit bit mask. */ +#define CPU_T_bp 6 /* Transfer Bit bit position. */ + +#define CPU_H_bm 0x20 /* Half Carry Flag bit mask. */ +#define CPU_H_bp 5 /* Half Carry Flag bit position. */ + +#define CPU_S_bm 0x10 /* N Exclusive Or V Flag bit mask. */ +#define CPU_S_bp 4 /* N Exclusive Or V Flag bit position. */ + +#define CPU_V_bm 0x08 /* Two's Complement Overflow Flag bit mask. */ +#define CPU_V_bp 3 /* Two's Complement Overflow Flag bit position. */ + +#define CPU_N_bm 0x04 /* Negative Flag bit mask. */ +#define CPU_N_bp 2 /* Negative Flag bit position. */ + +#define CPU_Z_bm 0x02 /* Zero Flag bit mask. */ +#define CPU_Z_bp 1 /* Zero Flag bit position. */ + +#define CPU_C_bm 0x01 /* Carry Flag bit mask. */ +#define CPU_C_bp 0 /* Carry Flag bit position. */ + +/* CLK - Clock System */ +/* CLK.CTRL bit masks and bit positions */ +#define CLK_SCLKSEL_gm 0x07 /* System Clock Selection group mask. */ +#define CLK_SCLKSEL_gp 0 /* System Clock Selection group position. */ +#define CLK_SCLKSEL0_bm (1<<0) /* System Clock Selection bit 0 mask. */ +#define CLK_SCLKSEL0_bp 0 /* System Clock Selection bit 0 position. */ +#define CLK_SCLKSEL1_bm (1<<1) /* System Clock Selection bit 1 mask. */ +#define CLK_SCLKSEL1_bp 1 /* System Clock Selection bit 1 position. */ +#define CLK_SCLKSEL2_bm (1<<2) /* System Clock Selection bit 2 mask. */ +#define CLK_SCLKSEL2_bp 2 /* System Clock Selection bit 2 position. */ + +/* CLK.PSCTRL bit masks and bit positions */ +#define CLK_PSADIV_gm 0x7C /* Prescaler A Division Factor group mask. */ +#define CLK_PSADIV_gp 2 /* Prescaler A Division Factor group position. */ +#define CLK_PSADIV0_bm (1<<2) /* Prescaler A Division Factor bit 0 mask. */ +#define CLK_PSADIV0_bp 2 /* Prescaler A Division Factor bit 0 position. */ +#define CLK_PSADIV1_bm (1<<3) /* Prescaler A Division Factor bit 1 mask. */ +#define CLK_PSADIV1_bp 3 /* Prescaler A Division Factor bit 1 position. */ +#define CLK_PSADIV2_bm (1<<4) /* Prescaler A Division Factor bit 2 mask. */ +#define CLK_PSADIV2_bp 4 /* Prescaler A Division Factor bit 2 position. */ +#define CLK_PSADIV3_bm (1<<5) /* Prescaler A Division Factor bit 3 mask. */ +#define CLK_PSADIV3_bp 5 /* Prescaler A Division Factor bit 3 position. */ +#define CLK_PSADIV4_bm (1<<6) /* Prescaler A Division Factor bit 4 mask. */ +#define CLK_PSADIV4_bp 6 /* Prescaler A Division Factor bit 4 position. */ + +#define CLK_PSBCDIV_gm 0x03 /* Prescaler B and C Division factor group mask. */ +#define CLK_PSBCDIV_gp 0 /* Prescaler B and C Division factor group position. */ +#define CLK_PSBCDIV0_bm (1<<0) /* Prescaler B and C Division factor bit 0 mask. */ +#define CLK_PSBCDIV0_bp 0 /* Prescaler B and C Division factor bit 0 position. */ +#define CLK_PSBCDIV1_bm (1<<1) /* Prescaler B and C Division factor bit 1 mask. */ +#define CLK_PSBCDIV1_bp 1 /* Prescaler B and C Division factor bit 1 position. */ + +/* CLK.LOCK bit masks and bit positions */ +#define CLK_LOCK_bm 0x01 /* Clock System Lock bit mask. */ +#define CLK_LOCK_bp 0 /* Clock System Lock bit position. */ + +/* CLK.RTCCTRL bit masks and bit positions */ +#define CLK_RTCSRC_gm 0x0E /* Clock Source group mask. */ +#define CLK_RTCSRC_gp 1 /* Clock Source group position. */ +#define CLK_RTCSRC0_bm (1<<1) /* Clock Source bit 0 mask. */ +#define CLK_RTCSRC0_bp 1 /* Clock Source bit 0 position. */ +#define CLK_RTCSRC1_bm (1<<2) /* Clock Source bit 1 mask. */ +#define CLK_RTCSRC1_bp 2 /* Clock Source bit 1 position. */ +#define CLK_RTCSRC2_bm (1<<3) /* Clock Source bit 2 mask. */ +#define CLK_RTCSRC2_bp 3 /* Clock Source bit 2 position. */ + +#define CLK_RTCEN_bm 0x01 /* Clock Source Enable bit mask. */ +#define CLK_RTCEN_bp 0 /* Clock Source Enable bit position. */ + +/* CLK.USBCTRL bit masks and bit positions */ +#define CLK_USBPSDIV_gm 0x38 /* Prescaler Division Factor group mask. */ +#define CLK_USBPSDIV_gp 3 /* Prescaler Division Factor group position. */ +#define CLK_USBPSDIV0_bm (1<<3) /* Prescaler Division Factor bit 0 mask. */ +#define CLK_USBPSDIV0_bp 3 /* Prescaler Division Factor bit 0 position. */ +#define CLK_USBPSDIV1_bm (1<<4) /* Prescaler Division Factor bit 1 mask. */ +#define CLK_USBPSDIV1_bp 4 /* Prescaler Division Factor bit 1 position. */ +#define CLK_USBPSDIV2_bm (1<<5) /* Prescaler Division Factor bit 2 mask. */ +#define CLK_USBPSDIV2_bp 5 /* Prescaler Division Factor bit 2 position. */ + +#define CLK_USBSRC_gm 0x06 /* Clock Source group mask. */ +#define CLK_USBSRC_gp 1 /* Clock Source group position. */ +#define CLK_USBSRC0_bm (1<<1) /* Clock Source bit 0 mask. */ +#define CLK_USBSRC0_bp 1 /* Clock Source bit 0 position. */ +#define CLK_USBSRC1_bm (1<<2) /* Clock Source bit 1 mask. */ +#define CLK_USBSRC1_bp 2 /* Clock Source bit 1 position. */ + +#define CLK_USBSEN_bm 0x01 /* Clock Source Enable bit mask. */ +#define CLK_USBSEN_bp 0 /* Clock Source Enable bit position. */ + +/* PR.PRGEN bit masks and bit positions */ +#define PR_USB_bm 0x40 /* USB bit mask. */ +#define PR_USB_bp 6 /* USB bit position. */ + +#define PR_AES_bm 0x10 /* AES bit mask. */ +#define PR_AES_bp 4 /* AES bit position. */ + +#define PR_EBI_bm 0x08 /* External Bus Interface bit mask. */ +#define PR_EBI_bp 3 /* External Bus Interface bit position. */ + +#define PR_RTC_bm 0x04 /* Real-time Counter bit mask. */ +#define PR_RTC_bp 2 /* Real-time Counter bit position. */ + +#define PR_EVSYS_bm 0x02 /* Event System bit mask. */ +#define PR_EVSYS_bp 1 /* Event System bit position. */ + +#define PR_DMA_bm 0x01 /* DMA-Controller bit mask. */ +#define PR_DMA_bp 0 /* DMA-Controller bit position. */ + +/* PR.PRPA bit masks and bit positions */ +#define PR_DAC_bm 0x04 /* Port A DAC bit mask. */ +#define PR_DAC_bp 2 /* Port A DAC bit position. */ + +#define PR_ADC_bm 0x02 /* Port A ADC bit mask. */ +#define PR_ADC_bp 1 /* Port A ADC bit position. */ + +#define PR_AC_bm 0x01 /* Port A Analog Comparator bit mask. */ +#define PR_AC_bp 0 /* Port A Analog Comparator bit position. */ + +/* PR.PRPB bit masks and bit positions */ +/* PR_DAC Predefined. */ +/* PR_DAC Predefined. */ + +/* PR_ADC Predefined. */ +/* PR_ADC Predefined. */ + +/* PR_AC Predefined. */ +/* PR_AC Predefined. */ + +/* PR.PRPC bit masks and bit positions */ +#define PR_TWI_bm 0x40 /* Port C Two-wire Interface bit mask. */ +#define PR_TWI_bp 6 /* Port C Two-wire Interface bit position. */ + +#define PR_USART1_bm 0x20 /* Port C USART1 bit mask. */ +#define PR_USART1_bp 5 /* Port C USART1 bit position. */ + +#define PR_USART0_bm 0x10 /* Port C USART0 bit mask. */ +#define PR_USART0_bp 4 /* Port C USART0 bit position. */ + +#define PR_SPI_bm 0x08 /* Port C SPI bit mask. */ +#define PR_SPI_bp 3 /* Port C SPI bit position. */ + +#define PR_HIRES_bm 0x04 /* Port C AWEX bit mask. */ +#define PR_HIRES_bp 2 /* Port C AWEX bit position. */ + +#define PR_TC1_bm 0x02 /* Port C Timer/Counter1 bit mask. */ +#define PR_TC1_bp 1 /* Port C Timer/Counter1 bit position. */ + +#define PR_TC0_bm 0x01 /* Port C Timer/Counter0 bit mask. */ +#define PR_TC0_bp 0 /* Port C Timer/Counter0 bit position. */ + +/* PR.PRPD bit masks and bit positions */ +/* PR_TWI Predefined. */ +/* PR_TWI Predefined. */ + +/* PR_USART1 Predefined. */ +/* PR_USART1 Predefined. */ + +/* PR_USART0 Predefined. */ +/* PR_USART0 Predefined. */ + +/* PR_SPI Predefined. */ +/* PR_SPI Predefined. */ + +/* PR_HIRES Predefined. */ +/* PR_HIRES Predefined. */ + +/* PR_TC1 Predefined. */ +/* PR_TC1 Predefined. */ + +/* PR_TC0 Predefined. */ +/* PR_TC0 Predefined. */ + +/* PR.PRPE bit masks and bit positions */ +/* PR_TWI Predefined. */ +/* PR_TWI Predefined. */ + +/* PR_USART1 Predefined. */ +/* PR_USART1 Predefined. */ + +/* PR_USART0 Predefined. */ +/* PR_USART0 Predefined. */ + +/* PR_SPI Predefined. */ +/* PR_SPI Predefined. */ + +/* PR_HIRES Predefined. */ +/* PR_HIRES Predefined. */ + +/* PR_TC1 Predefined. */ +/* PR_TC1 Predefined. */ + +/* PR_TC0 Predefined. */ +/* PR_TC0 Predefined. */ + +/* PR.PRPF bit masks and bit positions */ +/* PR_TWI Predefined. */ +/* PR_TWI Predefined. */ + +/* PR_USART1 Predefined. */ +/* PR_USART1 Predefined. */ + +/* PR_USART0 Predefined. */ +/* PR_USART0 Predefined. */ + +/* PR_SPI Predefined. */ +/* PR_SPI Predefined. */ + +/* PR_HIRES Predefined. */ +/* PR_HIRES Predefined. */ + +/* PR_TC1 Predefined. */ +/* PR_TC1 Predefined. */ + +/* PR_TC0 Predefined. */ +/* PR_TC0 Predefined. */ + +/* SLEEP - Sleep Controller */ +/* SLEEP.CTRL bit masks and bit positions */ +#define SLEEP_SMODE_gm 0x0E /* Sleep Mode group mask. */ +#define SLEEP_SMODE_gp 1 /* Sleep Mode group position. */ +#define SLEEP_SMODE0_bm (1<<1) /* Sleep Mode bit 0 mask. */ +#define SLEEP_SMODE0_bp 1 /* Sleep Mode bit 0 position. */ +#define SLEEP_SMODE1_bm (1<<2) /* Sleep Mode bit 1 mask. */ +#define SLEEP_SMODE1_bp 2 /* Sleep Mode bit 1 position. */ +#define SLEEP_SMODE2_bm (1<<3) /* Sleep Mode bit 2 mask. */ +#define SLEEP_SMODE2_bp 3 /* Sleep Mode bit 2 position. */ + +#define SLEEP_SEN_bm 0x01 /* Sleep Enable bit mask. */ +#define SLEEP_SEN_bp 0 /* Sleep Enable bit position. */ + +/* OSC - Oscillator */ +/* OSC.CTRL bit masks and bit positions */ +#define OSC_PLLEN_bm 0x10 /* PLL Enable bit mask. */ +#define OSC_PLLEN_bp 4 /* PLL Enable bit position. */ + +#define OSC_XOSCEN_bm 0x08 /* External Oscillator Enable bit mask. */ +#define OSC_XOSCEN_bp 3 /* External Oscillator Enable bit position. */ + +#define OSC_RC32KEN_bm 0x04 /* Internal 32.768 kHz RC Oscillator Enable bit mask. */ +#define OSC_RC32KEN_bp 2 /* Internal 32.768 kHz RC Oscillator Enable bit position. */ + +#define OSC_RC32MEN_bm 0x02 /* Internal 32 MHz RC Oscillator Enable bit mask. */ +#define OSC_RC32MEN_bp 1 /* Internal 32 MHz RC Oscillator Enable bit position. */ + +#define OSC_RC2MEN_bm 0x01 /* Internal 2 MHz RC Oscillator Enable bit mask. */ +#define OSC_RC2MEN_bp 0 /* Internal 2 MHz RC Oscillator Enable bit position. */ + +/* OSC.STATUS bit masks and bit positions */ +#define OSC_PLLRDY_bm 0x10 /* PLL Ready bit mask. */ +#define OSC_PLLRDY_bp 4 /* PLL Ready bit position. */ + +#define OSC_XOSCRDY_bm 0x08 /* External Oscillator Ready bit mask. */ +#define OSC_XOSCRDY_bp 3 /* External Oscillator Ready bit position. */ + +#define OSC_RC32KRDY_bm 0x04 /* Internal 32.768 kHz RC Oscillator Ready bit mask. */ +#define OSC_RC32KRDY_bp 2 /* Internal 32.768 kHz RC Oscillator Ready bit position. */ + +#define OSC_RC32MRDY_bm 0x02 /* Internal 32 MHz RC Oscillator Ready bit mask. */ +#define OSC_RC32MRDY_bp 1 /* Internal 32 MHz RC Oscillator Ready bit position. */ + +#define OSC_RC2MRDY_bm 0x01 /* Internal 2 MHz RC Oscillator Ready bit mask. */ +#define OSC_RC2MRDY_bp 0 /* Internal 2 MHz RC Oscillator Ready bit position. */ + +/* OSC.XOSCCTRL bit masks and bit positions */ +#define OSC_FRQRANGE_gm 0xC0 /* Frequency Range group mask. */ +#define OSC_FRQRANGE_gp 6 /* Frequency Range group position. */ +#define OSC_FRQRANGE0_bm (1<<6) /* Frequency Range bit 0 mask. */ +#define OSC_FRQRANGE0_bp 6 /* Frequency Range bit 0 position. */ +#define OSC_FRQRANGE1_bm (1<<7) /* Frequency Range bit 1 mask. */ +#define OSC_FRQRANGE1_bp 7 /* Frequency Range bit 1 position. */ + +#define OSC_X32KLPM_bm 0x20 /* 32.768 kHz XTAL OSC Low-power Mode bit mask. */ +#define OSC_X32KLPM_bp 5 /* 32.768 kHz XTAL OSC Low-power Mode bit position. */ + +#define OSC_XOSCPWR_bm 0x10 /* 16 MHz Crystal Oscillator High Power mode bit mask. */ +#define OSC_XOSCPWR_bp 4 /* 16 MHz Crystal Oscillator High Power mode bit position. */ + +#define OSC_XOSCSEL_gm 0x0F /* External Oscillator Selection and Startup Time group mask. */ +#define OSC_XOSCSEL_gp 0 /* External Oscillator Selection and Startup Time group position. */ +#define OSC_XOSCSEL0_bm (1<<0) /* External Oscillator Selection and Startup Time bit 0 mask. */ +#define OSC_XOSCSEL0_bp 0 /* External Oscillator Selection and Startup Time bit 0 position. */ +#define OSC_XOSCSEL1_bm (1<<1) /* External Oscillator Selection and Startup Time bit 1 mask. */ +#define OSC_XOSCSEL1_bp 1 /* External Oscillator Selection and Startup Time bit 1 position. */ +#define OSC_XOSCSEL2_bm (1<<2) /* External Oscillator Selection and Startup Time bit 2 mask. */ +#define OSC_XOSCSEL2_bp 2 /* External Oscillator Selection and Startup Time bit 2 position. */ +#define OSC_XOSCSEL3_bm (1<<3) /* External Oscillator Selection and Startup Time bit 3 mask. */ +#define OSC_XOSCSEL3_bp 3 /* External Oscillator Selection and Startup Time bit 3 position. */ + +/* OSC.XOSCFAIL bit masks and bit positions */ +#define OSC_PLLFDIF_bm 0x08 /* PLL Failure Detection Interrupt Flag bit mask. */ +#define OSC_PLLFDIF_bp 3 /* PLL Failure Detection Interrupt Flag bit position. */ + +#define OSC_PLLFDEN_bm 0x04 /* PLL Failure Detection Enable bit mask. */ +#define OSC_PLLFDEN_bp 2 /* PLL Failure Detection Enable bit position. */ + +#define OSC_XOSCFDIF_bm 0x02 /* XOSC Failure Detection Interrupt Flag bit mask. */ +#define OSC_XOSCFDIF_bp 1 /* XOSC Failure Detection Interrupt Flag bit position. */ + +#define OSC_XOSCFDEN_bm 0x01 /* XOSC Failure Detection Enable bit mask. */ +#define OSC_XOSCFDEN_bp 0 /* XOSC Failure Detection Enable bit position. */ + +/* OSC.PLLCTRL bit masks and bit positions */ +#define OSC_PLLSRC_gm 0xC0 /* Clock Source group mask. */ +#define OSC_PLLSRC_gp 6 /* Clock Source group position. */ +#define OSC_PLLSRC0_bm (1<<6) /* Clock Source bit 0 mask. */ +#define OSC_PLLSRC0_bp 6 /* Clock Source bit 0 position. */ +#define OSC_PLLSRC1_bm (1<<7) /* Clock Source bit 1 mask. */ +#define OSC_PLLSRC1_bp 7 /* Clock Source bit 1 position. */ + +#define OSC_PLLDIV_bm 0x20 /* Divide by 2 bit mask. */ +#define OSC_PLLDIV_bp 5 /* Divide by 2 bit position. */ + +#define OSC_PLLFAC_gm 0x1F /* Multiplication Factor group mask. */ +#define OSC_PLLFAC_gp 0 /* Multiplication Factor group position. */ +#define OSC_PLLFAC0_bm (1<<0) /* Multiplication Factor bit 0 mask. */ +#define OSC_PLLFAC0_bp 0 /* Multiplication Factor bit 0 position. */ +#define OSC_PLLFAC1_bm (1<<1) /* Multiplication Factor bit 1 mask. */ +#define OSC_PLLFAC1_bp 1 /* Multiplication Factor bit 1 position. */ +#define OSC_PLLFAC2_bm (1<<2) /* Multiplication Factor bit 2 mask. */ +#define OSC_PLLFAC2_bp 2 /* Multiplication Factor bit 2 position. */ +#define OSC_PLLFAC3_bm (1<<3) /* Multiplication Factor bit 3 mask. */ +#define OSC_PLLFAC3_bp 3 /* Multiplication Factor bit 3 position. */ +#define OSC_PLLFAC4_bm (1<<4) /* Multiplication Factor bit 4 mask. */ +#define OSC_PLLFAC4_bp 4 /* Multiplication Factor bit 4 position. */ + +/* OSC.DFLLCTRL bit masks and bit positions */ +#define OSC_RC32MCREF_gm 0x06 /* 32 MHz DFLL Calibration Reference group mask. */ +#define OSC_RC32MCREF_gp 1 /* 32 MHz DFLL Calibration Reference group position. */ +#define OSC_RC32MCREF0_bm (1<<1) /* 32 MHz DFLL Calibration Reference bit 0 mask. */ +#define OSC_RC32MCREF0_bp 1 /* 32 MHz DFLL Calibration Reference bit 0 position. */ +#define OSC_RC32MCREF1_bm (1<<2) /* 32 MHz DFLL Calibration Reference bit 1 mask. */ +#define OSC_RC32MCREF1_bp 2 /* 32 MHz DFLL Calibration Reference bit 1 position. */ + +#define OSC_RC2MCREF_bm 0x01 /* 2 MHz DFLL Calibration Reference bit mask. */ +#define OSC_RC2MCREF_bp 0 /* 2 MHz DFLL Calibration Reference bit position. */ + +/* DFLL - DFLL */ +/* DFLL.CTRL bit masks and bit positions */ +#define DFLL_ENABLE_bm 0x01 /* DFLL Enable bit mask. */ +#define DFLL_ENABLE_bp 0 /* DFLL Enable bit position. */ + +/* DFLL.CALA bit masks and bit positions */ +#define DFLL_CALL_gm 0x7F /* DFLL Calibration Value A group mask. */ +#define DFLL_CALL_gp 0 /* DFLL Calibration Value A group position. */ +#define DFLL_CALL0_bm (1<<0) /* DFLL Calibration Value A bit 0 mask. */ +#define DFLL_CALL0_bp 0 /* DFLL Calibration Value A bit 0 position. */ +#define DFLL_CALL1_bm (1<<1) /* DFLL Calibration Value A bit 1 mask. */ +#define DFLL_CALL1_bp 1 /* DFLL Calibration Value A bit 1 position. */ +#define DFLL_CALL2_bm (1<<2) /* DFLL Calibration Value A bit 2 mask. */ +#define DFLL_CALL2_bp 2 /* DFLL Calibration Value A bit 2 position. */ +#define DFLL_CALL3_bm (1<<3) /* DFLL Calibration Value A bit 3 mask. */ +#define DFLL_CALL3_bp 3 /* DFLL Calibration Value A bit 3 position. */ +#define DFLL_CALL4_bm (1<<4) /* DFLL Calibration Value A bit 4 mask. */ +#define DFLL_CALL4_bp 4 /* DFLL Calibration Value A bit 4 position. */ +#define DFLL_CALL5_bm (1<<5) /* DFLL Calibration Value A bit 5 mask. */ +#define DFLL_CALL5_bp 5 /* DFLL Calibration Value A bit 5 position. */ +#define DFLL_CALL6_bm (1<<6) /* DFLL Calibration Value A bit 6 mask. */ +#define DFLL_CALL6_bp 6 /* DFLL Calibration Value A bit 6 position. */ + +/* DFLL.CALB bit masks and bit positions */ +#define DFLL_CALH_gm 0x3F /* DFLL Calibration Value B group mask. */ +#define DFLL_CALH_gp 0 /* DFLL Calibration Value B group position. */ +#define DFLL_CALH0_bm (1<<0) /* DFLL Calibration Value B bit 0 mask. */ +#define DFLL_CALH0_bp 0 /* DFLL Calibration Value B bit 0 position. */ +#define DFLL_CALH1_bm (1<<1) /* DFLL Calibration Value B bit 1 mask. */ +#define DFLL_CALH1_bp 1 /* DFLL Calibration Value B bit 1 position. */ +#define DFLL_CALH2_bm (1<<2) /* DFLL Calibration Value B bit 2 mask. */ +#define DFLL_CALH2_bp 2 /* DFLL Calibration Value B bit 2 position. */ +#define DFLL_CALH3_bm (1<<3) /* DFLL Calibration Value B bit 3 mask. */ +#define DFLL_CALH3_bp 3 /* DFLL Calibration Value B bit 3 position. */ +#define DFLL_CALH4_bm (1<<4) /* DFLL Calibration Value B bit 4 mask. */ +#define DFLL_CALH4_bp 4 /* DFLL Calibration Value B bit 4 position. */ +#define DFLL_CALH5_bm (1<<5) /* DFLL Calibration Value B bit 5 mask. */ +#define DFLL_CALH5_bp 5 /* DFLL Calibration Value B bit 5 position. */ + +/* RST - Reset */ +/* RST.STATUS bit masks and bit positions */ +#define RST_SDRF_bm 0x40 /* Spike Detection Reset Flag bit mask. */ +#define RST_SDRF_bp 6 /* Spike Detection Reset Flag bit position. */ + +#define RST_SRF_bm 0x20 /* Software Reset Flag bit mask. */ +#define RST_SRF_bp 5 /* Software Reset Flag bit position. */ + +#define RST_PDIRF_bm 0x10 /* Programming and Debug Interface Interface Reset Flag bit mask. */ +#define RST_PDIRF_bp 4 /* Programming and Debug Interface Interface Reset Flag bit position. */ + +#define RST_WDRF_bm 0x08 /* Watchdog Reset Flag bit mask. */ +#define RST_WDRF_bp 3 /* Watchdog Reset Flag bit position. */ + +#define RST_BORF_bm 0x04 /* Brown-out Reset Flag bit mask. */ +#define RST_BORF_bp 2 /* Brown-out Reset Flag bit position. */ + +#define RST_EXTRF_bm 0x02 /* External Reset Flag bit mask. */ +#define RST_EXTRF_bp 1 /* External Reset Flag bit position. */ + +#define RST_PORF_bm 0x01 /* Power-on Reset Flag bit mask. */ +#define RST_PORF_bp 0 /* Power-on Reset Flag bit position. */ + +/* RST.CTRL bit masks and bit positions */ +#define RST_SWRST_bm 0x01 /* Software Reset bit mask. */ +#define RST_SWRST_bp 0 /* Software Reset bit position. */ + +/* WDT - Watch-Dog Timer */ +/* WDT.CTRL bit masks and bit positions */ +#define WDT_PER_gm 0x3C /* Period group mask. */ +#define WDT_PER_gp 2 /* Period group position. */ +#define WDT_PER0_bm (1<<2) /* Period bit 0 mask. */ +#define WDT_PER0_bp 2 /* Period bit 0 position. */ +#define WDT_PER1_bm (1<<3) /* Period bit 1 mask. */ +#define WDT_PER1_bp 3 /* Period bit 1 position. */ +#define WDT_PER2_bm (1<<4) /* Period bit 2 mask. */ +#define WDT_PER2_bp 4 /* Period bit 2 position. */ +#define WDT_PER3_bm (1<<5) /* Period bit 3 mask. */ +#define WDT_PER3_bp 5 /* Period bit 3 position. */ + +#define WDT_ENABLE_bm 0x02 /* Enable bit mask. */ +#define WDT_ENABLE_bp 1 /* Enable bit position. */ + +#define WDT_CEN_bm 0x01 /* Change Enable bit mask. */ +#define WDT_CEN_bp 0 /* Change Enable bit position. */ + +/* WDT.WINCTRL bit masks and bit positions */ +#define WDT_WPER_gm 0x3C /* Windowed Mode Period group mask. */ +#define WDT_WPER_gp 2 /* Windowed Mode Period group position. */ +#define WDT_WPER0_bm (1<<2) /* Windowed Mode Period bit 0 mask. */ +#define WDT_WPER0_bp 2 /* Windowed Mode Period bit 0 position. */ +#define WDT_WPER1_bm (1<<3) /* Windowed Mode Period bit 1 mask. */ +#define WDT_WPER1_bp 3 /* Windowed Mode Period bit 1 position. */ +#define WDT_WPER2_bm (1<<4) /* Windowed Mode Period bit 2 mask. */ +#define WDT_WPER2_bp 4 /* Windowed Mode Period bit 2 position. */ +#define WDT_WPER3_bm (1<<5) /* Windowed Mode Period bit 3 mask. */ +#define WDT_WPER3_bp 5 /* Windowed Mode Period bit 3 position. */ + +#define WDT_WEN_bm 0x02 /* Windowed Mode Enable bit mask. */ +#define WDT_WEN_bp 1 /* Windowed Mode Enable bit position. */ + +#define WDT_WCEN_bm 0x01 /* Windowed Mode Change Enable bit mask. */ +#define WDT_WCEN_bp 0 /* Windowed Mode Change Enable bit position. */ + +/* WDT.STATUS bit masks and bit positions */ +#define WDT_SYNCBUSY_bm 0x01 /* Syncronization busy bit mask. */ +#define WDT_SYNCBUSY_bp 0 /* Syncronization busy bit position. */ + +/* MCU - MCU Control */ +/* MCU.MCUCR bit masks and bit positions */ +#define MCU_JTAGD_bm 0x01 /* JTAG Disable bit mask. */ +#define MCU_JTAGD_bp 0 /* JTAG Disable bit position. */ + +/* MCU.ANAINIT bit masks and bit positions */ +#define MCU_STARTUPDLYB_gm 0x0C /* Analog startup delay Port B group mask. */ +#define MCU_STARTUPDLYB_gp 2 /* Analog startup delay Port B group position. */ +#define MCU_STARTUPDLYB0_bm (1<<2) /* Analog startup delay Port B bit 0 mask. */ +#define MCU_STARTUPDLYB0_bp 2 /* Analog startup delay Port B bit 0 position. */ +#define MCU_STARTUPDLYB1_bm (1<<3) /* Analog startup delay Port B bit 1 mask. */ +#define MCU_STARTUPDLYB1_bp 3 /* Analog startup delay Port B bit 1 position. */ + +#define MCU_STARTUPDLYA_gm 0x03 /* Analog startup delay Port A group mask. */ +#define MCU_STARTUPDLYA_gp 0 /* Analog startup delay Port A group position. */ +#define MCU_STARTUPDLYA0_bm (1<<0) /* Analog startup delay Port A bit 0 mask. */ +#define MCU_STARTUPDLYA0_bp 0 /* Analog startup delay Port A bit 0 position. */ +#define MCU_STARTUPDLYA1_bm (1<<1) /* Analog startup delay Port A bit 1 mask. */ +#define MCU_STARTUPDLYA1_bp 1 /* Analog startup delay Port A bit 1 position. */ + +/* MCU.EVSYSLOCK bit masks and bit positions */ +#define MCU_EVSYS1LOCK_bm 0x10 /* Event Channel 4-7 Lock bit mask. */ +#define MCU_EVSYS1LOCK_bp 4 /* Event Channel 4-7 Lock bit position. */ + +#define MCU_EVSYS0LOCK_bm 0x01 /* Event Channel 0-3 Lock bit mask. */ +#define MCU_EVSYS0LOCK_bp 0 /* Event Channel 0-3 Lock bit position. */ + +/* MCU.AWEXLOCK bit masks and bit positions */ +#define MCU_AWEXFLOCK_bm 0x08 /* AWeX on T/C F0 Lock bit mask. */ +#define MCU_AWEXFLOCK_bp 3 /* AWeX on T/C F0 Lock bit position. */ + +#define MCU_AWEXELOCK_bm 0x04 /* AWeX on T/C E0 Lock bit mask. */ +#define MCU_AWEXELOCK_bp 2 /* AWeX on T/C E0 Lock bit position. */ + +#define MCU_AWEXDLOCK_bm 0x02 /* AWeX on T/C D0 Lock bit mask. */ +#define MCU_AWEXDLOCK_bp 1 /* AWeX on T/C D0 Lock bit position. */ + +#define MCU_AWEXCLOCK_bm 0x01 /* AWeX on T/C C0 Lock bit mask. */ +#define MCU_AWEXCLOCK_bp 0 /* AWeX on T/C C0 Lock bit position. */ + +/* PMIC - Programmable Multi-level Interrupt Controller */ +/* PMIC.STATUS bit masks and bit positions */ +#define PMIC_NMIEX_bm 0x80 /* Non-maskable Interrupt Executing bit mask. */ +#define PMIC_NMIEX_bp 7 /* Non-maskable Interrupt Executing bit position. */ + +#define PMIC_HILVLEX_bm 0x04 /* High Level Interrupt Executing bit mask. */ +#define PMIC_HILVLEX_bp 2 /* High Level Interrupt Executing bit position. */ + +#define PMIC_MEDLVLEX_bm 0x02 /* Medium Level Interrupt Executing bit mask. */ +#define PMIC_MEDLVLEX_bp 1 /* Medium Level Interrupt Executing bit position. */ + +#define PMIC_LOLVLEX_bm 0x01 /* Low Level Interrupt Executing bit mask. */ +#define PMIC_LOLVLEX_bp 0 /* Low Level Interrupt Executing bit position. */ + +/* PMIC.INTPRI bit masks and bit positions */ +#define PMIC_INTPRI_gm 0xFF /* Interrupt Priority group mask. */ +#define PMIC_INTPRI_gp 0 /* Interrupt Priority group position. */ +#define PMIC_INTPRI0_bm (1<<0) /* Interrupt Priority bit 0 mask. */ +#define PMIC_INTPRI0_bp 0 /* Interrupt Priority bit 0 position. */ +#define PMIC_INTPRI1_bm (1<<1) /* Interrupt Priority bit 1 mask. */ +#define PMIC_INTPRI1_bp 1 /* Interrupt Priority bit 1 position. */ +#define PMIC_INTPRI2_bm (1<<2) /* Interrupt Priority bit 2 mask. */ +#define PMIC_INTPRI2_bp 2 /* Interrupt Priority bit 2 position. */ +#define PMIC_INTPRI3_bm (1<<3) /* Interrupt Priority bit 3 mask. */ +#define PMIC_INTPRI3_bp 3 /* Interrupt Priority bit 3 position. */ +#define PMIC_INTPRI4_bm (1<<4) /* Interrupt Priority bit 4 mask. */ +#define PMIC_INTPRI4_bp 4 /* Interrupt Priority bit 4 position. */ +#define PMIC_INTPRI5_bm (1<<5) /* Interrupt Priority bit 5 mask. */ +#define PMIC_INTPRI5_bp 5 /* Interrupt Priority bit 5 position. */ +#define PMIC_INTPRI6_bm (1<<6) /* Interrupt Priority bit 6 mask. */ +#define PMIC_INTPRI6_bp 6 /* Interrupt Priority bit 6 position. */ +#define PMIC_INTPRI7_bm (1<<7) /* Interrupt Priority bit 7 mask. */ +#define PMIC_INTPRI7_bp 7 /* Interrupt Priority bit 7 position. */ + +/* PMIC.CTRL bit masks and bit positions */ +#define PMIC_RREN_bm 0x80 /* Round-Robin Priority Enable bit mask. */ +#define PMIC_RREN_bp 7 /* Round-Robin Priority Enable bit position. */ + +#define PMIC_IVSEL_bm 0x40 /* Interrupt Vector Select bit mask. */ +#define PMIC_IVSEL_bp 6 /* Interrupt Vector Select bit position. */ + +#define PMIC_HILVLEN_bm 0x04 /* High Level Enable bit mask. */ +#define PMIC_HILVLEN_bp 2 /* High Level Enable bit position. */ + +#define PMIC_MEDLVLEN_bm 0x02 /* Medium Level Enable bit mask. */ +#define PMIC_MEDLVLEN_bp 1 /* Medium Level Enable bit position. */ + +#define PMIC_LOLVLEN_bm 0x01 /* Low Level Enable bit mask. */ +#define PMIC_LOLVLEN_bp 0 /* Low Level Enable bit position. */ + +/* PORTCFG - Port Configuration */ +/* PORTCFG.VPCTRLA bit masks and bit positions */ +#define PORTCFG_VP1MAP_gm 0xF0 /* Virtual Port 1 Mapping group mask. */ +#define PORTCFG_VP1MAP_gp 4 /* Virtual Port 1 Mapping group position. */ +#define PORTCFG_VP1MAP0_bm (1<<4) /* Virtual Port 1 Mapping bit 0 mask. */ +#define PORTCFG_VP1MAP0_bp 4 /* Virtual Port 1 Mapping bit 0 position. */ +#define PORTCFG_VP1MAP1_bm (1<<5) /* Virtual Port 1 Mapping bit 1 mask. */ +#define PORTCFG_VP1MAP1_bp 5 /* Virtual Port 1 Mapping bit 1 position. */ +#define PORTCFG_VP1MAP2_bm (1<<6) /* Virtual Port 1 Mapping bit 2 mask. */ +#define PORTCFG_VP1MAP2_bp 6 /* Virtual Port 1 Mapping bit 2 position. */ +#define PORTCFG_VP1MAP3_bm (1<<7) /* Virtual Port 1 Mapping bit 3 mask. */ +#define PORTCFG_VP1MAP3_bp 7 /* Virtual Port 1 Mapping bit 3 position. */ + +#define PORTCFG_VP0MAP_gm 0x0F /* Virtual Port 0 Mapping group mask. */ +#define PORTCFG_VP0MAP_gp 0 /* Virtual Port 0 Mapping group position. */ +#define PORTCFG_VP0MAP0_bm (1<<0) /* Virtual Port 0 Mapping bit 0 mask. */ +#define PORTCFG_VP0MAP0_bp 0 /* Virtual Port 0 Mapping bit 0 position. */ +#define PORTCFG_VP0MAP1_bm (1<<1) /* Virtual Port 0 Mapping bit 1 mask. */ +#define PORTCFG_VP0MAP1_bp 1 /* Virtual Port 0 Mapping bit 1 position. */ +#define PORTCFG_VP0MAP2_bm (1<<2) /* Virtual Port 0 Mapping bit 2 mask. */ +#define PORTCFG_VP0MAP2_bp 2 /* Virtual Port 0 Mapping bit 2 position. */ +#define PORTCFG_VP0MAP3_bm (1<<3) /* Virtual Port 0 Mapping bit 3 mask. */ +#define PORTCFG_VP0MAP3_bp 3 /* Virtual Port 0 Mapping bit 3 position. */ + +/* PORTCFG.VPCTRLB bit masks and bit positions */ +#define PORTCFG_VP3MAP_gm 0xF0 /* Virtual Port 3 Mapping group mask. */ +#define PORTCFG_VP3MAP_gp 4 /* Virtual Port 3 Mapping group position. */ +#define PORTCFG_VP3MAP0_bm (1<<4) /* Virtual Port 3 Mapping bit 0 mask. */ +#define PORTCFG_VP3MAP0_bp 4 /* Virtual Port 3 Mapping bit 0 position. */ +#define PORTCFG_VP3MAP1_bm (1<<5) /* Virtual Port 3 Mapping bit 1 mask. */ +#define PORTCFG_VP3MAP1_bp 5 /* Virtual Port 3 Mapping bit 1 position. */ +#define PORTCFG_VP3MAP2_bm (1<<6) /* Virtual Port 3 Mapping bit 2 mask. */ +#define PORTCFG_VP3MAP2_bp 6 /* Virtual Port 3 Mapping bit 2 position. */ +#define PORTCFG_VP3MAP3_bm (1<<7) /* Virtual Port 3 Mapping bit 3 mask. */ +#define PORTCFG_VP3MAP3_bp 7 /* Virtual Port 3 Mapping bit 3 position. */ + +#define PORTCFG_VP2MAP_gm 0x0F /* Virtual Port 2 Mapping group mask. */ +#define PORTCFG_VP2MAP_gp 0 /* Virtual Port 2 Mapping group position. */ +#define PORTCFG_VP2MAP0_bm (1<<0) /* Virtual Port 2 Mapping bit 0 mask. */ +#define PORTCFG_VP2MAP0_bp 0 /* Virtual Port 2 Mapping bit 0 position. */ +#define PORTCFG_VP2MAP1_bm (1<<1) /* Virtual Port 2 Mapping bit 1 mask. */ +#define PORTCFG_VP2MAP1_bp 1 /* Virtual Port 2 Mapping bit 1 position. */ +#define PORTCFG_VP2MAP2_bm (1<<2) /* Virtual Port 2 Mapping bit 2 mask. */ +#define PORTCFG_VP2MAP2_bp 2 /* Virtual Port 2 Mapping bit 2 position. */ +#define PORTCFG_VP2MAP3_bm (1<<3) /* Virtual Port 2 Mapping bit 3 mask. */ +#define PORTCFG_VP2MAP3_bp 3 /* Virtual Port 2 Mapping bit 3 position. */ + +/* PORTCFG.CLKEVOUT bit masks and bit positions */ +#define PORTCFG_CLKOUT_gm 0x03 /* Peripheral Clock Output Port group mask. */ +#define PORTCFG_CLKOUT_gp 0 /* Peripheral Clock Output Port group position. */ +#define PORTCFG_CLKOUT0_bm (1<<0) /* Peripheral Clock Output Port bit 0 mask. */ +#define PORTCFG_CLKOUT0_bp 0 /* Peripheral Clock Output Port bit 0 position. */ +#define PORTCFG_CLKOUT1_bm (1<<1) /* Peripheral Clock Output Port bit 1 mask. */ +#define PORTCFG_CLKOUT1_bp 1 /* Peripheral Clock Output Port bit 1 position. */ + +#define PORTCFG_CLKOUTSEL_gm 0x0C /* Peripheral Clock Output Select group mask. */ +#define PORTCFG_CLKOUTSEL_gp 2 /* Peripheral Clock Output Select group position. */ +#define PORTCFG_CLKOUTSEL0_bm (1<<2) /* Peripheral Clock Output Select bit 0 mask. */ +#define PORTCFG_CLKOUTSEL0_bp 2 /* Peripheral Clock Output Select bit 0 position. */ +#define PORTCFG_CLKOUTSEL1_bm (1<<3) /* Peripheral Clock Output Select bit 1 mask. */ +#define PORTCFG_CLKOUTSEL1_bp 3 /* Peripheral Clock Output Select bit 1 position. */ + +#define PORTCFG_EVOUT_gm 0x30 /* Event Output Port group mask. */ +#define PORTCFG_EVOUT_gp 4 /* Event Output Port group position. */ +#define PORTCFG_EVOUT0_bm (1<<4) /* Event Output Port bit 0 mask. */ +#define PORTCFG_EVOUT0_bp 4 /* Event Output Port bit 0 position. */ +#define PORTCFG_EVOUT1_bm (1<<5) /* Event Output Port bit 1 mask. */ +#define PORTCFG_EVOUT1_bp 5 /* Event Output Port bit 1 position. */ + +#define PORTCFG_RTCOUT_bm 0x40 /* RTC Clock Output bit mask. */ +#define PORTCFG_RTCOUT_bp 6 /* RTC Clock Output bit position. */ + +#define PORTCFG_CLKEVPIN_bm 0x80 /* Peripheral Clock and Event Output pin Select bit mask. */ +#define PORTCFG_CLKEVPIN_bp 7 /* Peripheral Clock and Event Output pin Select bit position. */ + +/* PORTCFG.EBIOUT bit masks and bit positions */ +#define PORTCFG_EBICSOUT_gm 0x03 /* EBI Chip Select Output group mask. */ +#define PORTCFG_EBICSOUT_gp 0 /* EBI Chip Select Output group position. */ +#define PORTCFG_EBICSOUT0_bm (1<<0) /* EBI Chip Select Output bit 0 mask. */ +#define PORTCFG_EBICSOUT0_bp 0 /* EBI Chip Select Output bit 0 position. */ +#define PORTCFG_EBICSOUT1_bm (1<<1) /* EBI Chip Select Output bit 1 mask. */ +#define PORTCFG_EBICSOUT1_bp 1 /* EBI Chip Select Output bit 1 position. */ + +#define PORTCFG_EBIADROUT_gm 0x0C /* EBI Address Output group mask. */ +#define PORTCFG_EBIADROUT_gp 2 /* EBI Address Output group position. */ +#define PORTCFG_EBIADROUT0_bm (1<<2) /* EBI Address Output bit 0 mask. */ +#define PORTCFG_EBIADROUT0_bp 2 /* EBI Address Output bit 0 position. */ +#define PORTCFG_EBIADROUT1_bm (1<<3) /* EBI Address Output bit 1 mask. */ +#define PORTCFG_EBIADROUT1_bp 3 /* EBI Address Output bit 1 position. */ + +/* PORTCFG.EVOUTSEL bit masks and bit positions */ +#define PORTCFG_EVOUTSEL_gm 0x07 /* Event Output Select group mask. */ +#define PORTCFG_EVOUTSEL_gp 0 /* Event Output Select group position. */ +#define PORTCFG_EVOUTSEL0_bm (1<<0) /* Event Output Select bit 0 mask. */ +#define PORTCFG_EVOUTSEL0_bp 0 /* Event Output Select bit 0 position. */ +#define PORTCFG_EVOUTSEL1_bm (1<<1) /* Event Output Select bit 1 mask. */ +#define PORTCFG_EVOUTSEL1_bp 1 /* Event Output Select bit 1 position. */ +#define PORTCFG_EVOUTSEL2_bm (1<<2) /* Event Output Select bit 2 mask. */ +#define PORTCFG_EVOUTSEL2_bp 2 /* Event Output Select bit 2 position. */ + +/* AES - AES Module */ +/* AES.CTRL bit masks and bit positions */ +#define AES_START_bm 0x80 /* Start/Run bit mask. */ +#define AES_START_bp 7 /* Start/Run bit position. */ + +#define AES_AUTO_bm 0x40 /* Auto Start Trigger bit mask. */ +#define AES_AUTO_bp 6 /* Auto Start Trigger bit position. */ + +#define AES_RESET_bm 0x20 /* AES Software Reset bit mask. */ +#define AES_RESET_bp 5 /* AES Software Reset bit position. */ + +#define AES_DECRYPT_bm 0x10 /* Decryption / Direction bit mask. */ +#define AES_DECRYPT_bp 4 /* Decryption / Direction bit position. */ + +#define AES_XOR_bm 0x04 /* State XOR Load Enable bit mask. */ +#define AES_XOR_bp 2 /* State XOR Load Enable bit position. */ + +/* AES.STATUS bit masks and bit positions */ +#define AES_ERROR_bm 0x80 /* AES Error bit mask. */ +#define AES_ERROR_bp 7 /* AES Error bit position. */ + +#define AES_SRIF_bm 0x01 /* State Ready Interrupt Flag bit mask. */ +#define AES_SRIF_bp 0 /* State Ready Interrupt Flag bit position. */ + +/* AES.INTCTRL bit masks and bit positions */ +#define AES_INTLVL_gm 0x03 /* Interrupt level group mask. */ +#define AES_INTLVL_gp 0 /* Interrupt level group position. */ +#define AES_INTLVL0_bm (1<<0) /* Interrupt level bit 0 mask. */ +#define AES_INTLVL0_bp 0 /* Interrupt level bit 0 position. */ +#define AES_INTLVL1_bm (1<<1) /* Interrupt level bit 1 mask. */ +#define AES_INTLVL1_bp 1 /* Interrupt level bit 1 position. */ + +/* CRC - Cyclic Redundancy Checker */ +/* CRC.CTRL bit masks and bit positions */ +#define CRC_RESET_gm 0xC0 /* Reset group mask. */ +#define CRC_RESET_gp 6 /* Reset group position. */ +#define CRC_RESET0_bm (1<<6) /* Reset bit 0 mask. */ +#define CRC_RESET0_bp 6 /* Reset bit 0 position. */ +#define CRC_RESET1_bm (1<<7) /* Reset bit 1 mask. */ +#define CRC_RESET1_bp 7 /* Reset bit 1 position. */ + +#define CRC_CRC32_bm 0x20 /* CRC Mode bit mask. */ +#define CRC_CRC32_bp 5 /* CRC Mode bit position. */ + +#define CRC_SOURCE_gm 0x0F /* Input Source group mask. */ +#define CRC_SOURCE_gp 0 /* Input Source group position. */ +#define CRC_SOURCE0_bm (1<<0) /* Input Source bit 0 mask. */ +#define CRC_SOURCE0_bp 0 /* Input Source bit 0 position. */ +#define CRC_SOURCE1_bm (1<<1) /* Input Source bit 1 mask. */ +#define CRC_SOURCE1_bp 1 /* Input Source bit 1 position. */ +#define CRC_SOURCE2_bm (1<<2) /* Input Source bit 2 mask. */ +#define CRC_SOURCE2_bp 2 /* Input Source bit 2 position. */ +#define CRC_SOURCE3_bm (1<<3) /* Input Source bit 3 mask. */ +#define CRC_SOURCE3_bp 3 /* Input Source bit 3 position. */ + +/* CRC.STATUS bit masks and bit positions */ +#define CRC_ZERO_bm 0x02 /* Zero detection bit mask. */ +#define CRC_ZERO_bp 1 /* Zero detection bit position. */ + +#define CRC_BUSY_bm 0x01 /* Busy bit mask. */ +#define CRC_BUSY_bp 0 /* Busy bit position. */ + +/* DMA - DMA Controller */ +/* DMA_CH.CTRLA bit masks and bit positions */ +#define DMA_CH_ENABLE_bm 0x80 /* Channel Enable bit mask. */ +#define DMA_CH_ENABLE_bp 7 /* Channel Enable bit position. */ + +#define DMA_CH_RESET_bm 0x40 /* Channel Software Reset bit mask. */ +#define DMA_CH_RESET_bp 6 /* Channel Software Reset bit position. */ + +#define DMA_CH_REPEAT_bm 0x20 /* Channel Repeat Mode bit mask. */ +#define DMA_CH_REPEAT_bp 5 /* Channel Repeat Mode bit position. */ + +#define DMA_CH_TRFREQ_bm 0x10 /* Channel Transfer Request bit mask. */ +#define DMA_CH_TRFREQ_bp 4 /* Channel Transfer Request bit position. */ + +#define DMA_CH_SINGLE_bm 0x04 /* Channel Single Shot Data Transfer bit mask. */ +#define DMA_CH_SINGLE_bp 2 /* Channel Single Shot Data Transfer bit position. */ + +#define DMA_CH_BURSTLEN_gm 0x03 /* Channel Transfer Mode group mask. */ +#define DMA_CH_BURSTLEN_gp 0 /* Channel Transfer Mode group position. */ +#define DMA_CH_BURSTLEN0_bm (1<<0) /* Channel Transfer Mode bit 0 mask. */ +#define DMA_CH_BURSTLEN0_bp 0 /* Channel Transfer Mode bit 0 position. */ +#define DMA_CH_BURSTLEN1_bm (1<<1) /* Channel Transfer Mode bit 1 mask. */ +#define DMA_CH_BURSTLEN1_bp 1 /* Channel Transfer Mode bit 1 position. */ + +/* DMA_CH.CTRLB bit masks and bit positions */ +#define DMA_CH_CHBUSY_bm 0x80 /* Block Transfer Busy bit mask. */ +#define DMA_CH_CHBUSY_bp 7 /* Block Transfer Busy bit position. */ + +#define DMA_CH_CHPEND_bm 0x40 /* Block Transfer Pending bit mask. */ +#define DMA_CH_CHPEND_bp 6 /* Block Transfer Pending bit position. */ + +#define DMA_CH_ERRIF_bm 0x20 /* Block Transfer Error Interrupt Flag bit mask. */ +#define DMA_CH_ERRIF_bp 5 /* Block Transfer Error Interrupt Flag bit position. */ + +#define DMA_CH_TRNIF_bm 0x10 /* Transaction Complete Interrup Flag bit mask. */ +#define DMA_CH_TRNIF_bp 4 /* Transaction Complete Interrup Flag bit position. */ + +#define DMA_CH_ERRINTLVL_gm 0x0C /* Transfer Error Interrupt Level group mask. */ +#define DMA_CH_ERRINTLVL_gp 2 /* Transfer Error Interrupt Level group position. */ +#define DMA_CH_ERRINTLVL0_bm (1<<2) /* Transfer Error Interrupt Level bit 0 mask. */ +#define DMA_CH_ERRINTLVL0_bp 2 /* Transfer Error Interrupt Level bit 0 position. */ +#define DMA_CH_ERRINTLVL1_bm (1<<3) /* Transfer Error Interrupt Level bit 1 mask. */ +#define DMA_CH_ERRINTLVL1_bp 3 /* Transfer Error Interrupt Level bit 1 position. */ + +#define DMA_CH_TRNINTLVL_gm 0x03 /* Transaction Complete Interrupt Level group mask. */ +#define DMA_CH_TRNINTLVL_gp 0 /* Transaction Complete Interrupt Level group position. */ +#define DMA_CH_TRNINTLVL0_bm (1<<0) /* Transaction Complete Interrupt Level bit 0 mask. */ +#define DMA_CH_TRNINTLVL0_bp 0 /* Transaction Complete Interrupt Level bit 0 position. */ +#define DMA_CH_TRNINTLVL1_bm (1<<1) /* Transaction Complete Interrupt Level bit 1 mask. */ +#define DMA_CH_TRNINTLVL1_bp 1 /* Transaction Complete Interrupt Level bit 1 position. */ + +/* DMA_CH.ADDRCTRL bit masks and bit positions */ +#define DMA_CH_SRCRELOAD_gm 0xC0 /* Channel Source Address Reload group mask. */ +#define DMA_CH_SRCRELOAD_gp 6 /* Channel Source Address Reload group position. */ +#define DMA_CH_SRCRELOAD0_bm (1<<6) /* Channel Source Address Reload bit 0 mask. */ +#define DMA_CH_SRCRELOAD0_bp 6 /* Channel Source Address Reload bit 0 position. */ +#define DMA_CH_SRCRELOAD1_bm (1<<7) /* Channel Source Address Reload bit 1 mask. */ +#define DMA_CH_SRCRELOAD1_bp 7 /* Channel Source Address Reload bit 1 position. */ + +#define DMA_CH_SRCDIR_gm 0x30 /* Channel Source Address Mode group mask. */ +#define DMA_CH_SRCDIR_gp 4 /* Channel Source Address Mode group position. */ +#define DMA_CH_SRCDIR0_bm (1<<4) /* Channel Source Address Mode bit 0 mask. */ +#define DMA_CH_SRCDIR0_bp 4 /* Channel Source Address Mode bit 0 position. */ +#define DMA_CH_SRCDIR1_bm (1<<5) /* Channel Source Address Mode bit 1 mask. */ +#define DMA_CH_SRCDIR1_bp 5 /* Channel Source Address Mode bit 1 position. */ + +#define DMA_CH_DESTRELOAD_gm 0x0C /* Channel Destination Address Reload group mask. */ +#define DMA_CH_DESTRELOAD_gp 2 /* Channel Destination Address Reload group position. */ +#define DMA_CH_DESTRELOAD0_bm (1<<2) /* Channel Destination Address Reload bit 0 mask. */ +#define DMA_CH_DESTRELOAD0_bp 2 /* Channel Destination Address Reload bit 0 position. */ +#define DMA_CH_DESTRELOAD1_bm (1<<3) /* Channel Destination Address Reload bit 1 mask. */ +#define DMA_CH_DESTRELOAD1_bp 3 /* Channel Destination Address Reload bit 1 position. */ + +#define DMA_CH_DESTDIR_gm 0x03 /* Channel Destination Address Mode group mask. */ +#define DMA_CH_DESTDIR_gp 0 /* Channel Destination Address Mode group position. */ +#define DMA_CH_DESTDIR0_bm (1<<0) /* Channel Destination Address Mode bit 0 mask. */ +#define DMA_CH_DESTDIR0_bp 0 /* Channel Destination Address Mode bit 0 position. */ +#define DMA_CH_DESTDIR1_bm (1<<1) /* Channel Destination Address Mode bit 1 mask. */ +#define DMA_CH_DESTDIR1_bp 1 /* Channel Destination Address Mode bit 1 position. */ + +/* DMA_CH.TRIGSRC bit masks and bit positions */ +#define DMA_CH_TRIGSRC_gm 0xFF /* Channel Trigger Source group mask. */ +#define DMA_CH_TRIGSRC_gp 0 /* Channel Trigger Source group position. */ +#define DMA_CH_TRIGSRC0_bm (1<<0) /* Channel Trigger Source bit 0 mask. */ +#define DMA_CH_TRIGSRC0_bp 0 /* Channel Trigger Source bit 0 position. */ +#define DMA_CH_TRIGSRC1_bm (1<<1) /* Channel Trigger Source bit 1 mask. */ +#define DMA_CH_TRIGSRC1_bp 1 /* Channel Trigger Source bit 1 position. */ +#define DMA_CH_TRIGSRC2_bm (1<<2) /* Channel Trigger Source bit 2 mask. */ +#define DMA_CH_TRIGSRC2_bp 2 /* Channel Trigger Source bit 2 position. */ +#define DMA_CH_TRIGSRC3_bm (1<<3) /* Channel Trigger Source bit 3 mask. */ +#define DMA_CH_TRIGSRC3_bp 3 /* Channel Trigger Source bit 3 position. */ +#define DMA_CH_TRIGSRC4_bm (1<<4) /* Channel Trigger Source bit 4 mask. */ +#define DMA_CH_TRIGSRC4_bp 4 /* Channel Trigger Source bit 4 position. */ +#define DMA_CH_TRIGSRC5_bm (1<<5) /* Channel Trigger Source bit 5 mask. */ +#define DMA_CH_TRIGSRC5_bp 5 /* Channel Trigger Source bit 5 position. */ +#define DMA_CH_TRIGSRC6_bm (1<<6) /* Channel Trigger Source bit 6 mask. */ +#define DMA_CH_TRIGSRC6_bp 6 /* Channel Trigger Source bit 6 position. */ +#define DMA_CH_TRIGSRC7_bm (1<<7) /* Channel Trigger Source bit 7 mask. */ +#define DMA_CH_TRIGSRC7_bp 7 /* Channel Trigger Source bit 7 position. */ + +/* DMA.CTRL bit masks and bit positions */ +#define DMA_ENABLE_bm 0x80 /* Enable bit mask. */ +#define DMA_ENABLE_bp 7 /* Enable bit position. */ + +#define DMA_RESET_bm 0x40 /* Software Reset bit mask. */ +#define DMA_RESET_bp 6 /* Software Reset bit position. */ + +#define DMA_DBUFMODE_gm 0x0C /* Double Buffering Mode group mask. */ +#define DMA_DBUFMODE_gp 2 /* Double Buffering Mode group position. */ +#define DMA_DBUFMODE0_bm (1<<2) /* Double Buffering Mode bit 0 mask. */ +#define DMA_DBUFMODE0_bp 2 /* Double Buffering Mode bit 0 position. */ +#define DMA_DBUFMODE1_bm (1<<3) /* Double Buffering Mode bit 1 mask. */ +#define DMA_DBUFMODE1_bp 3 /* Double Buffering Mode bit 1 position. */ + +#define DMA_PRIMODE_gm 0x03 /* Channel Priority Mode group mask. */ +#define DMA_PRIMODE_gp 0 /* Channel Priority Mode group position. */ +#define DMA_PRIMODE0_bm (1<<0) /* Channel Priority Mode bit 0 mask. */ +#define DMA_PRIMODE0_bp 0 /* Channel Priority Mode bit 0 position. */ +#define DMA_PRIMODE1_bm (1<<1) /* Channel Priority Mode bit 1 mask. */ +#define DMA_PRIMODE1_bp 1 /* Channel Priority Mode bit 1 position. */ + +/* DMA.INTFLAGS bit masks and bit positions */ +#define DMA_CH3ERRIF_bm 0x80 /* Channel 3 Block Transfer Error Interrupt Flag bit mask. */ +#define DMA_CH3ERRIF_bp 7 /* Channel 3 Block Transfer Error Interrupt Flag bit position. */ + +#define DMA_CH2ERRIF_bm 0x40 /* Channel 2 Block Transfer Error Interrupt Flag bit mask. */ +#define DMA_CH2ERRIF_bp 6 /* Channel 2 Block Transfer Error Interrupt Flag bit position. */ + +#define DMA_CH1ERRIF_bm 0x20 /* Channel 1 Block Transfer Error Interrupt Flag bit mask. */ +#define DMA_CH1ERRIF_bp 5 /* Channel 1 Block Transfer Error Interrupt Flag bit position. */ + +#define DMA_CH0ERRIF_bm 0x10 /* Channel 0 Block Transfer Error Interrupt Flag bit mask. */ +#define DMA_CH0ERRIF_bp 4 /* Channel 0 Block Transfer Error Interrupt Flag bit position. */ + +#define DMA_CH3TRNIF_bm 0x08 /* Channel 3 Transaction Complete Interrupt Flag bit mask. */ +#define DMA_CH3TRNIF_bp 3 /* Channel 3 Transaction Complete Interrupt Flag bit position. */ + +#define DMA_CH2TRNIF_bm 0x04 /* Channel 2 Transaction Complete Interrupt Flag bit mask. */ +#define DMA_CH2TRNIF_bp 2 /* Channel 2 Transaction Complete Interrupt Flag bit position. */ + +#define DMA_CH1TRNIF_bm 0x02 /* Channel 1 Transaction Complete Interrupt Flag bit mask. */ +#define DMA_CH1TRNIF_bp 1 /* Channel 1 Transaction Complete Interrupt Flag bit position. */ + +#define DMA_CH0TRNIF_bm 0x01 /* Channel 0 Transaction Complete Interrupt Flag bit mask. */ +#define DMA_CH0TRNIF_bp 0 /* Channel 0 Transaction Complete Interrupt Flag bit position. */ + +/* DMA.STATUS bit masks and bit positions */ +#define DMA_CH3BUSY_bm 0x80 /* Channel 3 Block Transfer Busy bit mask. */ +#define DMA_CH3BUSY_bp 7 /* Channel 3 Block Transfer Busy bit position. */ + +#define DMA_CH2BUSY_bm 0x40 /* Channel 2 Block Transfer Busy bit mask. */ +#define DMA_CH2BUSY_bp 6 /* Channel 2 Block Transfer Busy bit position. */ + +#define DMA_CH1BUSY_bm 0x20 /* Channel 1 Block Transfer Busy bit mask. */ +#define DMA_CH1BUSY_bp 5 /* Channel 1 Block Transfer Busy bit position. */ + +#define DMA_CH0BUSY_bm 0x10 /* Channel 0 Block Transfer Busy bit mask. */ +#define DMA_CH0BUSY_bp 4 /* Channel 0 Block Transfer Busy bit position. */ + +#define DMA_CH3PEND_bm 0x08 /* Channel 3 Block Transfer Pending bit mask. */ +#define DMA_CH3PEND_bp 3 /* Channel 3 Block Transfer Pending bit position. */ + +#define DMA_CH2PEND_bm 0x04 /* Channel 2 Block Transfer Pending bit mask. */ +#define DMA_CH2PEND_bp 2 /* Channel 2 Block Transfer Pending bit position. */ + +#define DMA_CH1PEND_bm 0x02 /* Channel 1 Block Transfer Pending bit mask. */ +#define DMA_CH1PEND_bp 1 /* Channel 1 Block Transfer Pending bit position. */ + +#define DMA_CH0PEND_bm 0x01 /* Channel 0 Block Transfer Pending bit mask. */ +#define DMA_CH0PEND_bp 0 /* Channel 0 Block Transfer Pending bit position. */ + +/* EVSYS - Event System */ +/* EVSYS.CH0MUX bit masks and bit positions */ +#define EVSYS_CHMUX_gm 0xFF /* Event Channel 0 Multiplexer group mask. */ +#define EVSYS_CHMUX_gp 0 /* Event Channel 0 Multiplexer group position. */ +#define EVSYS_CHMUX0_bm (1<<0) /* Event Channel 0 Multiplexer bit 0 mask. */ +#define EVSYS_CHMUX0_bp 0 /* Event Channel 0 Multiplexer bit 0 position. */ +#define EVSYS_CHMUX1_bm (1<<1) /* Event Channel 0 Multiplexer bit 1 mask. */ +#define EVSYS_CHMUX1_bp 1 /* Event Channel 0 Multiplexer bit 1 position. */ +#define EVSYS_CHMUX2_bm (1<<2) /* Event Channel 0 Multiplexer bit 2 mask. */ +#define EVSYS_CHMUX2_bp 2 /* Event Channel 0 Multiplexer bit 2 position. */ +#define EVSYS_CHMUX3_bm (1<<3) /* Event Channel 0 Multiplexer bit 3 mask. */ +#define EVSYS_CHMUX3_bp 3 /* Event Channel 0 Multiplexer bit 3 position. */ +#define EVSYS_CHMUX4_bm (1<<4) /* Event Channel 0 Multiplexer bit 4 mask. */ +#define EVSYS_CHMUX4_bp 4 /* Event Channel 0 Multiplexer bit 4 position. */ +#define EVSYS_CHMUX5_bm (1<<5) /* Event Channel 0 Multiplexer bit 5 mask. */ +#define EVSYS_CHMUX5_bp 5 /* Event Channel 0 Multiplexer bit 5 position. */ +#define EVSYS_CHMUX6_bm (1<<6) /* Event Channel 0 Multiplexer bit 6 mask. */ +#define EVSYS_CHMUX6_bp 6 /* Event Channel 0 Multiplexer bit 6 position. */ +#define EVSYS_CHMUX7_bm (1<<7) /* Event Channel 0 Multiplexer bit 7 mask. */ +#define EVSYS_CHMUX7_bp 7 /* Event Channel 0 Multiplexer bit 7 position. */ + +/* EVSYS.CH1MUX bit masks and bit positions */ +/* EVSYS_CHMUX Predefined. */ +/* EVSYS_CHMUX Predefined. */ + +/* EVSYS.CH2MUX bit masks and bit positions */ +/* EVSYS_CHMUX Predefined. */ +/* EVSYS_CHMUX Predefined. */ + +/* EVSYS.CH3MUX bit masks and bit positions */ +/* EVSYS_CHMUX Predefined. */ +/* EVSYS_CHMUX Predefined. */ + +/* EVSYS.CH4MUX bit masks and bit positions */ +/* EVSYS_CHMUX Predefined. */ +/* EVSYS_CHMUX Predefined. */ + +/* EVSYS.CH5MUX bit masks and bit positions */ +/* EVSYS_CHMUX Predefined. */ +/* EVSYS_CHMUX Predefined. */ + +/* EVSYS.CH6MUX bit masks and bit positions */ +/* EVSYS_CHMUX Predefined. */ +/* EVSYS_CHMUX Predefined. */ + +/* EVSYS.CH7MUX bit masks and bit positions */ +/* EVSYS_CHMUX Predefined. */ +/* EVSYS_CHMUX Predefined. */ + +/* EVSYS.CH0CTRL bit masks and bit positions */ +#define EVSYS_QDIRM_gm 0x60 /* Quadrature Decoder Index Recognition Mode group mask. */ +#define EVSYS_QDIRM_gp 5 /* Quadrature Decoder Index Recognition Mode group position. */ +#define EVSYS_QDIRM0_bm (1<<5) /* Quadrature Decoder Index Recognition Mode bit 0 mask. */ +#define EVSYS_QDIRM0_bp 5 /* Quadrature Decoder Index Recognition Mode bit 0 position. */ +#define EVSYS_QDIRM1_bm (1<<6) /* Quadrature Decoder Index Recognition Mode bit 1 mask. */ +#define EVSYS_QDIRM1_bp 6 /* Quadrature Decoder Index Recognition Mode bit 1 position. */ + +#define EVSYS_QDIEN_bm 0x10 /* Quadrature Decoder Index Enable bit mask. */ +#define EVSYS_QDIEN_bp 4 /* Quadrature Decoder Index Enable bit position. */ + +#define EVSYS_QDEN_bm 0x08 /* Quadrature Decoder Enable bit mask. */ +#define EVSYS_QDEN_bp 3 /* Quadrature Decoder Enable bit position. */ + +#define EVSYS_DIGFILT_gm 0x07 /* Digital Filter group mask. */ +#define EVSYS_DIGFILT_gp 0 /* Digital Filter group position. */ +#define EVSYS_DIGFILT0_bm (1<<0) /* Digital Filter bit 0 mask. */ +#define EVSYS_DIGFILT0_bp 0 /* Digital Filter bit 0 position. */ +#define EVSYS_DIGFILT1_bm (1<<1) /* Digital Filter bit 1 mask. */ +#define EVSYS_DIGFILT1_bp 1 /* Digital Filter bit 1 position. */ +#define EVSYS_DIGFILT2_bm (1<<2) /* Digital Filter bit 2 mask. */ +#define EVSYS_DIGFILT2_bp 2 /* Digital Filter bit 2 position. */ + +/* EVSYS.CH1CTRL bit masks and bit positions */ +/* EVSYS_DIGFILT Predefined. */ +/* EVSYS_DIGFILT Predefined. */ + +/* EVSYS.CH2CTRL bit masks and bit positions */ +/* EVSYS_QDIRM Predefined. */ +/* EVSYS_QDIRM Predefined. */ + +/* EVSYS_QDIEN Predefined. */ +/* EVSYS_QDIEN Predefined. */ + +/* EVSYS_QDEN Predefined. */ +/* EVSYS_QDEN Predefined. */ + +/* EVSYS_DIGFILT Predefined. */ +/* EVSYS_DIGFILT Predefined. */ + +/* EVSYS.CH3CTRL bit masks and bit positions */ +/* EVSYS_DIGFILT Predefined. */ +/* EVSYS_DIGFILT Predefined. */ + +/* EVSYS.CH4CTRL bit masks and bit positions */ +/* EVSYS_QDIRM Predefined. */ +/* EVSYS_QDIRM Predefined. */ + +/* EVSYS_QDIEN Predefined. */ +/* EVSYS_QDIEN Predefined. */ + +/* EVSYS_QDEN Predefined. */ +/* EVSYS_QDEN Predefined. */ + +/* EVSYS_DIGFILT Predefined. */ +/* EVSYS_DIGFILT Predefined. */ + +/* EVSYS.CH5CTRL bit masks and bit positions */ +/* EVSYS_DIGFILT Predefined. */ +/* EVSYS_DIGFILT Predefined. */ + +/* EVSYS.CH6CTRL bit masks and bit positions */ +/* EVSYS_DIGFILT Predefined. */ +/* EVSYS_DIGFILT Predefined. */ + +/* EVSYS.CH7CTRL bit masks and bit positions */ +/* EVSYS_DIGFILT Predefined. */ +/* EVSYS_DIGFILT Predefined. */ + +/* NVM - Non Volatile Memory Controller */ +/* NVM.CMD bit masks and bit positions */ +#define NVM_CMD_gm 0x7F /* Command group mask. */ +#define NVM_CMD_gp 0 /* Command group position. */ +#define NVM_CMD0_bm (1<<0) /* Command bit 0 mask. */ +#define NVM_CMD0_bp 0 /* Command bit 0 position. */ +#define NVM_CMD1_bm (1<<1) /* Command bit 1 mask. */ +#define NVM_CMD1_bp 1 /* Command bit 1 position. */ +#define NVM_CMD2_bm (1<<2) /* Command bit 2 mask. */ +#define NVM_CMD2_bp 2 /* Command bit 2 position. */ +#define NVM_CMD3_bm (1<<3) /* Command bit 3 mask. */ +#define NVM_CMD3_bp 3 /* Command bit 3 position. */ +#define NVM_CMD4_bm (1<<4) /* Command bit 4 mask. */ +#define NVM_CMD4_bp 4 /* Command bit 4 position. */ +#define NVM_CMD5_bm (1<<5) /* Command bit 5 mask. */ +#define NVM_CMD5_bp 5 /* Command bit 5 position. */ +#define NVM_CMD6_bm (1<<6) /* Command bit 6 mask. */ +#define NVM_CMD6_bp 6 /* Command bit 6 position. */ + +/* NVM.CTRLA bit masks and bit positions */ +#define NVM_CMDEX_bm 0x01 /* Command Execute bit mask. */ +#define NVM_CMDEX_bp 0 /* Command Execute bit position. */ + +/* NVM.CTRLB bit masks and bit positions */ +#define NVM_EEMAPEN_bm 0x08 /* EEPROM Mapping Enable bit mask. */ +#define NVM_EEMAPEN_bp 3 /* EEPROM Mapping Enable bit position. */ + +#define NVM_FPRM_bm 0x04 /* Flash Power Reduction Enable bit mask. */ +#define NVM_FPRM_bp 2 /* Flash Power Reduction Enable bit position. */ + +#define NVM_EPRM_bm 0x02 /* EEPROM Power Reduction Enable bit mask. */ +#define NVM_EPRM_bp 1 /* EEPROM Power Reduction Enable bit position. */ + +#define NVM_SPMLOCK_bm 0x01 /* SPM Lock bit mask. */ +#define NVM_SPMLOCK_bp 0 /* SPM Lock bit position. */ + +/* NVM.INTCTRL bit masks and bit positions */ +#define NVM_SPMLVL_gm 0x0C /* SPM Interrupt Level group mask. */ +#define NVM_SPMLVL_gp 2 /* SPM Interrupt Level group position. */ +#define NVM_SPMLVL0_bm (1<<2) /* SPM Interrupt Level bit 0 mask. */ +#define NVM_SPMLVL0_bp 2 /* SPM Interrupt Level bit 0 position. */ +#define NVM_SPMLVL1_bm (1<<3) /* SPM Interrupt Level bit 1 mask. */ +#define NVM_SPMLVL1_bp 3 /* SPM Interrupt Level bit 1 position. */ + +#define NVM_EELVL_gm 0x03 /* EEPROM Interrupt Level group mask. */ +#define NVM_EELVL_gp 0 /* EEPROM Interrupt Level group position. */ +#define NVM_EELVL0_bm (1<<0) /* EEPROM Interrupt Level bit 0 mask. */ +#define NVM_EELVL0_bp 0 /* EEPROM Interrupt Level bit 0 position. */ +#define NVM_EELVL1_bm (1<<1) /* EEPROM Interrupt Level bit 1 mask. */ +#define NVM_EELVL1_bp 1 /* EEPROM Interrupt Level bit 1 position. */ + +/* NVM.STATUS bit masks and bit positions */ +#define NVM_NVMBUSY_bm 0x80 /* Non-volatile Memory Busy bit mask. */ +#define NVM_NVMBUSY_bp 7 /* Non-volatile Memory Busy bit position. */ + +#define NVM_FBUSY_bm 0x40 /* Flash Memory Busy bit mask. */ +#define NVM_FBUSY_bp 6 /* Flash Memory Busy bit position. */ + +#define NVM_EELOAD_bm 0x02 /* EEPROM Page Buffer Active Loading bit mask. */ +#define NVM_EELOAD_bp 1 /* EEPROM Page Buffer Active Loading bit position. */ + +#define NVM_FLOAD_bm 0x01 /* Flash Page Buffer Active Loading bit mask. */ +#define NVM_FLOAD_bp 0 /* Flash Page Buffer Active Loading bit position. */ + +/* NVM.LOCKBITS bit masks and bit positions */ +#define NVM_BLBB_gm 0xC0 /* Boot Lock Bits - Boot Section group mask. */ +#define NVM_BLBB_gp 6 /* Boot Lock Bits - Boot Section group position. */ +#define NVM_BLBB0_bm (1<<6) /* Boot Lock Bits - Boot Section bit 0 mask. */ +#define NVM_BLBB0_bp 6 /* Boot Lock Bits - Boot Section bit 0 position. */ +#define NVM_BLBB1_bm (1<<7) /* Boot Lock Bits - Boot Section bit 1 mask. */ +#define NVM_BLBB1_bp 7 /* Boot Lock Bits - Boot Section bit 1 position. */ + +#define NVM_BLBA_gm 0x30 /* Boot Lock Bits - Application Section group mask. */ +#define NVM_BLBA_gp 4 /* Boot Lock Bits - Application Section group position. */ +#define NVM_BLBA0_bm (1<<4) /* Boot Lock Bits - Application Section bit 0 mask. */ +#define NVM_BLBA0_bp 4 /* Boot Lock Bits - Application Section bit 0 position. */ +#define NVM_BLBA1_bm (1<<5) /* Boot Lock Bits - Application Section bit 1 mask. */ +#define NVM_BLBA1_bp 5 /* Boot Lock Bits - Application Section bit 1 position. */ + +#define NVM_BLBAT_gm 0x0C /* Boot Lock Bits - Application Table group mask. */ +#define NVM_BLBAT_gp 2 /* Boot Lock Bits - Application Table group position. */ +#define NVM_BLBAT0_bm (1<<2) /* Boot Lock Bits - Application Table bit 0 mask. */ +#define NVM_BLBAT0_bp 2 /* Boot Lock Bits - Application Table bit 0 position. */ +#define NVM_BLBAT1_bm (1<<3) /* Boot Lock Bits - Application Table bit 1 mask. */ +#define NVM_BLBAT1_bp 3 /* Boot Lock Bits - Application Table bit 1 position. */ + +#define NVM_LB_gm 0x03 /* Lock Bits group mask. */ +#define NVM_LB_gp 0 /* Lock Bits group position. */ +#define NVM_LB0_bm (1<<0) /* Lock Bits bit 0 mask. */ +#define NVM_LB0_bp 0 /* Lock Bits bit 0 position. */ +#define NVM_LB1_bm (1<<1) /* Lock Bits bit 1 mask. */ +#define NVM_LB1_bp 1 /* Lock Bits bit 1 position. */ + +/* AC - Analog Comparator */ +/* AC.AC0CTRL bit masks and bit positions */ +#define AC_INTMODE_gm 0xC0 /* Interrupt Mode group mask. */ +#define AC_INTMODE_gp 6 /* Interrupt Mode group position. */ +#define AC_INTMODE0_bm (1<<6) /* Interrupt Mode bit 0 mask. */ +#define AC_INTMODE0_bp 6 /* Interrupt Mode bit 0 position. */ +#define AC_INTMODE1_bm (1<<7) /* Interrupt Mode bit 1 mask. */ +#define AC_INTMODE1_bp 7 /* Interrupt Mode bit 1 position. */ + +#define AC_INTLVL_gm 0x30 /* Interrupt Level group mask. */ +#define AC_INTLVL_gp 4 /* Interrupt Level group position. */ +#define AC_INTLVL0_bm (1<<4) /* Interrupt Level bit 0 mask. */ +#define AC_INTLVL0_bp 4 /* Interrupt Level bit 0 position. */ +#define AC_INTLVL1_bm (1<<5) /* Interrupt Level bit 1 mask. */ +#define AC_INTLVL1_bp 5 /* Interrupt Level bit 1 position. */ + +#define AC_HSMODE_bm 0x08 /* High-speed Mode bit mask. */ +#define AC_HSMODE_bp 3 /* High-speed Mode bit position. */ + +#define AC_HYSMODE_gm 0x06 /* Hysteresis Mode group mask. */ +#define AC_HYSMODE_gp 1 /* Hysteresis Mode group position. */ +#define AC_HYSMODE0_bm (1<<1) /* Hysteresis Mode bit 0 mask. */ +#define AC_HYSMODE0_bp 1 /* Hysteresis Mode bit 0 position. */ +#define AC_HYSMODE1_bm (1<<2) /* Hysteresis Mode bit 1 mask. */ +#define AC_HYSMODE1_bp 2 /* Hysteresis Mode bit 1 position. */ + +#define AC_ENABLE_bm 0x01 /* Enable bit mask. */ +#define AC_ENABLE_bp 0 /* Enable bit position. */ + +/* AC.AC1CTRL bit masks and bit positions */ +/* AC_INTMODE Predefined. */ +/* AC_INTMODE Predefined. */ + +/* AC_INTLVL Predefined. */ +/* AC_INTLVL Predefined. */ + +/* AC_HSMODE Predefined. */ +/* AC_HSMODE Predefined. */ + +/* AC_HYSMODE Predefined. */ +/* AC_HYSMODE Predefined. */ + +/* AC_ENABLE Predefined. */ +/* AC_ENABLE Predefined. */ + +/* AC.AC0MUXCTRL bit masks and bit positions */ +#define AC_MUXPOS_gm 0x38 /* MUX Positive Input group mask. */ +#define AC_MUXPOS_gp 3 /* MUX Positive Input group position. */ +#define AC_MUXPOS0_bm (1<<3) /* MUX Positive Input bit 0 mask. */ +#define AC_MUXPOS0_bp 3 /* MUX Positive Input bit 0 position. */ +#define AC_MUXPOS1_bm (1<<4) /* MUX Positive Input bit 1 mask. */ +#define AC_MUXPOS1_bp 4 /* MUX Positive Input bit 1 position. */ +#define AC_MUXPOS2_bm (1<<5) /* MUX Positive Input bit 2 mask. */ +#define AC_MUXPOS2_bp 5 /* MUX Positive Input bit 2 position. */ + +#define AC_MUXNEG_gm 0x07 /* MUX Negative Input group mask. */ +#define AC_MUXNEG_gp 0 /* MUX Negative Input group position. */ +#define AC_MUXNEG0_bm (1<<0) /* MUX Negative Input bit 0 mask. */ +#define AC_MUXNEG0_bp 0 /* MUX Negative Input bit 0 position. */ +#define AC_MUXNEG1_bm (1<<1) /* MUX Negative Input bit 1 mask. */ +#define AC_MUXNEG1_bp 1 /* MUX Negative Input bit 1 position. */ +#define AC_MUXNEG2_bm (1<<2) /* MUX Negative Input bit 2 mask. */ +#define AC_MUXNEG2_bp 2 /* MUX Negative Input bit 2 position. */ + +/* AC.AC1MUXCTRL bit masks and bit positions */ +/* AC_MUXPOS Predefined. */ +/* AC_MUXPOS Predefined. */ + +/* AC_MUXNEG Predefined. */ +/* AC_MUXNEG Predefined. */ + +/* AC.CTRLA bit masks and bit positions */ +#define AC_AC1OUT_bm 0x02 /* Analog Comparator 1 Output Enable bit mask. */ +#define AC_AC1OUT_bp 1 /* Analog Comparator 1 Output Enable bit position. */ + +#define AC_AC0OUT_bm 0x01 /* Analog Comparator 0 Output Enable bit mask. */ +#define AC_AC0OUT_bp 0 /* Analog Comparator 0 Output Enable bit position. */ + +/* AC.CTRLB bit masks and bit positions */ +#define AC_SCALEFAC_gm 0x3F /* VCC Voltage Scaler Factor group mask. */ +#define AC_SCALEFAC_gp 0 /* VCC Voltage Scaler Factor group position. */ +#define AC_SCALEFAC0_bm (1<<0) /* VCC Voltage Scaler Factor bit 0 mask. */ +#define AC_SCALEFAC0_bp 0 /* VCC Voltage Scaler Factor bit 0 position. */ +#define AC_SCALEFAC1_bm (1<<1) /* VCC Voltage Scaler Factor bit 1 mask. */ +#define AC_SCALEFAC1_bp 1 /* VCC Voltage Scaler Factor bit 1 position. */ +#define AC_SCALEFAC2_bm (1<<2) /* VCC Voltage Scaler Factor bit 2 mask. */ +#define AC_SCALEFAC2_bp 2 /* VCC Voltage Scaler Factor bit 2 position. */ +#define AC_SCALEFAC3_bm (1<<3) /* VCC Voltage Scaler Factor bit 3 mask. */ +#define AC_SCALEFAC3_bp 3 /* VCC Voltage Scaler Factor bit 3 position. */ +#define AC_SCALEFAC4_bm (1<<4) /* VCC Voltage Scaler Factor bit 4 mask. */ +#define AC_SCALEFAC4_bp 4 /* VCC Voltage Scaler Factor bit 4 position. */ +#define AC_SCALEFAC5_bm (1<<5) /* VCC Voltage Scaler Factor bit 5 mask. */ +#define AC_SCALEFAC5_bp 5 /* VCC Voltage Scaler Factor bit 5 position. */ + +/* AC.WINCTRL bit masks and bit positions */ +#define AC_WEN_bm 0x10 /* Window Mode Enable bit mask. */ +#define AC_WEN_bp 4 /* Window Mode Enable bit position. */ + +#define AC_WINTMODE_gm 0x0C /* Window Interrupt Mode group mask. */ +#define AC_WINTMODE_gp 2 /* Window Interrupt Mode group position. */ +#define AC_WINTMODE0_bm (1<<2) /* Window Interrupt Mode bit 0 mask. */ +#define AC_WINTMODE0_bp 2 /* Window Interrupt Mode bit 0 position. */ +#define AC_WINTMODE1_bm (1<<3) /* Window Interrupt Mode bit 1 mask. */ +#define AC_WINTMODE1_bp 3 /* Window Interrupt Mode bit 1 position. */ + +#define AC_WINTLVL_gm 0x03 /* Window Interrupt Level group mask. */ +#define AC_WINTLVL_gp 0 /* Window Interrupt Level group position. */ +#define AC_WINTLVL0_bm (1<<0) /* Window Interrupt Level bit 0 mask. */ +#define AC_WINTLVL0_bp 0 /* Window Interrupt Level bit 0 position. */ +#define AC_WINTLVL1_bm (1<<1) /* Window Interrupt Level bit 1 mask. */ +#define AC_WINTLVL1_bp 1 /* Window Interrupt Level bit 1 position. */ + +/* AC.STATUS bit masks and bit positions */ +#define AC_WSTATE_gm 0xC0 /* Window Mode State group mask. */ +#define AC_WSTATE_gp 6 /* Window Mode State group position. */ +#define AC_WSTATE0_bm (1<<6) /* Window Mode State bit 0 mask. */ +#define AC_WSTATE0_bp 6 /* Window Mode State bit 0 position. */ +#define AC_WSTATE1_bm (1<<7) /* Window Mode State bit 1 mask. */ +#define AC_WSTATE1_bp 7 /* Window Mode State bit 1 position. */ + +#define AC_AC1STATE_bm 0x20 /* Analog Comparator 1 State bit mask. */ +#define AC_AC1STATE_bp 5 /* Analog Comparator 1 State bit position. */ + +#define AC_AC0STATE_bm 0x10 /* Analog Comparator 0 State bit mask. */ +#define AC_AC0STATE_bp 4 /* Analog Comparator 0 State bit position. */ + +#define AC_WIF_bm 0x04 /* Window Mode Interrupt Flag bit mask. */ +#define AC_WIF_bp 2 /* Window Mode Interrupt Flag bit position. */ + +#define AC_AC1IF_bm 0x02 /* Analog Comparator 1 Interrupt Flag bit mask. */ +#define AC_AC1IF_bp 1 /* Analog Comparator 1 Interrupt Flag bit position. */ + +#define AC_AC0IF_bm 0x01 /* Analog Comparator 0 Interrupt Flag bit mask. */ +#define AC_AC0IF_bp 0 /* Analog Comparator 0 Interrupt Flag bit position. */ + +/* ADC - Analog/Digital Converter */ +/* ADC_CH.CTRL bit masks and bit positions */ +#define ADC_CH_START_bm 0x80 /* Channel Start Conversion bit mask. */ +#define ADC_CH_START_bp 7 /* Channel Start Conversion bit position. */ + +#define ADC_CH_GAIN_gm 0x1C /* Gain Factor group mask. */ +#define ADC_CH_GAIN_gp 2 /* Gain Factor group position. */ +#define ADC_CH_GAIN0_bm (1<<2) /* Gain Factor bit 0 mask. */ +#define ADC_CH_GAIN0_bp 2 /* Gain Factor bit 0 position. */ +#define ADC_CH_GAIN1_bm (1<<3) /* Gain Factor bit 1 mask. */ +#define ADC_CH_GAIN1_bp 3 /* Gain Factor bit 1 position. */ +#define ADC_CH_GAIN2_bm (1<<4) /* Gain Factor bit 2 mask. */ +#define ADC_CH_GAIN2_bp 4 /* Gain Factor bit 2 position. */ + +#define ADC_CH_INPUTMODE_gm 0x03 /* Input Mode Select group mask. */ +#define ADC_CH_INPUTMODE_gp 0 /* Input Mode Select group position. */ +#define ADC_CH_INPUTMODE0_bm (1<<0) /* Input Mode Select bit 0 mask. */ +#define ADC_CH_INPUTMODE0_bp 0 /* Input Mode Select bit 0 position. */ +#define ADC_CH_INPUTMODE1_bm (1<<1) /* Input Mode Select bit 1 mask. */ +#define ADC_CH_INPUTMODE1_bp 1 /* Input Mode Select bit 1 position. */ + +/* ADC_CH.MUXCTRL bit masks and bit positions */ +#define ADC_CH_MUXPOS_gm 0x78 /* MUX selection on Positive ADC input group mask. */ +#define ADC_CH_MUXPOS_gp 3 /* MUX selection on Positive ADC input group position. */ +#define ADC_CH_MUXPOS0_bm (1<<3) /* MUX selection on Positive ADC input bit 0 mask. */ +#define ADC_CH_MUXPOS0_bp 3 /* MUX selection on Positive ADC input bit 0 position. */ +#define ADC_CH_MUXPOS1_bm (1<<4) /* MUX selection on Positive ADC input bit 1 mask. */ +#define ADC_CH_MUXPOS1_bp 4 /* MUX selection on Positive ADC input bit 1 position. */ +#define ADC_CH_MUXPOS2_bm (1<<5) /* MUX selection on Positive ADC input bit 2 mask. */ +#define ADC_CH_MUXPOS2_bp 5 /* MUX selection on Positive ADC input bit 2 position. */ +#define ADC_CH_MUXPOS3_bm (1<<6) /* MUX selection on Positive ADC input bit 3 mask. */ +#define ADC_CH_MUXPOS3_bp 6 /* MUX selection on Positive ADC input bit 3 position. */ + +#define ADC_CH_MUXINT_gm 0x78 /* MUX selection on Internal ADC input group mask. */ +#define ADC_CH_MUXINT_gp 3 /* MUX selection on Internal ADC input group position. */ +#define ADC_CH_MUXINT0_bm (1<<3) /* MUX selection on Internal ADC input bit 0 mask. */ +#define ADC_CH_MUXINT0_bp 3 /* MUX selection on Internal ADC input bit 0 position. */ +#define ADC_CH_MUXINT1_bm (1<<4) /* MUX selection on Internal ADC input bit 1 mask. */ +#define ADC_CH_MUXINT1_bp 4 /* MUX selection on Internal ADC input bit 1 position. */ +#define ADC_CH_MUXINT2_bm (1<<5) /* MUX selection on Internal ADC input bit 2 mask. */ +#define ADC_CH_MUXINT2_bp 5 /* MUX selection on Internal ADC input bit 2 position. */ +#define ADC_CH_MUXINT3_bm (1<<6) /* MUX selection on Internal ADC input bit 3 mask. */ +#define ADC_CH_MUXINT3_bp 6 /* MUX selection on Internal ADC input bit 3 position. */ + +#define ADC_CH_MUXNEG_gm 0x07 /* MUX selection on Negative ADC input group mask. */ +#define ADC_CH_MUXNEG_gp 0 /* MUX selection on Negative ADC input group position. */ +#define ADC_CH_MUXNEG0_bm (1<<0) /* MUX selection on Negative ADC input bit 0 mask. */ +#define ADC_CH_MUXNEG0_bp 0 /* MUX selection on Negative ADC input bit 0 position. */ +#define ADC_CH_MUXNEG1_bm (1<<1) /* MUX selection on Negative ADC input bit 1 mask. */ +#define ADC_CH_MUXNEG1_bp 1 /* MUX selection on Negative ADC input bit 1 position. */ +#define ADC_CH_MUXNEG2_bm (1<<2) /* MUX selection on Negative ADC input bit 2 mask. */ +#define ADC_CH_MUXNEG2_bp 2 /* MUX selection on Negative ADC input bit 2 position. */ + +/* ADC_CH.INTCTRL bit masks and bit positions */ +#define ADC_CH_INTMODE_gm 0x0C /* Interrupt Mode group mask. */ +#define ADC_CH_INTMODE_gp 2 /* Interrupt Mode group position. */ +#define ADC_CH_INTMODE0_bm (1<<2) /* Interrupt Mode bit 0 mask. */ +#define ADC_CH_INTMODE0_bp 2 /* Interrupt Mode bit 0 position. */ +#define ADC_CH_INTMODE1_bm (1<<3) /* Interrupt Mode bit 1 mask. */ +#define ADC_CH_INTMODE1_bp 3 /* Interrupt Mode bit 1 position. */ + +#define ADC_CH_INTLVL_gm 0x03 /* Interrupt Level group mask. */ +#define ADC_CH_INTLVL_gp 0 /* Interrupt Level group position. */ +#define ADC_CH_INTLVL0_bm (1<<0) /* Interrupt Level bit 0 mask. */ +#define ADC_CH_INTLVL0_bp 0 /* Interrupt Level bit 0 position. */ +#define ADC_CH_INTLVL1_bm (1<<1) /* Interrupt Level bit 1 mask. */ +#define ADC_CH_INTLVL1_bp 1 /* Interrupt Level bit 1 position. */ + +/* ADC_CH.INTFLAGS bit masks and bit positions */ +#define ADC_CH_CHIF_bm 0x01 /* Channel Interrupt Flag bit mask. */ +#define ADC_CH_CHIF_bp 0 /* Channel Interrupt Flag bit position. */ + +/* ADC_CH.SCAN bit masks and bit positions */ +#define ADC_CH_OFFSET_gm 0xF0 /* Positive MUX setting offset group mask. */ +#define ADC_CH_OFFSET_gp 4 /* Positive MUX setting offset group position. */ +#define ADC_CH_OFFSET0_bm (1<<4) /* Positive MUX setting offset bit 0 mask. */ +#define ADC_CH_OFFSET0_bp 4 /* Positive MUX setting offset bit 0 position. */ +#define ADC_CH_OFFSET1_bm (1<<5) /* Positive MUX setting offset bit 1 mask. */ +#define ADC_CH_OFFSET1_bp 5 /* Positive MUX setting offset bit 1 position. */ +#define ADC_CH_OFFSET2_bm (1<<6) /* Positive MUX setting offset bit 2 mask. */ +#define ADC_CH_OFFSET2_bp 6 /* Positive MUX setting offset bit 2 position. */ +#define ADC_CH_OFFSET3_bm (1<<7) /* Positive MUX setting offset bit 3 mask. */ +#define ADC_CH_OFFSET3_bp 7 /* Positive MUX setting offset bit 3 position. */ + +#define ADC_CH_SCANNUM_gm 0x0F /* Number of Channels included in scan group mask. */ +#define ADC_CH_SCANNUM_gp 0 /* Number of Channels included in scan group position. */ +#define ADC_CH_SCANNUM0_bm (1<<0) /* Number of Channels included in scan bit 0 mask. */ +#define ADC_CH_SCANNUM0_bp 0 /* Number of Channels included in scan bit 0 position. */ +#define ADC_CH_SCANNUM1_bm (1<<1) /* Number of Channels included in scan bit 1 mask. */ +#define ADC_CH_SCANNUM1_bp 1 /* Number of Channels included in scan bit 1 position. */ +#define ADC_CH_SCANNUM2_bm (1<<2) /* Number of Channels included in scan bit 2 mask. */ +#define ADC_CH_SCANNUM2_bp 2 /* Number of Channels included in scan bit 2 position. */ +#define ADC_CH_SCANNUM3_bm (1<<3) /* Number of Channels included in scan bit 3 mask. */ +#define ADC_CH_SCANNUM3_bp 3 /* Number of Channels included in scan bit 3 position. */ + +/* ADC.CTRLA bit masks and bit positions */ +#define ADC_DMASEL_gm 0xC0 /* DMA Selection group mask. */ +#define ADC_DMASEL_gp 6 /* DMA Selection group position. */ +#define ADC_DMASEL0_bm (1<<6) /* DMA Selection bit 0 mask. */ +#define ADC_DMASEL0_bp 6 /* DMA Selection bit 0 position. */ +#define ADC_DMASEL1_bm (1<<7) /* DMA Selection bit 1 mask. */ +#define ADC_DMASEL1_bp 7 /* DMA Selection bit 1 position. */ + +#define ADC_CH3START_bm 0x20 /* Channel 3 Start Conversion bit mask. */ +#define ADC_CH3START_bp 5 /* Channel 3 Start Conversion bit position. */ + +#define ADC_CH2START_bm 0x10 /* Channel 2 Start Conversion bit mask. */ +#define ADC_CH2START_bp 4 /* Channel 2 Start Conversion bit position. */ + +#define ADC_CH1START_bm 0x08 /* Channel 1 Start Conversion bit mask. */ +#define ADC_CH1START_bp 3 /* Channel 1 Start Conversion bit position. */ + +#define ADC_CH0START_bm 0x04 /* Channel 0 Start Conversion bit mask. */ +#define ADC_CH0START_bp 2 /* Channel 0 Start Conversion bit position. */ + +#define ADC_FLUSH_bm 0x02 /* Flush Pipeline bit mask. */ +#define ADC_FLUSH_bp 1 /* Flush Pipeline bit position. */ + +#define ADC_ENABLE_bm 0x01 /* Enable ADC bit mask. */ +#define ADC_ENABLE_bp 0 /* Enable ADC bit position. */ + +/* ADC.CTRLB bit masks and bit positions */ +#define ADC_IMPMODE_bm 0x80 /* Gain Stage Impedance Mode bit mask. */ +#define ADC_IMPMODE_bp 7 /* Gain Stage Impedance Mode bit position. */ + +#define ADC_CURRLIMIT_gm 0x60 /* Current Limitation group mask. */ +#define ADC_CURRLIMIT_gp 5 /* Current Limitation group position. */ +#define ADC_CURRLIMIT0_bm (1<<5) /* Current Limitation bit 0 mask. */ +#define ADC_CURRLIMIT0_bp 5 /* Current Limitation bit 0 position. */ +#define ADC_CURRLIMIT1_bm (1<<6) /* Current Limitation bit 1 mask. */ +#define ADC_CURRLIMIT1_bp 6 /* Current Limitation bit 1 position. */ + +#define ADC_CONMODE_bm 0x10 /* Conversion Mode bit mask. */ +#define ADC_CONMODE_bp 4 /* Conversion Mode bit position. */ + +#define ADC_FREERUN_bm 0x08 /* Free Running Mode Enable bit mask. */ +#define ADC_FREERUN_bp 3 /* Free Running Mode Enable bit position. */ + +#define ADC_RESOLUTION_gm 0x06 /* Result Resolution group mask. */ +#define ADC_RESOLUTION_gp 1 /* Result Resolution group position. */ +#define ADC_RESOLUTION0_bm (1<<1) /* Result Resolution bit 0 mask. */ +#define ADC_RESOLUTION0_bp 1 /* Result Resolution bit 0 position. */ +#define ADC_RESOLUTION1_bm (1<<2) /* Result Resolution bit 1 mask. */ +#define ADC_RESOLUTION1_bp 2 /* Result Resolution bit 1 position. */ + +/* ADC.REFCTRL bit masks and bit positions */ +#define ADC_REFSEL_gm 0x70 /* Reference Selection group mask. */ +#define ADC_REFSEL_gp 4 /* Reference Selection group position. */ +#define ADC_REFSEL0_bm (1<<4) /* Reference Selection bit 0 mask. */ +#define ADC_REFSEL0_bp 4 /* Reference Selection bit 0 position. */ +#define ADC_REFSEL1_bm (1<<5) /* Reference Selection bit 1 mask. */ +#define ADC_REFSEL1_bp 5 /* Reference Selection bit 1 position. */ +#define ADC_REFSEL2_bm (1<<6) /* Reference Selection bit 2 mask. */ +#define ADC_REFSEL2_bp 6 /* Reference Selection bit 2 position. */ + +#define ADC_BANDGAP_bm 0x02 /* Bandgap enable bit mask. */ +#define ADC_BANDGAP_bp 1 /* Bandgap enable bit position. */ + +#define ADC_TEMPREF_bm 0x01 /* Temperature Reference Enable bit mask. */ +#define ADC_TEMPREF_bp 0 /* Temperature Reference Enable bit position. */ + +/* ADC.EVCTRL bit masks and bit positions */ +#define ADC_SWEEP_gm 0xC0 /* Channel Sweep Selection group mask. */ +#define ADC_SWEEP_gp 6 /* Channel Sweep Selection group position. */ +#define ADC_SWEEP0_bm (1<<6) /* Channel Sweep Selection bit 0 mask. */ +#define ADC_SWEEP0_bp 6 /* Channel Sweep Selection bit 0 position. */ +#define ADC_SWEEP1_bm (1<<7) /* Channel Sweep Selection bit 1 mask. */ +#define ADC_SWEEP1_bp 7 /* Channel Sweep Selection bit 1 position. */ + +#define ADC_EVSEL_gm 0x38 /* Event Input Select group mask. */ +#define ADC_EVSEL_gp 3 /* Event Input Select group position. */ +#define ADC_EVSEL0_bm (1<<3) /* Event Input Select bit 0 mask. */ +#define ADC_EVSEL0_bp 3 /* Event Input Select bit 0 position. */ +#define ADC_EVSEL1_bm (1<<4) /* Event Input Select bit 1 mask. */ +#define ADC_EVSEL1_bp 4 /* Event Input Select bit 1 position. */ +#define ADC_EVSEL2_bm (1<<5) /* Event Input Select bit 2 mask. */ +#define ADC_EVSEL2_bp 5 /* Event Input Select bit 2 position. */ + +#define ADC_EVACT_gm 0x07 /* Event Action Select group mask. */ +#define ADC_EVACT_gp 0 /* Event Action Select group position. */ +#define ADC_EVACT0_bm (1<<0) /* Event Action Select bit 0 mask. */ +#define ADC_EVACT0_bp 0 /* Event Action Select bit 0 position. */ +#define ADC_EVACT1_bm (1<<1) /* Event Action Select bit 1 mask. */ +#define ADC_EVACT1_bp 1 /* Event Action Select bit 1 position. */ +#define ADC_EVACT2_bm (1<<2) /* Event Action Select bit 2 mask. */ +#define ADC_EVACT2_bp 2 /* Event Action Select bit 2 position. */ + +/* ADC.PRESCALER bit masks and bit positions */ +#define ADC_PRESCALER_gm 0x07 /* Clock Prescaler Selection group mask. */ +#define ADC_PRESCALER_gp 0 /* Clock Prescaler Selection group position. */ +#define ADC_PRESCALER0_bm (1<<0) /* Clock Prescaler Selection bit 0 mask. */ +#define ADC_PRESCALER0_bp 0 /* Clock Prescaler Selection bit 0 position. */ +#define ADC_PRESCALER1_bm (1<<1) /* Clock Prescaler Selection bit 1 mask. */ +#define ADC_PRESCALER1_bp 1 /* Clock Prescaler Selection bit 1 position. */ +#define ADC_PRESCALER2_bm (1<<2) /* Clock Prescaler Selection bit 2 mask. */ +#define ADC_PRESCALER2_bp 2 /* Clock Prescaler Selection bit 2 position. */ + +/* ADC.INTFLAGS bit masks and bit positions */ +#define ADC_CH3IF_bm 0x08 /* Channel 3 Interrupt Flag bit mask. */ +#define ADC_CH3IF_bp 3 /* Channel 3 Interrupt Flag bit position. */ + +#define ADC_CH2IF_bm 0x04 /* Channel 2 Interrupt Flag bit mask. */ +#define ADC_CH2IF_bp 2 /* Channel 2 Interrupt Flag bit position. */ + +#define ADC_CH1IF_bm 0x02 /* Channel 1 Interrupt Flag bit mask. */ +#define ADC_CH1IF_bp 1 /* Channel 1 Interrupt Flag bit position. */ + +#define ADC_CH0IF_bm 0x01 /* Channel 0 Interrupt Flag bit mask. */ +#define ADC_CH0IF_bp 0 /* Channel 0 Interrupt Flag bit position. */ + +/* DAC - Digital/Analog Converter */ +/* DAC.CTRLA bit masks and bit positions */ +#define DAC_IDOEN_bm 0x10 /* Internal Output Enable bit mask. */ +#define DAC_IDOEN_bp 4 /* Internal Output Enable bit position. */ + +#define DAC_CH1EN_bm 0x08 /* Channel 1 Output Enable bit mask. */ +#define DAC_CH1EN_bp 3 /* Channel 1 Output Enable bit position. */ + +#define DAC_CH0EN_bm 0x04 /* Channel 0 Output Enable bit mask. */ +#define DAC_CH0EN_bp 2 /* Channel 0 Output Enable bit position. */ + +#define DAC_LPMODE_bm 0x02 /* Low Power Mode bit mask. */ +#define DAC_LPMODE_bp 1 /* Low Power Mode bit position. */ + +#define DAC_ENABLE_bm 0x01 /* Enable bit mask. */ +#define DAC_ENABLE_bp 0 /* Enable bit position. */ + +/* DAC.CTRLB bit masks and bit positions */ +#define DAC_CHSEL_gm 0x60 /* Channel Select group mask. */ +#define DAC_CHSEL_gp 5 /* Channel Select group position. */ +#define DAC_CHSEL0_bm (1<<5) /* Channel Select bit 0 mask. */ +#define DAC_CHSEL0_bp 5 /* Channel Select bit 0 position. */ +#define DAC_CHSEL1_bm (1<<6) /* Channel Select bit 1 mask. */ +#define DAC_CHSEL1_bp 6 /* Channel Select bit 1 position. */ + +#define DAC_CH1TRIG_bm 0x02 /* Channel 1 Event Trig Enable bit mask. */ +#define DAC_CH1TRIG_bp 1 /* Channel 1 Event Trig Enable bit position. */ + +#define DAC_CH0TRIG_bm 0x01 /* Channel 0 Event Trig Enable bit mask. */ +#define DAC_CH0TRIG_bp 0 /* Channel 0 Event Trig Enable bit position. */ + +/* DAC.CTRLC bit masks and bit positions */ +#define DAC_REFSEL_gm 0x18 /* Reference Select group mask. */ +#define DAC_REFSEL_gp 3 /* Reference Select group position. */ +#define DAC_REFSEL0_bm (1<<3) /* Reference Select bit 0 mask. */ +#define DAC_REFSEL0_bp 3 /* Reference Select bit 0 position. */ +#define DAC_REFSEL1_bm (1<<4) /* Reference Select bit 1 mask. */ +#define DAC_REFSEL1_bp 4 /* Reference Select bit 1 position. */ + +#define DAC_LEFTADJ_bm 0x01 /* Left-adjust Result bit mask. */ +#define DAC_LEFTADJ_bp 0 /* Left-adjust Result bit position. */ + +/* DAC.EVCTRL bit masks and bit positions */ +#define DAC_EVSPLIT_bm 0x08 /* Separate Event Channel Input for Channel 1 bit mask. */ +#define DAC_EVSPLIT_bp 3 /* Separate Event Channel Input for Channel 1 bit position. */ + +#define DAC_EVSEL_gm 0x07 /* Event Input Selection group mask. */ +#define DAC_EVSEL_gp 0 /* Event Input Selection group position. */ +#define DAC_EVSEL0_bm (1<<0) /* Event Input Selection bit 0 mask. */ +#define DAC_EVSEL0_bp 0 /* Event Input Selection bit 0 position. */ +#define DAC_EVSEL1_bm (1<<1) /* Event Input Selection bit 1 mask. */ +#define DAC_EVSEL1_bp 1 /* Event Input Selection bit 1 position. */ +#define DAC_EVSEL2_bm (1<<2) /* Event Input Selection bit 2 mask. */ +#define DAC_EVSEL2_bp 2 /* Event Input Selection bit 2 position. */ + +/* DAC.STATUS bit masks and bit positions */ +#define DAC_CH1DRE_bm 0x02 /* Channel 1 Data Register Empty bit mask. */ +#define DAC_CH1DRE_bp 1 /* Channel 1 Data Register Empty bit position. */ + +#define DAC_CH0DRE_bm 0x01 /* Channel 0 Data Register Empty bit mask. */ +#define DAC_CH0DRE_bp 0 /* Channel 0 Data Register Empty bit position. */ + +/* DAC.CH0GAINCAL bit masks and bit positions */ +#define DAC_CH0GAINCAL_gm 0x7F /* Gain Calibration group mask. */ +#define DAC_CH0GAINCAL_gp 0 /* Gain Calibration group position. */ +#define DAC_CH0GAINCAL0_bm (1<<0) /* Gain Calibration bit 0 mask. */ +#define DAC_CH0GAINCAL0_bp 0 /* Gain Calibration bit 0 position. */ +#define DAC_CH0GAINCAL1_bm (1<<1) /* Gain Calibration bit 1 mask. */ +#define DAC_CH0GAINCAL1_bp 1 /* Gain Calibration bit 1 position. */ +#define DAC_CH0GAINCAL2_bm (1<<2) /* Gain Calibration bit 2 mask. */ +#define DAC_CH0GAINCAL2_bp 2 /* Gain Calibration bit 2 position. */ +#define DAC_CH0GAINCAL3_bm (1<<3) /* Gain Calibration bit 3 mask. */ +#define DAC_CH0GAINCAL3_bp 3 /* Gain Calibration bit 3 position. */ +#define DAC_CH0GAINCAL4_bm (1<<4) /* Gain Calibration bit 4 mask. */ +#define DAC_CH0GAINCAL4_bp 4 /* Gain Calibration bit 4 position. */ +#define DAC_CH0GAINCAL5_bm (1<<5) /* Gain Calibration bit 5 mask. */ +#define DAC_CH0GAINCAL5_bp 5 /* Gain Calibration bit 5 position. */ +#define DAC_CH0GAINCAL6_bm (1<<6) /* Gain Calibration bit 6 mask. */ +#define DAC_CH0GAINCAL6_bp 6 /* Gain Calibration bit 6 position. */ + +/* DAC.CH0OFFSETCAL bit masks and bit positions */ +#define DAC_CH0OFFSETCAL_gm 0x7F /* Offset Calibration group mask. */ +#define DAC_CH0OFFSETCAL_gp 0 /* Offset Calibration group position. */ +#define DAC_CH0OFFSETCAL0_bm (1<<0) /* Offset Calibration bit 0 mask. */ +#define DAC_CH0OFFSETCAL0_bp 0 /* Offset Calibration bit 0 position. */ +#define DAC_CH0OFFSETCAL1_bm (1<<1) /* Offset Calibration bit 1 mask. */ +#define DAC_CH0OFFSETCAL1_bp 1 /* Offset Calibration bit 1 position. */ +#define DAC_CH0OFFSETCAL2_bm (1<<2) /* Offset Calibration bit 2 mask. */ +#define DAC_CH0OFFSETCAL2_bp 2 /* Offset Calibration bit 2 position. */ +#define DAC_CH0OFFSETCAL3_bm (1<<3) /* Offset Calibration bit 3 mask. */ +#define DAC_CH0OFFSETCAL3_bp 3 /* Offset Calibration bit 3 position. */ +#define DAC_CH0OFFSETCAL4_bm (1<<4) /* Offset Calibration bit 4 mask. */ +#define DAC_CH0OFFSETCAL4_bp 4 /* Offset Calibration bit 4 position. */ +#define DAC_CH0OFFSETCAL5_bm (1<<5) /* Offset Calibration bit 5 mask. */ +#define DAC_CH0OFFSETCAL5_bp 5 /* Offset Calibration bit 5 position. */ +#define DAC_CH0OFFSETCAL6_bm (1<<6) /* Offset Calibration bit 6 mask. */ +#define DAC_CH0OFFSETCAL6_bp 6 /* Offset Calibration bit 6 position. */ + +/* DAC.CH1GAINCAL bit masks and bit positions */ +#define DAC_CH1GAINCAL_gm 0x7F /* Gain Calibration group mask. */ +#define DAC_CH1GAINCAL_gp 0 /* Gain Calibration group position. */ +#define DAC_CH1GAINCAL0_bm (1<<0) /* Gain Calibration bit 0 mask. */ +#define DAC_CH1GAINCAL0_bp 0 /* Gain Calibration bit 0 position. */ +#define DAC_CH1GAINCAL1_bm (1<<1) /* Gain Calibration bit 1 mask. */ +#define DAC_CH1GAINCAL1_bp 1 /* Gain Calibration bit 1 position. */ +#define DAC_CH1GAINCAL2_bm (1<<2) /* Gain Calibration bit 2 mask. */ +#define DAC_CH1GAINCAL2_bp 2 /* Gain Calibration bit 2 position. */ +#define DAC_CH1GAINCAL3_bm (1<<3) /* Gain Calibration bit 3 mask. */ +#define DAC_CH1GAINCAL3_bp 3 /* Gain Calibration bit 3 position. */ +#define DAC_CH1GAINCAL4_bm (1<<4) /* Gain Calibration bit 4 mask. */ +#define DAC_CH1GAINCAL4_bp 4 /* Gain Calibration bit 4 position. */ +#define DAC_CH1GAINCAL5_bm (1<<5) /* Gain Calibration bit 5 mask. */ +#define DAC_CH1GAINCAL5_bp 5 /* Gain Calibration bit 5 position. */ +#define DAC_CH1GAINCAL6_bm (1<<6) /* Gain Calibration bit 6 mask. */ +#define DAC_CH1GAINCAL6_bp 6 /* Gain Calibration bit 6 position. */ + +/* DAC.CH1OFFSETCAL bit masks and bit positions */ +#define DAC_CH1OFFSETCAL_gm 0x7F /* Offset Calibration group mask. */ +#define DAC_CH1OFFSETCAL_gp 0 /* Offset Calibration group position. */ +#define DAC_CH1OFFSETCAL0_bm (1<<0) /* Offset Calibration bit 0 mask. */ +#define DAC_CH1OFFSETCAL0_bp 0 /* Offset Calibration bit 0 position. */ +#define DAC_CH1OFFSETCAL1_bm (1<<1) /* Offset Calibration bit 1 mask. */ +#define DAC_CH1OFFSETCAL1_bp 1 /* Offset Calibration bit 1 position. */ +#define DAC_CH1OFFSETCAL2_bm (1<<2) /* Offset Calibration bit 2 mask. */ +#define DAC_CH1OFFSETCAL2_bp 2 /* Offset Calibration bit 2 position. */ +#define DAC_CH1OFFSETCAL3_bm (1<<3) /* Offset Calibration bit 3 mask. */ +#define DAC_CH1OFFSETCAL3_bp 3 /* Offset Calibration bit 3 position. */ +#define DAC_CH1OFFSETCAL4_bm (1<<4) /* Offset Calibration bit 4 mask. */ +#define DAC_CH1OFFSETCAL4_bp 4 /* Offset Calibration bit 4 position. */ +#define DAC_CH1OFFSETCAL5_bm (1<<5) /* Offset Calibration bit 5 mask. */ +#define DAC_CH1OFFSETCAL5_bp 5 /* Offset Calibration bit 5 position. */ +#define DAC_CH1OFFSETCAL6_bm (1<<6) /* Offset Calibration bit 6 mask. */ +#define DAC_CH1OFFSETCAL6_bp 6 /* Offset Calibration bit 6 position. */ + +/* RTC - Real-Time Counter */ +/* RTC.CTRL bit masks and bit positions */ +#define RTC_PRESCALER_gm 0x07 /* Prescaling Factor group mask. */ +#define RTC_PRESCALER_gp 0 /* Prescaling Factor group position. */ +#define RTC_PRESCALER0_bm (1<<0) /* Prescaling Factor bit 0 mask. */ +#define RTC_PRESCALER0_bp 0 /* Prescaling Factor bit 0 position. */ +#define RTC_PRESCALER1_bm (1<<1) /* Prescaling Factor bit 1 mask. */ +#define RTC_PRESCALER1_bp 1 /* Prescaling Factor bit 1 position. */ +#define RTC_PRESCALER2_bm (1<<2) /* Prescaling Factor bit 2 mask. */ +#define RTC_PRESCALER2_bp 2 /* Prescaling Factor bit 2 position. */ + +/* RTC.STATUS bit masks and bit positions */ +#define RTC_SYNCBUSY_bm 0x01 /* Synchronization Busy Flag bit mask. */ +#define RTC_SYNCBUSY_bp 0 /* Synchronization Busy Flag bit position. */ + +/* RTC.INTCTRL bit masks and bit positions */ +#define RTC_COMPINTLVL_gm 0x0C /* Compare Match Interrupt Level group mask. */ +#define RTC_COMPINTLVL_gp 2 /* Compare Match Interrupt Level group position. */ +#define RTC_COMPINTLVL0_bm (1<<2) /* Compare Match Interrupt Level bit 0 mask. */ +#define RTC_COMPINTLVL0_bp 2 /* Compare Match Interrupt Level bit 0 position. */ +#define RTC_COMPINTLVL1_bm (1<<3) /* Compare Match Interrupt Level bit 1 mask. */ +#define RTC_COMPINTLVL1_bp 3 /* Compare Match Interrupt Level bit 1 position. */ + +#define RTC_OVFINTLVL_gm 0x03 /* Overflow Interrupt Level group mask. */ +#define RTC_OVFINTLVL_gp 0 /* Overflow Interrupt Level group position. */ +#define RTC_OVFINTLVL0_bm (1<<0) /* Overflow Interrupt Level bit 0 mask. */ +#define RTC_OVFINTLVL0_bp 0 /* Overflow Interrupt Level bit 0 position. */ +#define RTC_OVFINTLVL1_bm (1<<1) /* Overflow Interrupt Level bit 1 mask. */ +#define RTC_OVFINTLVL1_bp 1 /* Overflow Interrupt Level bit 1 position. */ + +/* RTC.INTFLAGS bit masks and bit positions */ +#define RTC_COMPIF_bm 0x02 /* Compare Match Interrupt Flag bit mask. */ +#define RTC_COMPIF_bp 1 /* Compare Match Interrupt Flag bit position. */ + +#define RTC_OVFIF_bm 0x01 /* Overflow Interrupt Flag bit mask. */ +#define RTC_OVFIF_bp 0 /* Overflow Interrupt Flag bit position. */ + +/* TWI - Two-Wire Interface */ +/* TWI_MASTER.CTRLA bit masks and bit positions */ +#define TWI_MASTER_INTLVL_gm 0xC0 /* Interrupt Level group mask. */ +#define TWI_MASTER_INTLVL_gp 6 /* Interrupt Level group position. */ +#define TWI_MASTER_INTLVL0_bm (1<<6) /* Interrupt Level bit 0 mask. */ +#define TWI_MASTER_INTLVL0_bp 6 /* Interrupt Level bit 0 position. */ +#define TWI_MASTER_INTLVL1_bm (1<<7) /* Interrupt Level bit 1 mask. */ +#define TWI_MASTER_INTLVL1_bp 7 /* Interrupt Level bit 1 position. */ + +#define TWI_MASTER_RIEN_bm 0x20 /* Read Interrupt Enable bit mask. */ +#define TWI_MASTER_RIEN_bp 5 /* Read Interrupt Enable bit position. */ + +#define TWI_MASTER_WIEN_bm 0x10 /* Write Interrupt Enable bit mask. */ +#define TWI_MASTER_WIEN_bp 4 /* Write Interrupt Enable bit position. */ + +#define TWI_MASTER_ENABLE_bm 0x08 /* Enable TWI Master bit mask. */ +#define TWI_MASTER_ENABLE_bp 3 /* Enable TWI Master bit position. */ + +/* TWI_MASTER.CTRLB bit masks and bit positions */ +#define TWI_MASTER_TIMEOUT_gm 0x0C /* Inactive Bus Timeout group mask. */ +#define TWI_MASTER_TIMEOUT_gp 2 /* Inactive Bus Timeout group position. */ +#define TWI_MASTER_TIMEOUT0_bm (1<<2) /* Inactive Bus Timeout bit 0 mask. */ +#define TWI_MASTER_TIMEOUT0_bp 2 /* Inactive Bus Timeout bit 0 position. */ +#define TWI_MASTER_TIMEOUT1_bm (1<<3) /* Inactive Bus Timeout bit 1 mask. */ +#define TWI_MASTER_TIMEOUT1_bp 3 /* Inactive Bus Timeout bit 1 position. */ + +#define TWI_MASTER_QCEN_bm 0x02 /* Quick Command Enable bit mask. */ +#define TWI_MASTER_QCEN_bp 1 /* Quick Command Enable bit position. */ + +#define TWI_MASTER_SMEN_bm 0x01 /* Smart Mode Enable bit mask. */ +#define TWI_MASTER_SMEN_bp 0 /* Smart Mode Enable bit position. */ + +/* TWI_MASTER.CTRLC bit masks and bit positions */ +#define TWI_MASTER_ACKACT_bm 0x04 /* Acknowledge Action bit mask. */ +#define TWI_MASTER_ACKACT_bp 2 /* Acknowledge Action bit position. */ + +#define TWI_MASTER_CMD_gm 0x03 /* Command group mask. */ +#define TWI_MASTER_CMD_gp 0 /* Command group position. */ +#define TWI_MASTER_CMD0_bm (1<<0) /* Command bit 0 mask. */ +#define TWI_MASTER_CMD0_bp 0 /* Command bit 0 position. */ +#define TWI_MASTER_CMD1_bm (1<<1) /* Command bit 1 mask. */ +#define TWI_MASTER_CMD1_bp 1 /* Command bit 1 position. */ + +/* TWI_MASTER.STATUS bit masks and bit positions */ +#define TWI_MASTER_RIF_bm 0x80 /* Read Interrupt Flag bit mask. */ +#define TWI_MASTER_RIF_bp 7 /* Read Interrupt Flag bit position. */ + +#define TWI_MASTER_WIF_bm 0x40 /* Write Interrupt Flag bit mask. */ +#define TWI_MASTER_WIF_bp 6 /* Write Interrupt Flag bit position. */ + +#define TWI_MASTER_CLKHOLD_bm 0x20 /* Clock Hold bit mask. */ +#define TWI_MASTER_CLKHOLD_bp 5 /* Clock Hold bit position. */ + +#define TWI_MASTER_RXACK_bm 0x10 /* Received Acknowledge bit mask. */ +#define TWI_MASTER_RXACK_bp 4 /* Received Acknowledge bit position. */ + +#define TWI_MASTER_ARBLOST_bm 0x08 /* Arbitration Lost bit mask. */ +#define TWI_MASTER_ARBLOST_bp 3 /* Arbitration Lost bit position. */ + +#define TWI_MASTER_BUSERR_bm 0x04 /* Bus Error bit mask. */ +#define TWI_MASTER_BUSERR_bp 2 /* Bus Error bit position. */ + +#define TWI_MASTER_BUSSTATE_gm 0x03 /* Bus State group mask. */ +#define TWI_MASTER_BUSSTATE_gp 0 /* Bus State group position. */ +#define TWI_MASTER_BUSSTATE0_bm (1<<0) /* Bus State bit 0 mask. */ +#define TWI_MASTER_BUSSTATE0_bp 0 /* Bus State bit 0 position. */ +#define TWI_MASTER_BUSSTATE1_bm (1<<1) /* Bus State bit 1 mask. */ +#define TWI_MASTER_BUSSTATE1_bp 1 /* Bus State bit 1 position. */ + +/* TWI_SLAVE.CTRLA bit masks and bit positions */ +#define TWI_SLAVE_INTLVL_gm 0xC0 /* Interrupt Level group mask. */ +#define TWI_SLAVE_INTLVL_gp 6 /* Interrupt Level group position. */ +#define TWI_SLAVE_INTLVL0_bm (1<<6) /* Interrupt Level bit 0 mask. */ +#define TWI_SLAVE_INTLVL0_bp 6 /* Interrupt Level bit 0 position. */ +#define TWI_SLAVE_INTLVL1_bm (1<<7) /* Interrupt Level bit 1 mask. */ +#define TWI_SLAVE_INTLVL1_bp 7 /* Interrupt Level bit 1 position. */ + +#define TWI_SLAVE_DIEN_bm 0x20 /* Data Interrupt Enable bit mask. */ +#define TWI_SLAVE_DIEN_bp 5 /* Data Interrupt Enable bit position. */ + +#define TWI_SLAVE_APIEN_bm 0x10 /* Address/Stop Interrupt Enable bit mask. */ +#define TWI_SLAVE_APIEN_bp 4 /* Address/Stop Interrupt Enable bit position. */ + +#define TWI_SLAVE_ENABLE_bm 0x08 /* Enable TWI Slave bit mask. */ +#define TWI_SLAVE_ENABLE_bp 3 /* Enable TWI Slave bit position. */ + +#define TWI_SLAVE_PIEN_bm 0x04 /* Stop Interrupt Enable bit mask. */ +#define TWI_SLAVE_PIEN_bp 2 /* Stop Interrupt Enable bit position. */ + +#define TWI_SLAVE_PMEN_bm 0x02 /* Promiscuous Mode Enable bit mask. */ +#define TWI_SLAVE_PMEN_bp 1 /* Promiscuous Mode Enable bit position. */ + +#define TWI_SLAVE_SMEN_bm 0x01 /* Smart Mode Enable bit mask. */ +#define TWI_SLAVE_SMEN_bp 0 /* Smart Mode Enable bit position. */ + +/* TWI_SLAVE.CTRLB bit masks and bit positions */ +#define TWI_SLAVE_ACKACT_bm 0x04 /* Acknowledge Action bit mask. */ +#define TWI_SLAVE_ACKACT_bp 2 /* Acknowledge Action bit position. */ + +#define TWI_SLAVE_CMD_gm 0x03 /* Command group mask. */ +#define TWI_SLAVE_CMD_gp 0 /* Command group position. */ +#define TWI_SLAVE_CMD0_bm (1<<0) /* Command bit 0 mask. */ +#define TWI_SLAVE_CMD0_bp 0 /* Command bit 0 position. */ +#define TWI_SLAVE_CMD1_bm (1<<1) /* Command bit 1 mask. */ +#define TWI_SLAVE_CMD1_bp 1 /* Command bit 1 position. */ + +/* TWI_SLAVE.STATUS bit masks and bit positions */ +#define TWI_SLAVE_DIF_bm 0x80 /* Data Interrupt Flag bit mask. */ +#define TWI_SLAVE_DIF_bp 7 /* Data Interrupt Flag bit position. */ + +#define TWI_SLAVE_APIF_bm 0x40 /* Address/Stop Interrupt Flag bit mask. */ +#define TWI_SLAVE_APIF_bp 6 /* Address/Stop Interrupt Flag bit position. */ + +#define TWI_SLAVE_CLKHOLD_bm 0x20 /* Clock Hold bit mask. */ +#define TWI_SLAVE_CLKHOLD_bp 5 /* Clock Hold bit position. */ + +#define TWI_SLAVE_RXACK_bm 0x10 /* Received Acknowledge bit mask. */ +#define TWI_SLAVE_RXACK_bp 4 /* Received Acknowledge bit position. */ + +#define TWI_SLAVE_COLL_bm 0x08 /* Collision bit mask. */ +#define TWI_SLAVE_COLL_bp 3 /* Collision bit position. */ + +#define TWI_SLAVE_BUSERR_bm 0x04 /* Bus Error bit mask. */ +#define TWI_SLAVE_BUSERR_bp 2 /* Bus Error bit position. */ + +#define TWI_SLAVE_DIR_bm 0x02 /* Read/Write Direction bit mask. */ +#define TWI_SLAVE_DIR_bp 1 /* Read/Write Direction bit position. */ + +#define TWI_SLAVE_AP_bm 0x01 /* Slave Address or Stop bit mask. */ +#define TWI_SLAVE_AP_bp 0 /* Slave Address or Stop bit position. */ + +/* TWI_SLAVE.ADDRMASK bit masks and bit positions */ +#define TWI_SLAVE_ADDRMASK_gm 0xFE /* Address Mask group mask. */ +#define TWI_SLAVE_ADDRMASK_gp 1 /* Address Mask group position. */ +#define TWI_SLAVE_ADDRMASK0_bm (1<<1) /* Address Mask bit 0 mask. */ +#define TWI_SLAVE_ADDRMASK0_bp 1 /* Address Mask bit 0 position. */ +#define TWI_SLAVE_ADDRMASK1_bm (1<<2) /* Address Mask bit 1 mask. */ +#define TWI_SLAVE_ADDRMASK1_bp 2 /* Address Mask bit 1 position. */ +#define TWI_SLAVE_ADDRMASK2_bm (1<<3) /* Address Mask bit 2 mask. */ +#define TWI_SLAVE_ADDRMASK2_bp 3 /* Address Mask bit 2 position. */ +#define TWI_SLAVE_ADDRMASK3_bm (1<<4) /* Address Mask bit 3 mask. */ +#define TWI_SLAVE_ADDRMASK3_bp 4 /* Address Mask bit 3 position. */ +#define TWI_SLAVE_ADDRMASK4_bm (1<<5) /* Address Mask bit 4 mask. */ +#define TWI_SLAVE_ADDRMASK4_bp 5 /* Address Mask bit 4 position. */ +#define TWI_SLAVE_ADDRMASK5_bm (1<<6) /* Address Mask bit 5 mask. */ +#define TWI_SLAVE_ADDRMASK5_bp 6 /* Address Mask bit 5 position. */ +#define TWI_SLAVE_ADDRMASK6_bm (1<<7) /* Address Mask bit 6 mask. */ +#define TWI_SLAVE_ADDRMASK6_bp 7 /* Address Mask bit 6 position. */ + +#define TWI_SLAVE_ADDREN_bm 0x01 /* Address Enable bit mask. */ +#define TWI_SLAVE_ADDREN_bp 0 /* Address Enable bit position. */ + +/* TWI.CTRL bit masks and bit positions */ +#define TWI_SDAHOLD_gm 0x06 /* SDA Hold Time Enable group mask. */ +#define TWI_SDAHOLD_gp 1 /* SDA Hold Time Enable group position. */ +#define TWI_SDAHOLD0_bm (1<<1) /* SDA Hold Time Enable bit 0 mask. */ +#define TWI_SDAHOLD0_bp 1 /* SDA Hold Time Enable bit 0 position. */ +#define TWI_SDAHOLD1_bm (1<<2) /* SDA Hold Time Enable bit 1 mask. */ +#define TWI_SDAHOLD1_bp 2 /* SDA Hold Time Enable bit 1 position. */ + +#define TWI_EDIEN_bm 0x01 /* External Driver Interface Enable bit mask. */ +#define TWI_EDIEN_bp 0 /* External Driver Interface Enable bit position. */ + +/* USB - USB */ +/* USB_EP.STATUS bit masks and bit positions */ +#define USB_EP_STALLF_bm 0x80 /* Endpoint Stall Flag bit mask. */ +#define USB_EP_STALLF_bp 7 /* Endpoint Stall Flag bit position. */ + +#define USB_EP_CRC_bm 0x80 /* CRC Error Flag bit mask. */ +#define USB_EP_CRC_bp 7 /* CRC Error Flag bit position. */ + +#define USB_EP_UNF_bm 0x40 /* Underflow Enpoint FLag bit mask. */ +#define USB_EP_UNF_bp 6 /* Underflow Enpoint FLag bit position. */ + +#define USB_EP_OVF_bm 0x40 /* Overflow Enpoint Flag for Output Endpoints bit mask. */ +#define USB_EP_OVF_bp 6 /* Overflow Enpoint Flag for Output Endpoints bit position. */ + +#define USB_EP_TRNCOMPL0_bm 0x20 /* Transaction Complete 0 Flag bit mask. */ +#define USB_EP_TRNCOMPL0_bp 5 /* Transaction Complete 0 Flag bit position. */ + +#define USB_EP_TRNCOMPL1_bm 0x10 /* Transaction Complete 1 Flag bit mask. */ +#define USB_EP_TRNCOMPL1_bp 4 /* Transaction Complete 1 Flag bit position. */ + +#define USB_EP_SETUP_bm 0x10 /* SETUP Transaction Complete Flag bit mask. */ +#define USB_EP_SETUP_bp 4 /* SETUP Transaction Complete Flag bit position. */ + +#define USB_EP_BANK_bm 0x08 /* Bank Select bit mask. */ +#define USB_EP_BANK_bp 3 /* Bank Select bit position. */ + +#define USB_EP_BUSNACK1_bm 0x04 /* Data Buffer 1 Not Acknowledge bit mask. */ +#define USB_EP_BUSNACK1_bp 2 /* Data Buffer 1 Not Acknowledge bit position. */ + +#define USB_EP_BUSNACK0_bm 0x02 /* Data Buffer 0 Not Acknowledge bit mask. */ +#define USB_EP_BUSNACK0_bp 1 /* Data Buffer 0 Not Acknowledge bit position. */ + +#define USB_EP_TOGGLE_bm 0x01 /* Data Toggle bit mask. */ +#define USB_EP_TOGGLE_bp 0 /* Data Toggle bit position. */ + +/* USB_EP.CTRL bit masks and bit positions */ +#define USB_EP_TYPE_gm 0xC0 /* Endpoint Type group mask. */ +#define USB_EP_TYPE_gp 6 /* Endpoint Type group position. */ +#define USB_EP_TYPE0_bm (1<<6) /* Endpoint Type bit 0 mask. */ +#define USB_EP_TYPE0_bp 6 /* Endpoint Type bit 0 position. */ +#define USB_EP_TYPE1_bm (1<<7) /* Endpoint Type bit 1 mask. */ +#define USB_EP_TYPE1_bp 7 /* Endpoint Type bit 1 position. */ + +#define USB_EP_MULTIPKT_bm 0x20 /* Multi Packet Transfer Enable bit mask. */ +#define USB_EP_MULTIPKT_bp 5 /* Multi Packet Transfer Enable bit position. */ + +#define USB_EP_PINGPONG_bm 0x10 /* Ping-Pong Enable bit mask. */ +#define USB_EP_PINGPONG_bp 4 /* Ping-Pong Enable bit position. */ + +#define USB_EP_INTDSBL_bm 0x08 /* Interrupt Disable bit mask. */ +#define USB_EP_INTDSBL_bp 3 /* Interrupt Disable bit position. */ + +#define USB_EP_STALL_bm 0x04 /* Data Stall bit mask. */ +#define USB_EP_STALL_bp 2 /* Data Stall bit position. */ + +#define USB_EP_BUFSIZE_gm 0x07 /* Data Buffer Size group mask. */ +#define USB_EP_BUFSIZE_gp 0 /* Data Buffer Size group position. */ +#define USB_EP_BUFSIZE0_bm (1<<0) /* Data Buffer Size bit 0 mask. */ +#define USB_EP_BUFSIZE0_bp 0 /* Data Buffer Size bit 0 position. */ +#define USB_EP_BUFSIZE1_bm (1<<1) /* Data Buffer Size bit 1 mask. */ +#define USB_EP_BUFSIZE1_bp 1 /* Data Buffer Size bit 1 position. */ +#define USB_EP_BUFSIZE2_bm (1<<2) /* Data Buffer Size bit 2 mask. */ +#define USB_EP_BUFSIZE2_bp 2 /* Data Buffer Size bit 2 position. */ + +/* USB_EP.CNT bit masks and bit positions */ +#define USB_EP_ZLP_bm 0x8000 /* Zero Length Packet bit mask. */ +#define USB_EP_ZLP_bp 15 /* Zero Length Packet bit position. */ + +/* USB.CTRLA bit masks and bit positions */ +#define USB_ENABLE_bm 0x80 /* USB Enable bit mask. */ +#define USB_ENABLE_bp 7 /* USB Enable bit position. */ + +#define USB_SPEED_bm 0x40 /* Speed Select bit mask. */ +#define USB_SPEED_bp 6 /* Speed Select bit position. */ + +#define USB_FIFOEN_bm 0x20 /* USB FIFO Enable bit mask. */ +#define USB_FIFOEN_bp 5 /* USB FIFO Enable bit position. */ + +#define USB_STFRNUM_bm 0x10 /* Store Frame Number Enable bit mask. */ +#define USB_STFRNUM_bp 4 /* Store Frame Number Enable bit position. */ + +#define USB_MAXEP_gm 0x0F /* Maximum Endpoint Addresses group mask. */ +#define USB_MAXEP_gp 0 /* Maximum Endpoint Addresses group position. */ +#define USB_MAXEP0_bm (1<<0) /* Maximum Endpoint Addresses bit 0 mask. */ +#define USB_MAXEP0_bp 0 /* Maximum Endpoint Addresses bit 0 position. */ +#define USB_MAXEP1_bm (1<<1) /* Maximum Endpoint Addresses bit 1 mask. */ +#define USB_MAXEP1_bp 1 /* Maximum Endpoint Addresses bit 1 position. */ +#define USB_MAXEP2_bm (1<<2) /* Maximum Endpoint Addresses bit 2 mask. */ +#define USB_MAXEP2_bp 2 /* Maximum Endpoint Addresses bit 2 position. */ +#define USB_MAXEP3_bm (1<<3) /* Maximum Endpoint Addresses bit 3 mask. */ +#define USB_MAXEP3_bp 3 /* Maximum Endpoint Addresses bit 3 position. */ + +/* USB.CTRLB bit masks and bit positions */ +#define USB_PULLRST_bm 0x10 /* Pull during Reset bit mask. */ +#define USB_PULLRST_bp 4 /* Pull during Reset bit position. */ + +#define USB_RWAKEUP_bm 0x04 /* Remote Wake-up bit mask. */ +#define USB_RWAKEUP_bp 2 /* Remote Wake-up bit position. */ + +#define USB_GNACK_bm 0x02 /* Global NACK bit mask. */ +#define USB_GNACK_bp 1 /* Global NACK bit position. */ + +#define USB_ATTACH_bm 0x01 /* Attach bit mask. */ +#define USB_ATTACH_bp 0 /* Attach bit position. */ + +/* USB.STATUS bit masks and bit positions */ +#define USB_URESUME_bm 0x08 /* Upstream Resume bit mask. */ +#define USB_URESUME_bp 3 /* Upstream Resume bit position. */ + +#define USB_RESUME_bm 0x04 /* Resume bit mask. */ +#define USB_RESUME_bp 2 /* Resume bit position. */ + +#define USB_SUSPEND_bm 0x02 /* Bus Suspended bit mask. */ +#define USB_SUSPEND_bp 1 /* Bus Suspended bit position. */ + +#define USB_BUSRST_bm 0x01 /* Bus Reset bit mask. */ +#define USB_BUSRST_bp 0 /* Bus Reset bit position. */ + +/* USB.ADDR bit masks and bit positions */ +#define USB_ADDR_gm 0x7F /* Device Address group mask. */ +#define USB_ADDR_gp 0 /* Device Address group position. */ +#define USB_ADDR0_bm (1<<0) /* Device Address bit 0 mask. */ +#define USB_ADDR0_bp 0 /* Device Address bit 0 position. */ +#define USB_ADDR1_bm (1<<1) /* Device Address bit 1 mask. */ +#define USB_ADDR1_bp 1 /* Device Address bit 1 position. */ +#define USB_ADDR2_bm (1<<2) /* Device Address bit 2 mask. */ +#define USB_ADDR2_bp 2 /* Device Address bit 2 position. */ +#define USB_ADDR3_bm (1<<3) /* Device Address bit 3 mask. */ +#define USB_ADDR3_bp 3 /* Device Address bit 3 position. */ +#define USB_ADDR4_bm (1<<4) /* Device Address bit 4 mask. */ +#define USB_ADDR4_bp 4 /* Device Address bit 4 position. */ +#define USB_ADDR5_bm (1<<5) /* Device Address bit 5 mask. */ +#define USB_ADDR5_bp 5 /* Device Address bit 5 position. */ +#define USB_ADDR6_bm (1<<6) /* Device Address bit 6 mask. */ +#define USB_ADDR6_bp 6 /* Device Address bit 6 position. */ + +/* USB.FIFOWP bit masks and bit positions */ +#define USB_FIFOWP_gm 0x1F /* FIFO Write Pointer group mask. */ +#define USB_FIFOWP_gp 0 /* FIFO Write Pointer group position. */ +#define USB_FIFOWP0_bm (1<<0) /* FIFO Write Pointer bit 0 mask. */ +#define USB_FIFOWP0_bp 0 /* FIFO Write Pointer bit 0 position. */ +#define USB_FIFOWP1_bm (1<<1) /* FIFO Write Pointer bit 1 mask. */ +#define USB_FIFOWP1_bp 1 /* FIFO Write Pointer bit 1 position. */ +#define USB_FIFOWP2_bm (1<<2) /* FIFO Write Pointer bit 2 mask. */ +#define USB_FIFOWP2_bp 2 /* FIFO Write Pointer bit 2 position. */ +#define USB_FIFOWP3_bm (1<<3) /* FIFO Write Pointer bit 3 mask. */ +#define USB_FIFOWP3_bp 3 /* FIFO Write Pointer bit 3 position. */ +#define USB_FIFOWP4_bm (1<<4) /* FIFO Write Pointer bit 4 mask. */ +#define USB_FIFOWP4_bp 4 /* FIFO Write Pointer bit 4 position. */ + +/* USB.FIFORP bit masks and bit positions */ +#define USB_FIFORP_gm 0x1F /* FIFO Read Pointer group mask. */ +#define USB_FIFORP_gp 0 /* FIFO Read Pointer group position. */ +#define USB_FIFORP0_bm (1<<0) /* FIFO Read Pointer bit 0 mask. */ +#define USB_FIFORP0_bp 0 /* FIFO Read Pointer bit 0 position. */ +#define USB_FIFORP1_bm (1<<1) /* FIFO Read Pointer bit 1 mask. */ +#define USB_FIFORP1_bp 1 /* FIFO Read Pointer bit 1 position. */ +#define USB_FIFORP2_bm (1<<2) /* FIFO Read Pointer bit 2 mask. */ +#define USB_FIFORP2_bp 2 /* FIFO Read Pointer bit 2 position. */ +#define USB_FIFORP3_bm (1<<3) /* FIFO Read Pointer bit 3 mask. */ +#define USB_FIFORP3_bp 3 /* FIFO Read Pointer bit 3 position. */ +#define USB_FIFORP4_bm (1<<4) /* FIFO Read Pointer bit 4 mask. */ +#define USB_FIFORP4_bp 4 /* FIFO Read Pointer bit 4 position. */ + +/* USB.INTCTRLA bit masks and bit positions */ +#define USB_SOFIE_bm 0x80 /* Start Of Frame Interrupt Enable bit mask. */ +#define USB_SOFIE_bp 7 /* Start Of Frame Interrupt Enable bit position. */ + +#define USB_BUSEVIE_bm 0x40 /* Bus Event Interrupt Enable bit mask. */ +#define USB_BUSEVIE_bp 6 /* Bus Event Interrupt Enable bit position. */ + +#define USB_BUSERRIE_bm 0x20 /* Bus Error Interrupt Enable bit mask. */ +#define USB_BUSERRIE_bp 5 /* Bus Error Interrupt Enable bit position. */ + +#define USB_STALLIE_bm 0x10 /* STALL Interrupt Enable bit mask. */ +#define USB_STALLIE_bp 4 /* STALL Interrupt Enable bit position. */ + +#define USB_INTLVL_gm 0x03 /* Interrupt Level group mask. */ +#define USB_INTLVL_gp 0 /* Interrupt Level group position. */ +#define USB_INTLVL0_bm (1<<0) /* Interrupt Level bit 0 mask. */ +#define USB_INTLVL0_bp 0 /* Interrupt Level bit 0 position. */ +#define USB_INTLVL1_bm (1<<1) /* Interrupt Level bit 1 mask. */ +#define USB_INTLVL1_bp 1 /* Interrupt Level bit 1 position. */ + +/* USB.INTCTRLB bit masks and bit positions */ +#define USB_TRNIE_bm 0x02 /* Transaction Complete Interrupt Enable bit mask. */ +#define USB_TRNIE_bp 1 /* Transaction Complete Interrupt Enable bit position. */ + +#define USB_SETUPIE_bm 0x01 /* SETUP Transaction Complete Interrupt Enable bit mask. */ +#define USB_SETUPIE_bp 0 /* SETUP Transaction Complete Interrupt Enable bit position. */ + +/* USB.INTFLAGSACLR bit masks and bit positions */ +#define USB_SOFIF_bm 0x80 /* Start Of Frame Interrupt Flag bit mask. */ +#define USB_SOFIF_bp 7 /* Start Of Frame Interrupt Flag bit position. */ + +#define USB_SUSPENDIF_bm 0x40 /* Suspend Interrupt Flag bit mask. */ +#define USB_SUSPENDIF_bp 6 /* Suspend Interrupt Flag bit position. */ + +#define USB_RESUMEIF_bm 0x20 /* Resume Interrupt Flag bit mask. */ +#define USB_RESUMEIF_bp 5 /* Resume Interrupt Flag bit position. */ + +#define USB_RSTIF_bm 0x10 /* Reset Interrupt Flag bit mask. */ +#define USB_RSTIF_bp 4 /* Reset Interrupt Flag bit position. */ + +#define USB_CRCIF_bm 0x08 /* Isochronous CRC Error Interrupt Flag bit mask. */ +#define USB_CRCIF_bp 3 /* Isochronous CRC Error Interrupt Flag bit position. */ + +#define USB_UNFIF_bm 0x04 /* Underflow Interrupt Flag bit mask. */ +#define USB_UNFIF_bp 2 /* Underflow Interrupt Flag bit position. */ + +#define USB_OVFIF_bm 0x02 /* Overflow Interrupt Flag bit mask. */ +#define USB_OVFIF_bp 1 /* Overflow Interrupt Flag bit position. */ + +#define USB_STALLIF_bm 0x01 /* STALL Interrupt Flag bit mask. */ +#define USB_STALLIF_bp 0 /* STALL Interrupt Flag bit position. */ + +/* USB.INTFLAGSASET bit masks and bit positions */ +/* USB_SOFIF Predefined. */ +/* USB_SOFIF Predefined. */ + +/* USB_SUSPENDIF Predefined. */ +/* USB_SUSPENDIF Predefined. */ + +/* USB_RESUMEIF Predefined. */ +/* USB_RESUMEIF Predefined. */ + +/* USB_RSTIF Predefined. */ +/* USB_RSTIF Predefined. */ + +/* USB_CRCIF Predefined. */ +/* USB_CRCIF Predefined. */ + +/* USB_UNFIF Predefined. */ +/* USB_UNFIF Predefined. */ + +/* USB_OVFIF Predefined. */ +/* USB_OVFIF Predefined. */ + +/* USB_STALLIF Predefined. */ +/* USB_STALLIF Predefined. */ + +/* USB.INTFLAGSBCLR bit masks and bit positions */ +#define USB_TRNIF_bm 0x02 /* Transaction Complete Interrupt Flag bit mask. */ +#define USB_TRNIF_bp 1 /* Transaction Complete Interrupt Flag bit position. */ + +#define USB_SETUPIF_bm 0x01 /* SETUP Transaction Complete Interrupt Flag bit mask. */ +#define USB_SETUPIF_bp 0 /* SETUP Transaction Complete Interrupt Flag bit position. */ + +/* USB.INTFLAGSBSET bit masks and bit positions */ +/* USB_TRNIF Predefined. */ +/* USB_TRNIF Predefined. */ + +/* USB_SETUPIF Predefined. */ +/* USB_SETUPIF Predefined. */ + +/* PORT - I/O Port Configuration */ +/* PORT.INTCTRL bit masks and bit positions */ +#define PORT_INT1LVL_gm 0x0C /* Port Interrupt 1 Level group mask. */ +#define PORT_INT1LVL_gp 2 /* Port Interrupt 1 Level group position. */ +#define PORT_INT1LVL0_bm (1<<2) /* Port Interrupt 1 Level bit 0 mask. */ +#define PORT_INT1LVL0_bp 2 /* Port Interrupt 1 Level bit 0 position. */ +#define PORT_INT1LVL1_bm (1<<3) /* Port Interrupt 1 Level bit 1 mask. */ +#define PORT_INT1LVL1_bp 3 /* Port Interrupt 1 Level bit 1 position. */ + +#define PORT_INT0LVL_gm 0x03 /* Port Interrupt 0 Level group mask. */ +#define PORT_INT0LVL_gp 0 /* Port Interrupt 0 Level group position. */ +#define PORT_INT0LVL0_bm (1<<0) /* Port Interrupt 0 Level bit 0 mask. */ +#define PORT_INT0LVL0_bp 0 /* Port Interrupt 0 Level bit 0 position. */ +#define PORT_INT0LVL1_bm (1<<1) /* Port Interrupt 0 Level bit 1 mask. */ +#define PORT_INT0LVL1_bp 1 /* Port Interrupt 0 Level bit 1 position. */ + +/* PORT.INTFLAGS bit masks and bit positions */ +#define PORT_INT1IF_bm 0x02 /* Port Interrupt 1 Flag bit mask. */ +#define PORT_INT1IF_bp 1 /* Port Interrupt 1 Flag bit position. */ + +#define PORT_INT0IF_bm 0x01 /* Port Interrupt 0 Flag bit mask. */ +#define PORT_INT0IF_bp 0 /* Port Interrupt 0 Flag bit position. */ + +/* PORT.REMAP bit masks and bit positions */ +#define PORT_SPI_bm 0x20 /* SPI bit mask. */ +#define PORT_SPI_bp 5 /* SPI bit position. */ + +#define PORT_USART0_bm 0x10 /* USART0 bit mask. */ +#define PORT_USART0_bp 4 /* USART0 bit position. */ + +#define PORT_TC0D_bm 0x08 /* Timer/Counter 0 Output Compare D bit mask. */ +#define PORT_TC0D_bp 3 /* Timer/Counter 0 Output Compare D bit position. */ + +#define PORT_TC0C_bm 0x04 /* Timer/Counter 0 Output Compare C bit mask. */ +#define PORT_TC0C_bp 2 /* Timer/Counter 0 Output Compare C bit position. */ + +#define PORT_TC0B_bm 0x02 /* Timer/Counter 0 Output Compare B bit mask. */ +#define PORT_TC0B_bp 1 /* Timer/Counter 0 Output Compare B bit position. */ + +#define PORT_TC0A_bm 0x01 /* Timer/Counter 0 Output Compare A bit mask. */ +#define PORT_TC0A_bp 0 /* Timer/Counter 0 Output Compare A bit position. */ + +/* PORT.PIN0CTRL bit masks and bit positions */ +#define PORT_SRLEN_bm 0x80 /* Slew Rate Enable bit mask. */ +#define PORT_SRLEN_bp 7 /* Slew Rate Enable bit position. */ + +#define PORT_INVEN_bm 0x40 /* Inverted I/O Enable bit mask. */ +#define PORT_INVEN_bp 6 /* Inverted I/O Enable bit position. */ + +#define PORT_OPC_gm 0x38 /* Output/Pull Configuration group mask. */ +#define PORT_OPC_gp 3 /* Output/Pull Configuration group position. */ +#define PORT_OPC0_bm (1<<3) /* Output/Pull Configuration bit 0 mask. */ +#define PORT_OPC0_bp 3 /* Output/Pull Configuration bit 0 position. */ +#define PORT_OPC1_bm (1<<4) /* Output/Pull Configuration bit 1 mask. */ +#define PORT_OPC1_bp 4 /* Output/Pull Configuration bit 1 position. */ +#define PORT_OPC2_bm (1<<5) /* Output/Pull Configuration bit 2 mask. */ +#define PORT_OPC2_bp 5 /* Output/Pull Configuration bit 2 position. */ + +#define PORT_ISC_gm 0x07 /* Input/Sense Configuration group mask. */ +#define PORT_ISC_gp 0 /* Input/Sense Configuration group position. */ +#define PORT_ISC0_bm (1<<0) /* Input/Sense Configuration bit 0 mask. */ +#define PORT_ISC0_bp 0 /* Input/Sense Configuration bit 0 position. */ +#define PORT_ISC1_bm (1<<1) /* Input/Sense Configuration bit 1 mask. */ +#define PORT_ISC1_bp 1 /* Input/Sense Configuration bit 1 position. */ +#define PORT_ISC2_bm (1<<2) /* Input/Sense Configuration bit 2 mask. */ +#define PORT_ISC2_bp 2 /* Input/Sense Configuration bit 2 position. */ + +/* PORT.PIN1CTRL bit masks and bit positions */ +/* PORT_SRLEN Predefined. */ +/* PORT_SRLEN Predefined. */ + +/* PORT_INVEN Predefined. */ +/* PORT_INVEN Predefined. */ + +/* PORT_OPC Predefined. */ +/* PORT_OPC Predefined. */ + +/* PORT_ISC Predefined. */ +/* PORT_ISC Predefined. */ + +/* PORT.PIN2CTRL bit masks and bit positions */ +/* PORT_SRLEN Predefined. */ +/* PORT_SRLEN Predefined. */ + +/* PORT_INVEN Predefined. */ +/* PORT_INVEN Predefined. */ + +/* PORT_OPC Predefined. */ +/* PORT_OPC Predefined. */ + +/* PORT_ISC Predefined. */ +/* PORT_ISC Predefined. */ + +/* PORT.PIN3CTRL bit masks and bit positions */ +/* PORT_SRLEN Predefined. */ +/* PORT_SRLEN Predefined. */ + +/* PORT_INVEN Predefined. */ +/* PORT_INVEN Predefined. */ + +/* PORT_OPC Predefined. */ +/* PORT_OPC Predefined. */ + +/* PORT_ISC Predefined. */ +/* PORT_ISC Predefined. */ + +/* PORT.PIN4CTRL bit masks and bit positions */ +/* PORT_SRLEN Predefined. */ +/* PORT_SRLEN Predefined. */ + +/* PORT_INVEN Predefined. */ +/* PORT_INVEN Predefined. */ + +/* PORT_OPC Predefined. */ +/* PORT_OPC Predefined. */ + +/* PORT_ISC Predefined. */ +/* PORT_ISC Predefined. */ + +/* PORT.PIN5CTRL bit masks and bit positions */ +/* PORT_SRLEN Predefined. */ +/* PORT_SRLEN Predefined. */ + +/* PORT_INVEN Predefined. */ +/* PORT_INVEN Predefined. */ + +/* PORT_OPC Predefined. */ +/* PORT_OPC Predefined. */ + +/* PORT_ISC Predefined. */ +/* PORT_ISC Predefined. */ + +/* PORT.PIN6CTRL bit masks and bit positions */ +/* PORT_SRLEN Predefined. */ +/* PORT_SRLEN Predefined. */ + +/* PORT_INVEN Predefined. */ +/* PORT_INVEN Predefined. */ + +/* PORT_OPC Predefined. */ +/* PORT_OPC Predefined. */ + +/* PORT_ISC Predefined. */ +/* PORT_ISC Predefined. */ + +/* PORT.PIN7CTRL bit masks and bit positions */ +/* PORT_SRLEN Predefined. */ +/* PORT_SRLEN Predefined. */ + +/* PORT_INVEN Predefined. */ +/* PORT_INVEN Predefined. */ + +/* PORT_OPC Predefined. */ +/* PORT_OPC Predefined. */ + +/* PORT_ISC Predefined. */ +/* PORT_ISC Predefined. */ + +/* TC - 16-bit Timer/Counter With PWM */ +/* TC0.CTRLA bit masks and bit positions */ +#define TC0_CLKSEL_gm 0x0F /* Clock Selection group mask. */ +#define TC0_CLKSEL_gp 0 /* Clock Selection group position. */ +#define TC0_CLKSEL0_bm (1<<0) /* Clock Selection bit 0 mask. */ +#define TC0_CLKSEL0_bp 0 /* Clock Selection bit 0 position. */ +#define TC0_CLKSEL1_bm (1<<1) /* Clock Selection bit 1 mask. */ +#define TC0_CLKSEL1_bp 1 /* Clock Selection bit 1 position. */ +#define TC0_CLKSEL2_bm (1<<2) /* Clock Selection bit 2 mask. */ +#define TC0_CLKSEL2_bp 2 /* Clock Selection bit 2 position. */ +#define TC0_CLKSEL3_bm (1<<3) /* Clock Selection bit 3 mask. */ +#define TC0_CLKSEL3_bp 3 /* Clock Selection bit 3 position. */ + +/* TC0.CTRLB bit masks and bit positions */ +#define TC0_CCDEN_bm 0x80 /* Compare or Capture D Enable bit mask. */ +#define TC0_CCDEN_bp 7 /* Compare or Capture D Enable bit position. */ + +#define TC0_CCCEN_bm 0x40 /* Compare or Capture C Enable bit mask. */ +#define TC0_CCCEN_bp 6 /* Compare or Capture C Enable bit position. */ + +#define TC0_CCBEN_bm 0x20 /* Compare or Capture B Enable bit mask. */ +#define TC0_CCBEN_bp 5 /* Compare or Capture B Enable bit position. */ + +#define TC0_CCAEN_bm 0x10 /* Compare or Capture A Enable bit mask. */ +#define TC0_CCAEN_bp 4 /* Compare or Capture A Enable bit position. */ + +#define TC0_WGMODE_gm 0x07 /* Waveform generation mode group mask. */ +#define TC0_WGMODE_gp 0 /* Waveform generation mode group position. */ +#define TC0_WGMODE0_bm (1<<0) /* Waveform generation mode bit 0 mask. */ +#define TC0_WGMODE0_bp 0 /* Waveform generation mode bit 0 position. */ +#define TC0_WGMODE1_bm (1<<1) /* Waveform generation mode bit 1 mask. */ +#define TC0_WGMODE1_bp 1 /* Waveform generation mode bit 1 position. */ +#define TC0_WGMODE2_bm (1<<2) /* Waveform generation mode bit 2 mask. */ +#define TC0_WGMODE2_bp 2 /* Waveform generation mode bit 2 position. */ + +/* TC0.CTRLC bit masks and bit positions */ +#define TC0_CMPD_bm 0x08 /* Compare D Output Value bit mask. */ +#define TC0_CMPD_bp 3 /* Compare D Output Value bit position. */ + +#define TC0_CMPC_bm 0x04 /* Compare C Output Value bit mask. */ +#define TC0_CMPC_bp 2 /* Compare C Output Value bit position. */ + +#define TC0_CMPB_bm 0x02 /* Compare B Output Value bit mask. */ +#define TC0_CMPB_bp 1 /* Compare B Output Value bit position. */ + +#define TC0_CMPA_bm 0x01 /* Compare A Output Value bit mask. */ +#define TC0_CMPA_bp 0 /* Compare A Output Value bit position. */ + +/* TC0.CTRLD bit masks and bit positions */ +#define TC0_EVACT_gm 0xE0 /* Event Action group mask. */ +#define TC0_EVACT_gp 5 /* Event Action group position. */ +#define TC0_EVACT0_bm (1<<5) /* Event Action bit 0 mask. */ +#define TC0_EVACT0_bp 5 /* Event Action bit 0 position. */ +#define TC0_EVACT1_bm (1<<6) /* Event Action bit 1 mask. */ +#define TC0_EVACT1_bp 6 /* Event Action bit 1 position. */ +#define TC0_EVACT2_bm (1<<7) /* Event Action bit 2 mask. */ +#define TC0_EVACT2_bp 7 /* Event Action bit 2 position. */ + +#define TC0_EVDLY_bm 0x10 /* Event Delay bit mask. */ +#define TC0_EVDLY_bp 4 /* Event Delay bit position. */ + +#define TC0_EVSEL_gm 0x0F /* Event Source Select group mask. */ +#define TC0_EVSEL_gp 0 /* Event Source Select group position. */ +#define TC0_EVSEL0_bm (1<<0) /* Event Source Select bit 0 mask. */ +#define TC0_EVSEL0_bp 0 /* Event Source Select bit 0 position. */ +#define TC0_EVSEL1_bm (1<<1) /* Event Source Select bit 1 mask. */ +#define TC0_EVSEL1_bp 1 /* Event Source Select bit 1 position. */ +#define TC0_EVSEL2_bm (1<<2) /* Event Source Select bit 2 mask. */ +#define TC0_EVSEL2_bp 2 /* Event Source Select bit 2 position. */ +#define TC0_EVSEL3_bm (1<<3) /* Event Source Select bit 3 mask. */ +#define TC0_EVSEL3_bp 3 /* Event Source Select bit 3 position. */ + +/* TC0.CTRLE bit masks and bit positions */ +#define TC0_BYTEM_gm 0x03 /* Byte Mode group mask. */ +#define TC0_BYTEM_gp 0 /* Byte Mode group position. */ +#define TC0_BYTEM0_bm (1<<0) /* Byte Mode bit 0 mask. */ +#define TC0_BYTEM0_bp 0 /* Byte Mode bit 0 position. */ +#define TC0_BYTEM1_bm (1<<1) /* Byte Mode bit 1 mask. */ +#define TC0_BYTEM1_bp 1 /* Byte Mode bit 1 position. */ + +/* TC0.INTCTRLA bit masks and bit positions */ +#define TC0_ERRINTLVL_gm 0x0C /* Error Interrupt Level group mask. */ +#define TC0_ERRINTLVL_gp 2 /* Error Interrupt Level group position. */ +#define TC0_ERRINTLVL0_bm (1<<2) /* Error Interrupt Level bit 0 mask. */ +#define TC0_ERRINTLVL0_bp 2 /* Error Interrupt Level bit 0 position. */ +#define TC0_ERRINTLVL1_bm (1<<3) /* Error Interrupt Level bit 1 mask. */ +#define TC0_ERRINTLVL1_bp 3 /* Error Interrupt Level bit 1 position. */ + +#define TC0_OVFINTLVL_gm 0x03 /* Overflow interrupt level group mask. */ +#define TC0_OVFINTLVL_gp 0 /* Overflow interrupt level group position. */ +#define TC0_OVFINTLVL0_bm (1<<0) /* Overflow interrupt level bit 0 mask. */ +#define TC0_OVFINTLVL0_bp 0 /* Overflow interrupt level bit 0 position. */ +#define TC0_OVFINTLVL1_bm (1<<1) /* Overflow interrupt level bit 1 mask. */ +#define TC0_OVFINTLVL1_bp 1 /* Overflow interrupt level bit 1 position. */ + +/* TC0.INTCTRLB bit masks and bit positions */ +#define TC0_CCDINTLVL_gm 0xC0 /* Compare or Capture D Interrupt Level group mask. */ +#define TC0_CCDINTLVL_gp 6 /* Compare or Capture D Interrupt Level group position. */ +#define TC0_CCDINTLVL0_bm (1<<6) /* Compare or Capture D Interrupt Level bit 0 mask. */ +#define TC0_CCDINTLVL0_bp 6 /* Compare or Capture D Interrupt Level bit 0 position. */ +#define TC0_CCDINTLVL1_bm (1<<7) /* Compare or Capture D Interrupt Level bit 1 mask. */ +#define TC0_CCDINTLVL1_bp 7 /* Compare or Capture D Interrupt Level bit 1 position. */ + +#define TC0_CCCINTLVL_gm 0x30 /* Compare or Capture C Interrupt Level group mask. */ +#define TC0_CCCINTLVL_gp 4 /* Compare or Capture C Interrupt Level group position. */ +#define TC0_CCCINTLVL0_bm (1<<4) /* Compare or Capture C Interrupt Level bit 0 mask. */ +#define TC0_CCCINTLVL0_bp 4 /* Compare or Capture C Interrupt Level bit 0 position. */ +#define TC0_CCCINTLVL1_bm (1<<5) /* Compare or Capture C Interrupt Level bit 1 mask. */ +#define TC0_CCCINTLVL1_bp 5 /* Compare or Capture C Interrupt Level bit 1 position. */ + +#define TC0_CCBINTLVL_gm 0x0C /* Compare or Capture B Interrupt Level group mask. */ +#define TC0_CCBINTLVL_gp 2 /* Compare or Capture B Interrupt Level group position. */ +#define TC0_CCBINTLVL0_bm (1<<2) /* Compare or Capture B Interrupt Level bit 0 mask. */ +#define TC0_CCBINTLVL0_bp 2 /* Compare or Capture B Interrupt Level bit 0 position. */ +#define TC0_CCBINTLVL1_bm (1<<3) /* Compare or Capture B Interrupt Level bit 1 mask. */ +#define TC0_CCBINTLVL1_bp 3 /* Compare or Capture B Interrupt Level bit 1 position. */ + +#define TC0_CCAINTLVL_gm 0x03 /* Compare or Capture A Interrupt Level group mask. */ +#define TC0_CCAINTLVL_gp 0 /* Compare or Capture A Interrupt Level group position. */ +#define TC0_CCAINTLVL0_bm (1<<0) /* Compare or Capture A Interrupt Level bit 0 mask. */ +#define TC0_CCAINTLVL0_bp 0 /* Compare or Capture A Interrupt Level bit 0 position. */ +#define TC0_CCAINTLVL1_bm (1<<1) /* Compare or Capture A Interrupt Level bit 1 mask. */ +#define TC0_CCAINTLVL1_bp 1 /* Compare or Capture A Interrupt Level bit 1 position. */ + +/* TC0.CTRLFCLR bit masks and bit positions */ +#define TC0_CMD_gm 0x0C /* Command group mask. */ +#define TC0_CMD_gp 2 /* Command group position. */ +#define TC0_CMD0_bm (1<<2) /* Command bit 0 mask. */ +#define TC0_CMD0_bp 2 /* Command bit 0 position. */ +#define TC0_CMD1_bm (1<<3) /* Command bit 1 mask. */ +#define TC0_CMD1_bp 3 /* Command bit 1 position. */ + +#define TC0_LUPD_bm 0x02 /* Lock Update bit mask. */ +#define TC0_LUPD_bp 1 /* Lock Update bit position. */ + +#define TC0_DIR_bm 0x01 /* Direction bit mask. */ +#define TC0_DIR_bp 0 /* Direction bit position. */ + +/* TC0.CTRLFSET bit masks and bit positions */ +/* TC0_CMD Predefined. */ +/* TC0_CMD Predefined. */ + +/* TC0_LUPD Predefined. */ +/* TC0_LUPD Predefined. */ + +/* TC0_DIR Predefined. */ +/* TC0_DIR Predefined. */ + +/* TC0.CTRLGCLR bit masks and bit positions */ +#define TC0_CCDBV_bm 0x10 /* Compare or Capture D Buffer Valid bit mask. */ +#define TC0_CCDBV_bp 4 /* Compare or Capture D Buffer Valid bit position. */ + +#define TC0_CCCBV_bm 0x08 /* Compare or Capture C Buffer Valid bit mask. */ +#define TC0_CCCBV_bp 3 /* Compare or Capture C Buffer Valid bit position. */ + +#define TC0_CCBBV_bm 0x04 /* Compare or Capture B Buffer Valid bit mask. */ +#define TC0_CCBBV_bp 2 /* Compare or Capture B Buffer Valid bit position. */ + +#define TC0_CCABV_bm 0x02 /* Compare or Capture A Buffer Valid bit mask. */ +#define TC0_CCABV_bp 1 /* Compare or Capture A Buffer Valid bit position. */ + +#define TC0_PERBV_bm 0x01 /* Period Buffer Valid bit mask. */ +#define TC0_PERBV_bp 0 /* Period Buffer Valid bit position. */ + +/* TC0.CTRLGSET bit masks and bit positions */ +/* TC0_CCDBV Predefined. */ +/* TC0_CCDBV Predefined. */ + +/* TC0_CCCBV Predefined. */ +/* TC0_CCCBV Predefined. */ + +/* TC0_CCBBV Predefined. */ +/* TC0_CCBBV Predefined. */ + +/* TC0_CCABV Predefined. */ +/* TC0_CCABV Predefined. */ + +/* TC0_PERBV Predefined. */ +/* TC0_PERBV Predefined. */ + +/* TC0.INTFLAGS bit masks and bit positions */ +#define TC0_CCDIF_bm 0x80 /* Compare or Capture D Interrupt Flag bit mask. */ +#define TC0_CCDIF_bp 7 /* Compare or Capture D Interrupt Flag bit position. */ + +#define TC0_CCCIF_bm 0x40 /* Compare or Capture C Interrupt Flag bit mask. */ +#define TC0_CCCIF_bp 6 /* Compare or Capture C Interrupt Flag bit position. */ + +#define TC0_CCBIF_bm 0x20 /* Compare or Capture B Interrupt Flag bit mask. */ +#define TC0_CCBIF_bp 5 /* Compare or Capture B Interrupt Flag bit position. */ + +#define TC0_CCAIF_bm 0x10 /* Compare or Capture A Interrupt Flag bit mask. */ +#define TC0_CCAIF_bp 4 /* Compare or Capture A Interrupt Flag bit position. */ + +#define TC0_ERRIF_bm 0x02 /* Error Interrupt Flag bit mask. */ +#define TC0_ERRIF_bp 1 /* Error Interrupt Flag bit position. */ + +#define TC0_OVFIF_bm 0x01 /* Overflow Interrupt Flag bit mask. */ +#define TC0_OVFIF_bp 0 /* Overflow Interrupt Flag bit position. */ + +/* TC1.CTRLA bit masks and bit positions */ +#define TC1_CLKSEL_gm 0x0F /* Clock Selection group mask. */ +#define TC1_CLKSEL_gp 0 /* Clock Selection group position. */ +#define TC1_CLKSEL0_bm (1<<0) /* Clock Selection bit 0 mask. */ +#define TC1_CLKSEL0_bp 0 /* Clock Selection bit 0 position. */ +#define TC1_CLKSEL1_bm (1<<1) /* Clock Selection bit 1 mask. */ +#define TC1_CLKSEL1_bp 1 /* Clock Selection bit 1 position. */ +#define TC1_CLKSEL2_bm (1<<2) /* Clock Selection bit 2 mask. */ +#define TC1_CLKSEL2_bp 2 /* Clock Selection bit 2 position. */ +#define TC1_CLKSEL3_bm (1<<3) /* Clock Selection bit 3 mask. */ +#define TC1_CLKSEL3_bp 3 /* Clock Selection bit 3 position. */ + +/* TC1.CTRLB bit masks and bit positions */ +#define TC1_CCBEN_bm 0x20 /* Compare or Capture B Enable bit mask. */ +#define TC1_CCBEN_bp 5 /* Compare or Capture B Enable bit position. */ + +#define TC1_CCAEN_bm 0x10 /* Compare or Capture A Enable bit mask. */ +#define TC1_CCAEN_bp 4 /* Compare or Capture A Enable bit position. */ + +#define TC1_WGMODE_gm 0x07 /* Waveform generation mode group mask. */ +#define TC1_WGMODE_gp 0 /* Waveform generation mode group position. */ +#define TC1_WGMODE0_bm (1<<0) /* Waveform generation mode bit 0 mask. */ +#define TC1_WGMODE0_bp 0 /* Waveform generation mode bit 0 position. */ +#define TC1_WGMODE1_bm (1<<1) /* Waveform generation mode bit 1 mask. */ +#define TC1_WGMODE1_bp 1 /* Waveform generation mode bit 1 position. */ +#define TC1_WGMODE2_bm (1<<2) /* Waveform generation mode bit 2 mask. */ +#define TC1_WGMODE2_bp 2 /* Waveform generation mode bit 2 position. */ + +/* TC1.CTRLC bit masks and bit positions */ +#define TC1_CMPB_bm 0x02 /* Compare B Output Value bit mask. */ +#define TC1_CMPB_bp 1 /* Compare B Output Value bit position. */ + +#define TC1_CMPA_bm 0x01 /* Compare A Output Value bit mask. */ +#define TC1_CMPA_bp 0 /* Compare A Output Value bit position. */ + +/* TC1.CTRLD bit masks and bit positions */ +#define TC1_EVACT_gm 0xE0 /* Event Action group mask. */ +#define TC1_EVACT_gp 5 /* Event Action group position. */ +#define TC1_EVACT0_bm (1<<5) /* Event Action bit 0 mask. */ +#define TC1_EVACT0_bp 5 /* Event Action bit 0 position. */ +#define TC1_EVACT1_bm (1<<6) /* Event Action bit 1 mask. */ +#define TC1_EVACT1_bp 6 /* Event Action bit 1 position. */ +#define TC1_EVACT2_bm (1<<7) /* Event Action bit 2 mask. */ +#define TC1_EVACT2_bp 7 /* Event Action bit 2 position. */ + +#define TC1_EVDLY_bm 0x10 /* Event Delay bit mask. */ +#define TC1_EVDLY_bp 4 /* Event Delay bit position. */ + +#define TC1_EVSEL_gm 0x0F /* Event Source Select group mask. */ +#define TC1_EVSEL_gp 0 /* Event Source Select group position. */ +#define TC1_EVSEL0_bm (1<<0) /* Event Source Select bit 0 mask. */ +#define TC1_EVSEL0_bp 0 /* Event Source Select bit 0 position. */ +#define TC1_EVSEL1_bm (1<<1) /* Event Source Select bit 1 mask. */ +#define TC1_EVSEL1_bp 1 /* Event Source Select bit 1 position. */ +#define TC1_EVSEL2_bm (1<<2) /* Event Source Select bit 2 mask. */ +#define TC1_EVSEL2_bp 2 /* Event Source Select bit 2 position. */ +#define TC1_EVSEL3_bm (1<<3) /* Event Source Select bit 3 mask. */ +#define TC1_EVSEL3_bp 3 /* Event Source Select bit 3 position. */ + +/* TC1.CTRLE bit masks and bit positions */ +#define TC1_BYTEM_bm 0x01 /* Byte Mode bit mask. */ +#define TC1_BYTEM_bp 0 /* Byte Mode bit position. */ + +/* TC1.INTCTRLA bit masks and bit positions */ +#define TC1_ERRINTLVL_gm 0x0C /* Error Interrupt Level group mask. */ +#define TC1_ERRINTLVL_gp 2 /* Error Interrupt Level group position. */ +#define TC1_ERRINTLVL0_bm (1<<2) /* Error Interrupt Level bit 0 mask. */ +#define TC1_ERRINTLVL0_bp 2 /* Error Interrupt Level bit 0 position. */ +#define TC1_ERRINTLVL1_bm (1<<3) /* Error Interrupt Level bit 1 mask. */ +#define TC1_ERRINTLVL1_bp 3 /* Error Interrupt Level bit 1 position. */ + +#define TC1_OVFINTLVL_gm 0x03 /* Overflow interrupt level group mask. */ +#define TC1_OVFINTLVL_gp 0 /* Overflow interrupt level group position. */ +#define TC1_OVFINTLVL0_bm (1<<0) /* Overflow interrupt level bit 0 mask. */ +#define TC1_OVFINTLVL0_bp 0 /* Overflow interrupt level bit 0 position. */ +#define TC1_OVFINTLVL1_bm (1<<1) /* Overflow interrupt level bit 1 mask. */ +#define TC1_OVFINTLVL1_bp 1 /* Overflow interrupt level bit 1 position. */ + +/* TC1.INTCTRLB bit masks and bit positions */ +#define TC1_CCBINTLVL_gm 0x0C /* Compare or Capture B Interrupt Level group mask. */ +#define TC1_CCBINTLVL_gp 2 /* Compare or Capture B Interrupt Level group position. */ +#define TC1_CCBINTLVL0_bm (1<<2) /* Compare or Capture B Interrupt Level bit 0 mask. */ +#define TC1_CCBINTLVL0_bp 2 /* Compare or Capture B Interrupt Level bit 0 position. */ +#define TC1_CCBINTLVL1_bm (1<<3) /* Compare or Capture B Interrupt Level bit 1 mask. */ +#define TC1_CCBINTLVL1_bp 3 /* Compare or Capture B Interrupt Level bit 1 position. */ + +#define TC1_CCAINTLVL_gm 0x03 /* Compare or Capture A Interrupt Level group mask. */ +#define TC1_CCAINTLVL_gp 0 /* Compare or Capture A Interrupt Level group position. */ +#define TC1_CCAINTLVL0_bm (1<<0) /* Compare or Capture A Interrupt Level bit 0 mask. */ +#define TC1_CCAINTLVL0_bp 0 /* Compare or Capture A Interrupt Level bit 0 position. */ +#define TC1_CCAINTLVL1_bm (1<<1) /* Compare or Capture A Interrupt Level bit 1 mask. */ +#define TC1_CCAINTLVL1_bp 1 /* Compare or Capture A Interrupt Level bit 1 position. */ + +/* TC1.CTRLFCLR bit masks and bit positions */ +#define TC1_CMD_gm 0x0C /* Command group mask. */ +#define TC1_CMD_gp 2 /* Command group position. */ +#define TC1_CMD0_bm (1<<2) /* Command bit 0 mask. */ +#define TC1_CMD0_bp 2 /* Command bit 0 position. */ +#define TC1_CMD1_bm (1<<3) /* Command bit 1 mask. */ +#define TC1_CMD1_bp 3 /* Command bit 1 position. */ + +#define TC1_LUPD_bm 0x02 /* Lock Update bit mask. */ +#define TC1_LUPD_bp 1 /* Lock Update bit position. */ + +#define TC1_DIR_bm 0x01 /* Direction bit mask. */ +#define TC1_DIR_bp 0 /* Direction bit position. */ + +/* TC1.CTRLFSET bit masks and bit positions */ +/* TC1_CMD Predefined. */ +/* TC1_CMD Predefined. */ + +/* TC1_LUPD Predefined. */ +/* TC1_LUPD Predefined. */ + +/* TC1_DIR Predefined. */ +/* TC1_DIR Predefined. */ + +/* TC1.CTRLGCLR bit masks and bit positions */ +#define TC1_CCBBV_bm 0x04 /* Compare or Capture B Buffer Valid bit mask. */ +#define TC1_CCBBV_bp 2 /* Compare or Capture B Buffer Valid bit position. */ + +#define TC1_CCABV_bm 0x02 /* Compare or Capture A Buffer Valid bit mask. */ +#define TC1_CCABV_bp 1 /* Compare or Capture A Buffer Valid bit position. */ + +#define TC1_PERBV_bm 0x01 /* Period Buffer Valid bit mask. */ +#define TC1_PERBV_bp 0 /* Period Buffer Valid bit position. */ + +/* TC1.CTRLGSET bit masks and bit positions */ +/* TC1_CCBBV Predefined. */ +/* TC1_CCBBV Predefined. */ + +/* TC1_CCABV Predefined. */ +/* TC1_CCABV Predefined. */ + +/* TC1_PERBV Predefined. */ +/* TC1_PERBV Predefined. */ + +/* TC1.INTFLAGS bit masks and bit positions */ +#define TC1_CCBIF_bm 0x20 /* Compare or Capture B Interrupt Flag bit mask. */ +#define TC1_CCBIF_bp 5 /* Compare or Capture B Interrupt Flag bit position. */ + +#define TC1_CCAIF_bm 0x10 /* Compare or Capture A Interrupt Flag bit mask. */ +#define TC1_CCAIF_bp 4 /* Compare or Capture A Interrupt Flag bit position. */ + +#define TC1_ERRIF_bm 0x02 /* Error Interrupt Flag bit mask. */ +#define TC1_ERRIF_bp 1 /* Error Interrupt Flag bit position. */ + +#define TC1_OVFIF_bm 0x01 /* Overflow Interrupt Flag bit mask. */ +#define TC1_OVFIF_bp 0 /* Overflow Interrupt Flag bit position. */ + +/* TC2 - 16-bit Timer/Counter type 2 */ +/* TC2.CTRLA bit masks and bit positions */ +#define TC2_CLKSEL_gm 0x0F /* Clock Selection group mask. */ +#define TC2_CLKSEL_gp 0 /* Clock Selection group position. */ +#define TC2_CLKSEL0_bm (1<<0) /* Clock Selection bit 0 mask. */ +#define TC2_CLKSEL0_bp 0 /* Clock Selection bit 0 position. */ +#define TC2_CLKSEL1_bm (1<<1) /* Clock Selection bit 1 mask. */ +#define TC2_CLKSEL1_bp 1 /* Clock Selection bit 1 position. */ +#define TC2_CLKSEL2_bm (1<<2) /* Clock Selection bit 2 mask. */ +#define TC2_CLKSEL2_bp 2 /* Clock Selection bit 2 position. */ +#define TC2_CLKSEL3_bm (1<<3) /* Clock Selection bit 3 mask. */ +#define TC2_CLKSEL3_bp 3 /* Clock Selection bit 3 position. */ + +/* TC2.CTRLB bit masks and bit positions */ +#define TC2_HCMPDEN_bm 0x80 /* High Byte Compare D Enable bit mask. */ +#define TC2_HCMPDEN_bp 7 /* High Byte Compare D Enable bit position. */ + +#define TC2_HCMPCEN_bm 0x40 /* High Byte Compare C Enable bit mask. */ +#define TC2_HCMPCEN_bp 6 /* High Byte Compare C Enable bit position. */ + +#define TC2_HCMPBEN_bm 0x20 /* High Byte Compare B Enable bit mask. */ +#define TC2_HCMPBEN_bp 5 /* High Byte Compare B Enable bit position. */ + +#define TC2_HCMPAEN_bm 0x10 /* High Byte Compare A Enable bit mask. */ +#define TC2_HCMPAEN_bp 4 /* High Byte Compare A Enable bit position. */ + +#define TC2_LCMPDEN_bm 0x08 /* Low Byte Compare D Enable bit mask. */ +#define TC2_LCMPDEN_bp 3 /* Low Byte Compare D Enable bit position. */ + +#define TC2_LCMPCEN_bm 0x04 /* Low Byte Compare C Enable bit mask. */ +#define TC2_LCMPCEN_bp 2 /* Low Byte Compare C Enable bit position. */ + +#define TC2_LCMPBEN_bm 0x02 /* Low Byte Compare B Enable bit mask. */ +#define TC2_LCMPBEN_bp 1 /* Low Byte Compare B Enable bit position. */ + +#define TC2_LCMPAEN_bm 0x01 /* Low Byte Compare A Enable bit mask. */ +#define TC2_LCMPAEN_bp 0 /* Low Byte Compare A Enable bit position. */ + +/* TC2.CTRLC bit masks and bit positions */ +#define TC2_HCMPD_bm 0x80 /* High Byte Compare D Output Value bit mask. */ +#define TC2_HCMPD_bp 7 /* High Byte Compare D Output Value bit position. */ + +#define TC2_HCMPC_bm 0x40 /* High Byte Compare C Output Value bit mask. */ +#define TC2_HCMPC_bp 6 /* High Byte Compare C Output Value bit position. */ + +#define TC2_HCMPB_bm 0x20 /* High Byte Compare B Output Value bit mask. */ +#define TC2_HCMPB_bp 5 /* High Byte Compare B Output Value bit position. */ + +#define TC2_HCMPA_bm 0x10 /* High Byte Compare A Output Value bit mask. */ +#define TC2_HCMPA_bp 4 /* High Byte Compare A Output Value bit position. */ + +#define TC2_LCMPD_bm 0x08 /* Low Byte Compare D Output Value bit mask. */ +#define TC2_LCMPD_bp 3 /* Low Byte Compare D Output Value bit position. */ + +#define TC2_LCMPC_bm 0x04 /* Low Byte Compare C Output Value bit mask. */ +#define TC2_LCMPC_bp 2 /* Low Byte Compare C Output Value bit position. */ + +#define TC2_LCMPB_bm 0x02 /* Low Byte Compare B Output Value bit mask. */ +#define TC2_LCMPB_bp 1 /* Low Byte Compare B Output Value bit position. */ + +#define TC2_LCMPA_bm 0x01 /* Low Byte Compare A Output Value bit mask. */ +#define TC2_LCMPA_bp 0 /* Low Byte Compare A Output Value bit position. */ + +/* TC2.CTRLE bit masks and bit positions */ +#define TC2_BYTEM_gm 0x03 /* Byte Mode group mask. */ +#define TC2_BYTEM_gp 0 /* Byte Mode group position. */ +#define TC2_BYTEM0_bm (1<<0) /* Byte Mode bit 0 mask. */ +#define TC2_BYTEM0_bp 0 /* Byte Mode bit 0 position. */ +#define TC2_BYTEM1_bm (1<<1) /* Byte Mode bit 1 mask. */ +#define TC2_BYTEM1_bp 1 /* Byte Mode bit 1 position. */ + +/* TC2.INTCTRLA bit masks and bit positions */ +#define TC2_HUNFINTLVL_gm 0x0C /* High Byte Underflow Interrupt Level group mask. */ +#define TC2_HUNFINTLVL_gp 2 /* High Byte Underflow Interrupt Level group position. */ +#define TC2_HUNFINTLVL0_bm (1<<2) /* High Byte Underflow Interrupt Level bit 0 mask. */ +#define TC2_HUNFINTLVL0_bp 2 /* High Byte Underflow Interrupt Level bit 0 position. */ +#define TC2_HUNFINTLVL1_bm (1<<3) /* High Byte Underflow Interrupt Level bit 1 mask. */ +#define TC2_HUNFINTLVL1_bp 3 /* High Byte Underflow Interrupt Level bit 1 position. */ + +#define TC2_LUNFINTLVL_gm 0x03 /* Low Byte Underflow interrupt level group mask. */ +#define TC2_LUNFINTLVL_gp 0 /* Low Byte Underflow interrupt level group position. */ +#define TC2_LUNFINTLVL0_bm (1<<0) /* Low Byte Underflow interrupt level bit 0 mask. */ +#define TC2_LUNFINTLVL0_bp 0 /* Low Byte Underflow interrupt level bit 0 position. */ +#define TC2_LUNFINTLVL1_bm (1<<1) /* Low Byte Underflow interrupt level bit 1 mask. */ +#define TC2_LUNFINTLVL1_bp 1 /* Low Byte Underflow interrupt level bit 1 position. */ + +/* TC2.INTCTRLB bit masks and bit positions */ +#define TC2_LCMPDINTLVL_gm 0xC0 /* Low Byte Compare D Interrupt Level group mask. */ +#define TC2_LCMPDINTLVL_gp 6 /* Low Byte Compare D Interrupt Level group position. */ +#define TC2_LCMPDINTLVL0_bm (1<<6) /* Low Byte Compare D Interrupt Level bit 0 mask. */ +#define TC2_LCMPDINTLVL0_bp 6 /* Low Byte Compare D Interrupt Level bit 0 position. */ +#define TC2_LCMPDINTLVL1_bm (1<<7) /* Low Byte Compare D Interrupt Level bit 1 mask. */ +#define TC2_LCMPDINTLVL1_bp 7 /* Low Byte Compare D Interrupt Level bit 1 position. */ + +#define TC2_LCMPCINTLVL_gm 0x30 /* Low Byte Compare C Interrupt Level group mask. */ +#define TC2_LCMPCINTLVL_gp 4 /* Low Byte Compare C Interrupt Level group position. */ +#define TC2_LCMPCINTLVL0_bm (1<<4) /* Low Byte Compare C Interrupt Level bit 0 mask. */ +#define TC2_LCMPCINTLVL0_bp 4 /* Low Byte Compare C Interrupt Level bit 0 position. */ +#define TC2_LCMPCINTLVL1_bm (1<<5) /* Low Byte Compare C Interrupt Level bit 1 mask. */ +#define TC2_LCMPCINTLVL1_bp 5 /* Low Byte Compare C Interrupt Level bit 1 position. */ + +#define TC2_LCMPBINTLVL_gm 0x0C /* Low Byte Compare B Interrupt Level group mask. */ +#define TC2_LCMPBINTLVL_gp 2 /* Low Byte Compare B Interrupt Level group position. */ +#define TC2_LCMPBINTLVL0_bm (1<<2) /* Low Byte Compare B Interrupt Level bit 0 mask. */ +#define TC2_LCMPBINTLVL0_bp 2 /* Low Byte Compare B Interrupt Level bit 0 position. */ +#define TC2_LCMPBINTLVL1_bm (1<<3) /* Low Byte Compare B Interrupt Level bit 1 mask. */ +#define TC2_LCMPBINTLVL1_bp 3 /* Low Byte Compare B Interrupt Level bit 1 position. */ + +#define TC2_LCMPAINTLVL_gm 0x03 /* Low Byte Compare A Interrupt Level group mask. */ +#define TC2_LCMPAINTLVL_gp 0 /* Low Byte Compare A Interrupt Level group position. */ +#define TC2_LCMPAINTLVL0_bm (1<<0) /* Low Byte Compare A Interrupt Level bit 0 mask. */ +#define TC2_LCMPAINTLVL0_bp 0 /* Low Byte Compare A Interrupt Level bit 0 position. */ +#define TC2_LCMPAINTLVL1_bm (1<<1) /* Low Byte Compare A Interrupt Level bit 1 mask. */ +#define TC2_LCMPAINTLVL1_bp 1 /* Low Byte Compare A Interrupt Level bit 1 position. */ + +/* TC2.CTRLF bit masks and bit positions */ +#define TC2_CMD_gm 0x0C /* Command group mask. */ +#define TC2_CMD_gp 2 /* Command group position. */ +#define TC2_CMD0_bm (1<<2) /* Command bit 0 mask. */ +#define TC2_CMD0_bp 2 /* Command bit 0 position. */ +#define TC2_CMD1_bm (1<<3) /* Command bit 1 mask. */ +#define TC2_CMD1_bp 3 /* Command bit 1 position. */ + +#define TC2_CMDEN_gm 0x03 /* Command Enable group mask. */ +#define TC2_CMDEN_gp 0 /* Command Enable group position. */ +#define TC2_CMDEN0_bm (1<<0) /* Command Enable bit 0 mask. */ +#define TC2_CMDEN0_bp 0 /* Command Enable bit 0 position. */ +#define TC2_CMDEN1_bm (1<<1) /* Command Enable bit 1 mask. */ +#define TC2_CMDEN1_bp 1 /* Command Enable bit 1 position. */ + +/* TC2.INTFLAGS bit masks and bit positions */ +#define TC2_LCMPDIF_bm 0x80 /* Low Byte Compare D Interrupt Flag bit mask. */ +#define TC2_LCMPDIF_bp 7 /* Low Byte Compare D Interrupt Flag bit position. */ + +#define TC2_LCMPCIF_bm 0x40 /* Low Byte Compare C Interrupt Flag bit mask. */ +#define TC2_LCMPCIF_bp 6 /* Low Byte Compare C Interrupt Flag bit position. */ + +#define TC2_LCMPBIF_bm 0x20 /* Low Byte Compare B Interrupt Flag bit mask. */ +#define TC2_LCMPBIF_bp 5 /* Low Byte Compare B Interrupt Flag bit position. */ + +#define TC2_LCMPAIF_bm 0x10 /* Low Byte Compare A Interrupt Flag bit mask. */ +#define TC2_LCMPAIF_bp 4 /* Low Byte Compare A Interrupt Flag bit position. */ + +#define TC2_HUNFIF_bm 0x02 /* High Byte Underflow Interrupt Flag bit mask. */ +#define TC2_HUNFIF_bp 1 /* High Byte Underflow Interrupt Flag bit position. */ + +#define TC2_LUNFIF_bm 0x01 /* Low Byte Underflow Interrupt Flag bit mask. */ +#define TC2_LUNFIF_bp 0 /* Low Byte Underflow Interrupt Flag bit position. */ + +/* AWEX - Timer/Counter Advanced Waveform Extension */ +/* AWEX.CTRL bit masks and bit positions */ +#define AWEX_PGM_bm 0x20 /* Pattern Generation Mode bit mask. */ +#define AWEX_PGM_bp 5 /* Pattern Generation Mode bit position. */ + +#define AWEX_CWCM_bm 0x10 /* Common Waveform Channel Mode bit mask. */ +#define AWEX_CWCM_bp 4 /* Common Waveform Channel Mode bit position. */ + +#define AWEX_DTICCDEN_bm 0x08 /* Dead Time Insertion Compare Channel D Enable bit mask. */ +#define AWEX_DTICCDEN_bp 3 /* Dead Time Insertion Compare Channel D Enable bit position. */ + +#define AWEX_DTICCCEN_bm 0x04 /* Dead Time Insertion Compare Channel C Enable bit mask. */ +#define AWEX_DTICCCEN_bp 2 /* Dead Time Insertion Compare Channel C Enable bit position. */ + +#define AWEX_DTICCBEN_bm 0x02 /* Dead Time Insertion Compare Channel B Enable bit mask. */ +#define AWEX_DTICCBEN_bp 1 /* Dead Time Insertion Compare Channel B Enable bit position. */ + +#define AWEX_DTICCAEN_bm 0x01 /* Dead Time Insertion Compare Channel A Enable bit mask. */ +#define AWEX_DTICCAEN_bp 0 /* Dead Time Insertion Compare Channel A Enable bit position. */ + +/* AWEX.FDCTRL bit masks and bit positions */ +#define AWEX_FDDBD_bm 0x10 /* Fault Detect on Disable Break Disable bit mask. */ +#define AWEX_FDDBD_bp 4 /* Fault Detect on Disable Break Disable bit position. */ + +#define AWEX_FDMODE_bm 0x04 /* Fault Detect Mode bit mask. */ +#define AWEX_FDMODE_bp 2 /* Fault Detect Mode bit position. */ + +#define AWEX_FDACT_gm 0x03 /* Fault Detect Action group mask. */ +#define AWEX_FDACT_gp 0 /* Fault Detect Action group position. */ +#define AWEX_FDACT0_bm (1<<0) /* Fault Detect Action bit 0 mask. */ +#define AWEX_FDACT0_bp 0 /* Fault Detect Action bit 0 position. */ +#define AWEX_FDACT1_bm (1<<1) /* Fault Detect Action bit 1 mask. */ +#define AWEX_FDACT1_bp 1 /* Fault Detect Action bit 1 position. */ + +/* AWEX.STATUS bit masks and bit positions */ +#define AWEX_FDF_bm 0x04 /* Fault Detect Flag bit mask. */ +#define AWEX_FDF_bp 2 /* Fault Detect Flag bit position. */ + +#define AWEX_DTHSBUFV_bm 0x02 /* Dead Time High Side Buffer Valid bit mask. */ +#define AWEX_DTHSBUFV_bp 1 /* Dead Time High Side Buffer Valid bit position. */ + +#define AWEX_DTLSBUFV_bm 0x01 /* Dead Time Low Side Buffer Valid bit mask. */ +#define AWEX_DTLSBUFV_bp 0 /* Dead Time Low Side Buffer Valid bit position. */ + +/* AWEX.STATUSSET bit masks and bit positions */ +/* AWEX_FDF Predefined. */ +/* AWEX_FDF Predefined. */ + +/* AWEX_DTHSBUFV Predefined. */ +/* AWEX_DTHSBUFV Predefined. */ + +/* AWEX_DTLSBUFV Predefined. */ +/* AWEX_DTLSBUFV Predefined. */ + +/* HIRES - Timer/Counter High-Resolution Extension */ +/* HIRES.CTRLA bit masks and bit positions */ +#define HIRES_HREN_gm 0x03 /* High Resolution Enable group mask. */ +#define HIRES_HREN_gp 0 /* High Resolution Enable group position. */ +#define HIRES_HREN0_bm (1<<0) /* High Resolution Enable bit 0 mask. */ +#define HIRES_HREN0_bp 0 /* High Resolution Enable bit 0 position. */ +#define HIRES_HREN1_bm (1<<1) /* High Resolution Enable bit 1 mask. */ +#define HIRES_HREN1_bp 1 /* High Resolution Enable bit 1 position. */ + +/* USART - Universal Asynchronous Receiver-Transmitter */ +/* USART.STATUS bit masks and bit positions */ +#define USART_RXCIF_bm 0x80 /* Receive Interrupt Flag bit mask. */ +#define USART_RXCIF_bp 7 /* Receive Interrupt Flag bit position. */ + +#define USART_TXCIF_bm 0x40 /* Transmit Interrupt Flag bit mask. */ +#define USART_TXCIF_bp 6 /* Transmit Interrupt Flag bit position. */ + +#define USART_DREIF_bm 0x20 /* Data Register Empty Flag bit mask. */ +#define USART_DREIF_bp 5 /* Data Register Empty Flag bit position. */ + +#define USART_FERR_bm 0x10 /* Frame Error bit mask. */ +#define USART_FERR_bp 4 /* Frame Error bit position. */ + +#define USART_BUFOVF_bm 0x08 /* Buffer Overflow bit mask. */ +#define USART_BUFOVF_bp 3 /* Buffer Overflow bit position. */ + +#define USART_PERR_bm 0x04 /* Parity Error bit mask. */ +#define USART_PERR_bp 2 /* Parity Error bit position. */ + +#define USART_RXB8_bm 0x01 /* Receive Bit 8 bit mask. */ +#define USART_RXB8_bp 0 /* Receive Bit 8 bit position. */ + +/* USART.CTRLA bit masks and bit positions */ +#define USART_RXCINTLVL_gm 0x30 /* Receive Interrupt Level group mask. */ +#define USART_RXCINTLVL_gp 4 /* Receive Interrupt Level group position. */ +#define USART_RXCINTLVL0_bm (1<<4) /* Receive Interrupt Level bit 0 mask. */ +#define USART_RXCINTLVL0_bp 4 /* Receive Interrupt Level bit 0 position. */ +#define USART_RXCINTLVL1_bm (1<<5) /* Receive Interrupt Level bit 1 mask. */ +#define USART_RXCINTLVL1_bp 5 /* Receive Interrupt Level bit 1 position. */ + +#define USART_TXCINTLVL_gm 0x0C /* Transmit Interrupt Level group mask. */ +#define USART_TXCINTLVL_gp 2 /* Transmit Interrupt Level group position. */ +#define USART_TXCINTLVL0_bm (1<<2) /* Transmit Interrupt Level bit 0 mask. */ +#define USART_TXCINTLVL0_bp 2 /* Transmit Interrupt Level bit 0 position. */ +#define USART_TXCINTLVL1_bm (1<<3) /* Transmit Interrupt Level bit 1 mask. */ +#define USART_TXCINTLVL1_bp 3 /* Transmit Interrupt Level bit 1 position. */ + +#define USART_DREINTLVL_gm 0x03 /* Data Register Empty Interrupt Level group mask. */ +#define USART_DREINTLVL_gp 0 /* Data Register Empty Interrupt Level group position. */ +#define USART_DREINTLVL0_bm (1<<0) /* Data Register Empty Interrupt Level bit 0 mask. */ +#define USART_DREINTLVL0_bp 0 /* Data Register Empty Interrupt Level bit 0 position. */ +#define USART_DREINTLVL1_bm (1<<1) /* Data Register Empty Interrupt Level bit 1 mask. */ +#define USART_DREINTLVL1_bp 1 /* Data Register Empty Interrupt Level bit 1 position. */ + +/* USART.CTRLB bit masks and bit positions */ +#define USART_RXEN_bm 0x10 /* Receiver Enable bit mask. */ +#define USART_RXEN_bp 4 /* Receiver Enable bit position. */ + +#define USART_TXEN_bm 0x08 /* Transmitter Enable bit mask. */ +#define USART_TXEN_bp 3 /* Transmitter Enable bit position. */ + +#define USART_CLK2X_bm 0x04 /* Double transmission speed bit mask. */ +#define USART_CLK2X_bp 2 /* Double transmission speed bit position. */ + +#define USART_MPCM_bm 0x02 /* Multi-processor Communication Mode bit mask. */ +#define USART_MPCM_bp 1 /* Multi-processor Communication Mode bit position. */ + +#define USART_TXB8_bm 0x01 /* Transmit bit 8 bit mask. */ +#define USART_TXB8_bp 0 /* Transmit bit 8 bit position. */ + +/* USART.CTRLC bit masks and bit positions */ +#define USART_CMODE_gm 0xC0 /* Communication Mode group mask. */ +#define USART_CMODE_gp 6 /* Communication Mode group position. */ +#define USART_CMODE0_bm (1<<6) /* Communication Mode bit 0 mask. */ +#define USART_CMODE0_bp 6 /* Communication Mode bit 0 position. */ +#define USART_CMODE1_bm (1<<7) /* Communication Mode bit 1 mask. */ +#define USART_CMODE1_bp 7 /* Communication Mode bit 1 position. */ + +#define USART_PMODE_gm 0x30 /* Parity Mode group mask. */ +#define USART_PMODE_gp 4 /* Parity Mode group position. */ +#define USART_PMODE0_bm (1<<4) /* Parity Mode bit 0 mask. */ +#define USART_PMODE0_bp 4 /* Parity Mode bit 0 position. */ +#define USART_PMODE1_bm (1<<5) /* Parity Mode bit 1 mask. */ +#define USART_PMODE1_bp 5 /* Parity Mode bit 1 position. */ + +#define USART_SBMODE_bm 0x08 /* Stop Bit Mode bit mask. */ +#define USART_SBMODE_bp 3 /* Stop Bit Mode bit position. */ + +#define USART_CHSIZE_gm 0x07 /* Character Size group mask. */ +#define USART_CHSIZE_gp 0 /* Character Size group position. */ +#define USART_CHSIZE0_bm (1<<0) /* Character Size bit 0 mask. */ +#define USART_CHSIZE0_bp 0 /* Character Size bit 0 position. */ +#define USART_CHSIZE1_bm (1<<1) /* Character Size bit 1 mask. */ +#define USART_CHSIZE1_bp 1 /* Character Size bit 1 position. */ +#define USART_CHSIZE2_bm (1<<2) /* Character Size bit 2 mask. */ +#define USART_CHSIZE2_bp 2 /* Character Size bit 2 position. */ + +/* USART.BAUDCTRLA bit masks and bit positions */ +#define USART_BSEL_gm 0xFF /* Baud Rate Selection Bits [7:0] group mask. */ +#define USART_BSEL_gp 0 /* Baud Rate Selection Bits [7:0] group position. */ +#define USART_BSEL0_bm (1<<0) /* Baud Rate Selection Bits [7:0] bit 0 mask. */ +#define USART_BSEL0_bp 0 /* Baud Rate Selection Bits [7:0] bit 0 position. */ +#define USART_BSEL1_bm (1<<1) /* Baud Rate Selection Bits [7:0] bit 1 mask. */ +#define USART_BSEL1_bp 1 /* Baud Rate Selection Bits [7:0] bit 1 position. */ +#define USART_BSEL2_bm (1<<2) /* Baud Rate Selection Bits [7:0] bit 2 mask. */ +#define USART_BSEL2_bp 2 /* Baud Rate Selection Bits [7:0] bit 2 position. */ +#define USART_BSEL3_bm (1<<3) /* Baud Rate Selection Bits [7:0] bit 3 mask. */ +#define USART_BSEL3_bp 3 /* Baud Rate Selection Bits [7:0] bit 3 position. */ +#define USART_BSEL4_bm (1<<4) /* Baud Rate Selection Bits [7:0] bit 4 mask. */ +#define USART_BSEL4_bp 4 /* Baud Rate Selection Bits [7:0] bit 4 position. */ +#define USART_BSEL5_bm (1<<5) /* Baud Rate Selection Bits [7:0] bit 5 mask. */ +#define USART_BSEL5_bp 5 /* Baud Rate Selection Bits [7:0] bit 5 position. */ +#define USART_BSEL6_bm (1<<6) /* Baud Rate Selection Bits [7:0] bit 6 mask. */ +#define USART_BSEL6_bp 6 /* Baud Rate Selection Bits [7:0] bit 6 position. */ +#define USART_BSEL7_bm (1<<7) /* Baud Rate Selection Bits [7:0] bit 7 mask. */ +#define USART_BSEL7_bp 7 /* Baud Rate Selection Bits [7:0] bit 7 position. */ + +/* USART.BAUDCTRLB bit masks and bit positions */ +#define USART_BSCALE_gm 0xF0 /* Baud Rate Scale group mask. */ +#define USART_BSCALE_gp 4 /* Baud Rate Scale group position. */ +#define USART_BSCALE0_bm (1<<4) /* Baud Rate Scale bit 0 mask. */ +#define USART_BSCALE0_bp 4 /* Baud Rate Scale bit 0 position. */ +#define USART_BSCALE1_bm (1<<5) /* Baud Rate Scale bit 1 mask. */ +#define USART_BSCALE1_bp 5 /* Baud Rate Scale bit 1 position. */ +#define USART_BSCALE2_bm (1<<6) /* Baud Rate Scale bit 2 mask. */ +#define USART_BSCALE2_bp 6 /* Baud Rate Scale bit 2 position. */ +#define USART_BSCALE3_bm (1<<7) /* Baud Rate Scale bit 3 mask. */ +#define USART_BSCALE3_bp 7 /* Baud Rate Scale bit 3 position. */ + +/* USART_BSEL Predefined. */ +/* USART_BSEL Predefined. */ + +/* SPI - Serial Peripheral Interface */ +/* SPI.CTRL bit masks and bit positions */ +#define SPI_CLK2X_bm 0x80 /* Enable Double Speed bit mask. */ +#define SPI_CLK2X_bp 7 /* Enable Double Speed bit position. */ + +#define SPI_ENABLE_bm 0x40 /* Enable Module bit mask. */ +#define SPI_ENABLE_bp 6 /* Enable Module bit position. */ + +#define SPI_DORD_bm 0x20 /* Data Order Setting bit mask. */ +#define SPI_DORD_bp 5 /* Data Order Setting bit position. */ + +#define SPI_MASTER_bm 0x10 /* Master Operation Enable bit mask. */ +#define SPI_MASTER_bp 4 /* Master Operation Enable bit position. */ + +#define SPI_MODE_gm 0x0C /* SPI Mode group mask. */ +#define SPI_MODE_gp 2 /* SPI Mode group position. */ +#define SPI_MODE0_bm (1<<2) /* SPI Mode bit 0 mask. */ +#define SPI_MODE0_bp 2 /* SPI Mode bit 0 position. */ +#define SPI_MODE1_bm (1<<3) /* SPI Mode bit 1 mask. */ +#define SPI_MODE1_bp 3 /* SPI Mode bit 1 position. */ + +#define SPI_PRESCALER_gm 0x03 /* Prescaler group mask. */ +#define SPI_PRESCALER_gp 0 /* Prescaler group position. */ +#define SPI_PRESCALER0_bm (1<<0) /* Prescaler bit 0 mask. */ +#define SPI_PRESCALER0_bp 0 /* Prescaler bit 0 position. */ +#define SPI_PRESCALER1_bm (1<<1) /* Prescaler bit 1 mask. */ +#define SPI_PRESCALER1_bp 1 /* Prescaler bit 1 position. */ + +/* SPI.INTCTRL bit masks and bit positions */ +#define SPI_INTLVL_gm 0x03 /* Interrupt level group mask. */ +#define SPI_INTLVL_gp 0 /* Interrupt level group position. */ +#define SPI_INTLVL0_bm (1<<0) /* Interrupt level bit 0 mask. */ +#define SPI_INTLVL0_bp 0 /* Interrupt level bit 0 position. */ +#define SPI_INTLVL1_bm (1<<1) /* Interrupt level bit 1 mask. */ +#define SPI_INTLVL1_bp 1 /* Interrupt level bit 1 position. */ + +/* SPI.STATUS bit masks and bit positions */ +#define SPI_IF_bm 0x80 /* Interrupt Flag bit mask. */ +#define SPI_IF_bp 7 /* Interrupt Flag bit position. */ + +#define SPI_WRCOL_bm 0x40 /* Write Collision bit mask. */ +#define SPI_WRCOL_bp 6 /* Write Collision bit position. */ + +/* IRCOM - IR Communication Module */ +/* IRCOM.CTRL bit masks and bit positions */ +#define IRCOM_EVSEL_gm 0x0F /* Event Channel Select group mask. */ +#define IRCOM_EVSEL_gp 0 /* Event Channel Select group position. */ +#define IRCOM_EVSEL0_bm (1<<0) /* Event Channel Select bit 0 mask. */ +#define IRCOM_EVSEL0_bp 0 /* Event Channel Select bit 0 position. */ +#define IRCOM_EVSEL1_bm (1<<1) /* Event Channel Select bit 1 mask. */ +#define IRCOM_EVSEL1_bp 1 /* Event Channel Select bit 1 position. */ +#define IRCOM_EVSEL2_bm (1<<2) /* Event Channel Select bit 2 mask. */ +#define IRCOM_EVSEL2_bp 2 /* Event Channel Select bit 2 position. */ +#define IRCOM_EVSEL3_bm (1<<3) /* Event Channel Select bit 3 mask. */ +#define IRCOM_EVSEL3_bp 3 /* Event Channel Select bit 3 position. */ + +/* FUSE - Fuses and Lockbits */ +/* NVM_FUSES.FUSEBYTE0 bit masks and bit positions */ +#define NVM_FUSES_JTAGUSERID_gm 0xFF /* JTAG User ID group mask. */ +#define NVM_FUSES_JTAGUSERID_gp 0 /* JTAG User ID group position. */ +#define NVM_FUSES_JTAGUSERID0_bm (1<<0) /* JTAG User ID bit 0 mask. */ +#define NVM_FUSES_JTAGUSERID0_bp 0 /* JTAG User ID bit 0 position. */ +#define NVM_FUSES_JTAGUSERID1_bm (1<<1) /* JTAG User ID bit 1 mask. */ +#define NVM_FUSES_JTAGUSERID1_bp 1 /* JTAG User ID bit 1 position. */ +#define NVM_FUSES_JTAGUSERID2_bm (1<<2) /* JTAG User ID bit 2 mask. */ +#define NVM_FUSES_JTAGUSERID2_bp 2 /* JTAG User ID bit 2 position. */ +#define NVM_FUSES_JTAGUSERID3_bm (1<<3) /* JTAG User ID bit 3 mask. */ +#define NVM_FUSES_JTAGUSERID3_bp 3 /* JTAG User ID bit 3 position. */ +#define NVM_FUSES_JTAGUSERID4_bm (1<<4) /* JTAG User ID bit 4 mask. */ +#define NVM_FUSES_JTAGUSERID4_bp 4 /* JTAG User ID bit 4 position. */ +#define NVM_FUSES_JTAGUSERID5_bm (1<<5) /* JTAG User ID bit 5 mask. */ +#define NVM_FUSES_JTAGUSERID5_bp 5 /* JTAG User ID bit 5 position. */ +#define NVM_FUSES_JTAGUSERID6_bm (1<<6) /* JTAG User ID bit 6 mask. */ +#define NVM_FUSES_JTAGUSERID6_bp 6 /* JTAG User ID bit 6 position. */ +#define NVM_FUSES_JTAGUSERID7_bm (1<<7) /* JTAG User ID bit 7 mask. */ +#define NVM_FUSES_JTAGUSERID7_bp 7 /* JTAG User ID bit 7 position. */ + +/* NVM_FUSES.FUSEBYTE1 bit masks and bit positions */ +#define NVM_FUSES_WDWP_gm 0xF0 /* Watchdog Window Timeout Period group mask. */ +#define NVM_FUSES_WDWP_gp 4 /* Watchdog Window Timeout Period group position. */ +#define NVM_FUSES_WDWP0_bm (1<<4) /* Watchdog Window Timeout Period bit 0 mask. */ +#define NVM_FUSES_WDWP0_bp 4 /* Watchdog Window Timeout Period bit 0 position. */ +#define NVM_FUSES_WDWP1_bm (1<<5) /* Watchdog Window Timeout Period bit 1 mask. */ +#define NVM_FUSES_WDWP1_bp 5 /* Watchdog Window Timeout Period bit 1 position. */ +#define NVM_FUSES_WDWP2_bm (1<<6) /* Watchdog Window Timeout Period bit 2 mask. */ +#define NVM_FUSES_WDWP2_bp 6 /* Watchdog Window Timeout Period bit 2 position. */ +#define NVM_FUSES_WDWP3_bm (1<<7) /* Watchdog Window Timeout Period bit 3 mask. */ +#define NVM_FUSES_WDWP3_bp 7 /* Watchdog Window Timeout Period bit 3 position. */ + +#define NVM_FUSES_WDP_gm 0x0F /* Watchdog Timeout Period group mask. */ +#define NVM_FUSES_WDP_gp 0 /* Watchdog Timeout Period group position. */ +#define NVM_FUSES_WDP0_bm (1<<0) /* Watchdog Timeout Period bit 0 mask. */ +#define NVM_FUSES_WDP0_bp 0 /* Watchdog Timeout Period bit 0 position. */ +#define NVM_FUSES_WDP1_bm (1<<1) /* Watchdog Timeout Period bit 1 mask. */ +#define NVM_FUSES_WDP1_bp 1 /* Watchdog Timeout Period bit 1 position. */ +#define NVM_FUSES_WDP2_bm (1<<2) /* Watchdog Timeout Period bit 2 mask. */ +#define NVM_FUSES_WDP2_bp 2 /* Watchdog Timeout Period bit 2 position. */ +#define NVM_FUSES_WDP3_bm (1<<3) /* Watchdog Timeout Period bit 3 mask. */ +#define NVM_FUSES_WDP3_bp 3 /* Watchdog Timeout Period bit 3 position. */ + +/* NVM_FUSES.FUSEBYTE2 bit masks and bit positions */ +#define NVM_FUSES_BOOTRST_bm 0x40 /* Boot Loader Section Reset Vector bit mask. */ +#define NVM_FUSES_BOOTRST_bp 6 /* Boot Loader Section Reset Vector bit position. */ + +#define NVM_FUSES_TOSCSEL_bm 0x20 /* Timer Oscillator pin location bit mask. */ +#define NVM_FUSES_TOSCSEL_bp 5 /* Timer Oscillator pin location bit position. */ + +#define NVM_FUSES_BODPD_gm 0x03 /* BOD Operation in Power-Down Mode group mask. */ +#define NVM_FUSES_BODPD_gp 0 /* BOD Operation in Power-Down Mode group position. */ +#define NVM_FUSES_BODPD0_bm (1<<0) /* BOD Operation in Power-Down Mode bit 0 mask. */ +#define NVM_FUSES_BODPD0_bp 0 /* BOD Operation in Power-Down Mode bit 0 position. */ +#define NVM_FUSES_BODPD1_bm (1<<1) /* BOD Operation in Power-Down Mode bit 1 mask. */ +#define NVM_FUSES_BODPD1_bp 1 /* BOD Operation in Power-Down Mode bit 1 position. */ + +/* NVM_FUSES.FUSEBYTE4 bit masks and bit positions */ +#define NVM_FUSES_RSTDISBL_bm 0x10 /* External Reset Disable bit mask. */ +#define NVM_FUSES_RSTDISBL_bp 4 /* External Reset Disable bit position. */ + +#define NVM_FUSES_SUT_gm 0x0C /* Start-up Time group mask. */ +#define NVM_FUSES_SUT_gp 2 /* Start-up Time group position. */ +#define NVM_FUSES_SUT0_bm (1<<2) /* Start-up Time bit 0 mask. */ +#define NVM_FUSES_SUT0_bp 2 /* Start-up Time bit 0 position. */ +#define NVM_FUSES_SUT1_bm (1<<3) /* Start-up Time bit 1 mask. */ +#define NVM_FUSES_SUT1_bp 3 /* Start-up Time bit 1 position. */ + +#define NVM_FUSES_WDLOCK_bm 0x02 /* Watchdog Timer Lock bit mask. */ +#define NVM_FUSES_WDLOCK_bp 1 /* Watchdog Timer Lock bit position. */ + +#define NVM_FUSES_JTAGEN_bm 0x01 /* JTAG Interface Enable bit mask. */ +#define NVM_FUSES_JTAGEN_bp 0 /* JTAG Interface Enable bit position. */ + +/* NVM_FUSES.FUSEBYTE5 bit masks and bit positions */ +#define NVM_FUSES_BODACT_gm 0x30 /* BOD Operation in Active Mode group mask. */ +#define NVM_FUSES_BODACT_gp 4 /* BOD Operation in Active Mode group position. */ +#define NVM_FUSES_BODACT0_bm (1<<4) /* BOD Operation in Active Mode bit 0 mask. */ +#define NVM_FUSES_BODACT0_bp 4 /* BOD Operation in Active Mode bit 0 position. */ +#define NVM_FUSES_BODACT1_bm (1<<5) /* BOD Operation in Active Mode bit 1 mask. */ +#define NVM_FUSES_BODACT1_bp 5 /* BOD Operation in Active Mode bit 1 position. */ + +#define NVM_FUSES_EESAVE_bm 0x08 /* Preserve EEPROM Through Chip Erase bit mask. */ +#define NVM_FUSES_EESAVE_bp 3 /* Preserve EEPROM Through Chip Erase bit position. */ + +#define NVM_FUSES_BODLVL_gm 0x07 /* Brownout Detection Voltage Level group mask. */ +#define NVM_FUSES_BODLVL_gp 0 /* Brownout Detection Voltage Level group position. */ +#define NVM_FUSES_BODLVL0_bm (1<<0) /* Brownout Detection Voltage Level bit 0 mask. */ +#define NVM_FUSES_BODLVL0_bp 0 /* Brownout Detection Voltage Level bit 0 position. */ +#define NVM_FUSES_BODLVL1_bm (1<<1) /* Brownout Detection Voltage Level bit 1 mask. */ +#define NVM_FUSES_BODLVL1_bp 1 /* Brownout Detection Voltage Level bit 1 position. */ +#define NVM_FUSES_BODLVL2_bm (1<<2) /* Brownout Detection Voltage Level bit 2 mask. */ +#define NVM_FUSES_BODLVL2_bp 2 /* Brownout Detection Voltage Level bit 2 position. */ + +/* LOCKBIT - Fuses and Lockbits */ +/* NVM_LOCKBITS.LOCKBITS bit masks and bit positions */ +#define NVM_LOCKBITS_BLBB_gm 0xC0 /* Boot Lock Bits - Boot Section group mask. */ +#define NVM_LOCKBITS_BLBB_gp 6 /* Boot Lock Bits - Boot Section group position. */ +#define NVM_LOCKBITS_BLBB0_bm (1<<6) /* Boot Lock Bits - Boot Section bit 0 mask. */ +#define NVM_LOCKBITS_BLBB0_bp 6 /* Boot Lock Bits - Boot Section bit 0 position. */ +#define NVM_LOCKBITS_BLBB1_bm (1<<7) /* Boot Lock Bits - Boot Section bit 1 mask. */ +#define NVM_LOCKBITS_BLBB1_bp 7 /* Boot Lock Bits - Boot Section bit 1 position. */ + +#define NVM_LOCKBITS_BLBA_gm 0x30 /* Boot Lock Bits - Application Section group mask. */ +#define NVM_LOCKBITS_BLBA_gp 4 /* Boot Lock Bits - Application Section group position. */ +#define NVM_LOCKBITS_BLBA0_bm (1<<4) /* Boot Lock Bits - Application Section bit 0 mask. */ +#define NVM_LOCKBITS_BLBA0_bp 4 /* Boot Lock Bits - Application Section bit 0 position. */ +#define NVM_LOCKBITS_BLBA1_bm (1<<5) /* Boot Lock Bits - Application Section bit 1 mask. */ +#define NVM_LOCKBITS_BLBA1_bp 5 /* Boot Lock Bits - Application Section bit 1 position. */ + +#define NVM_LOCKBITS_BLBAT_gm 0x0C /* Boot Lock Bits - Application Table group mask. */ +#define NVM_LOCKBITS_BLBAT_gp 2 /* Boot Lock Bits - Application Table group position. */ +#define NVM_LOCKBITS_BLBAT0_bm (1<<2) /* Boot Lock Bits - Application Table bit 0 mask. */ +#define NVM_LOCKBITS_BLBAT0_bp 2 /* Boot Lock Bits - Application Table bit 0 position. */ +#define NVM_LOCKBITS_BLBAT1_bm (1<<3) /* Boot Lock Bits - Application Table bit 1 mask. */ +#define NVM_LOCKBITS_BLBAT1_bp 3 /* Boot Lock Bits - Application Table bit 1 position. */ + +#define NVM_LOCKBITS_LB_gm 0x03 /* Lock Bits group mask. */ +#define NVM_LOCKBITS_LB_gp 0 /* Lock Bits group position. */ +#define NVM_LOCKBITS_LB0_bm (1<<0) /* Lock Bits bit 0 mask. */ +#define NVM_LOCKBITS_LB0_bp 0 /* Lock Bits bit 0 position. */ +#define NVM_LOCKBITS_LB1_bm (1<<1) /* Lock Bits bit 1 mask. */ +#define NVM_LOCKBITS_LB1_bp 1 /* Lock Bits bit 1 position. */ + + + +// Generic Port Pins + +#define PIN0_bm 0x01 +#define PIN0_bp 0 +#define PIN1_bm 0x02 +#define PIN1_bp 1 +#define PIN2_bm 0x04 +#define PIN2_bp 2 +#define PIN3_bm 0x08 +#define PIN3_bp 3 +#define PIN4_bm 0x10 +#define PIN4_bp 4 +#define PIN5_bm 0x20 +#define PIN5_bp 5 +#define PIN6_bm 0x40 +#define PIN6_bp 6 +#define PIN7_bm 0x80 +#define PIN7_bp 7 + +/* ========== Interrupt Vector Definitions ========== */ +/* Vector 0 is the reset vector */ + +/* OSC interrupt vectors */ +#define OSC_OSCF_vect_num 1 +#define OSC_OSCF_vect _VECTOR(1) /* Oscillator Failure Interrupt (NMI) */ + +/* PORTC interrupt vectors */ +#define PORTC_INT0_vect_num 2 +#define PORTC_INT0_vect _VECTOR(2) /* External Interrupt 0 */ +#define PORTC_INT1_vect_num 3 +#define PORTC_INT1_vect _VECTOR(3) /* External Interrupt 1 */ + +/* PORTR interrupt vectors */ +#define PORTR_INT0_vect_num 4 +#define PORTR_INT0_vect _VECTOR(4) /* External Interrupt 0 */ +#define PORTR_INT1_vect_num 5 +#define PORTR_INT1_vect _VECTOR(5) /* External Interrupt 1 */ + +/* DMA interrupt vectors */ +#define DMA_CH0_vect_num 6 +#define DMA_CH0_vect _VECTOR(6) /* Channel 0 Interrupt */ +#define DMA_CH1_vect_num 7 +#define DMA_CH1_vect _VECTOR(7) /* Channel 1 Interrupt */ +#define DMA_CH2_vect_num 8 +#define DMA_CH2_vect _VECTOR(8) /* Channel 2 Interrupt */ +#define DMA_CH3_vect_num 9 +#define DMA_CH3_vect _VECTOR(9) /* Channel 3 Interrupt */ + +/* RTC interrupt vectors */ +#define RTC_OVF_vect_num 10 +#define RTC_OVF_vect _VECTOR(10) /* Overflow Interrupt */ +#define RTC_COMP_vect_num 11 +#define RTC_COMP_vect _VECTOR(11) /* Compare Interrupt */ + +/* TWIC interrupt vectors */ +#define TWIC_TWIS_vect_num 12 +#define TWIC_TWIS_vect _VECTOR(12) /* TWI Slave Interrupt */ +#define TWIC_TWIM_vect_num 13 +#define TWIC_TWIM_vect _VECTOR(13) /* TWI Master Interrupt */ + +/* TCC0 interrupt vectors */ +#define TCC0_OVF_vect_num 14 +#define TCC0_OVF_vect _VECTOR(14) /* Overflow Interrupt */ + +/* TCC2 interrupt vectors */ +#define TCC2_LUNF_vect_num 14 +#define TCC2_LUNF_vect _VECTOR(14) /* Low Byte Underflow Interrupt */ + +/* TCC0 interrupt vectors */ +#define TCC0_ERR_vect_num 15 +#define TCC0_ERR_vect _VECTOR(15) /* Error Interrupt */ + +/* TCC2 interrupt vectors */ +#define TCC2_HUNF_vect_num 15 +#define TCC2_HUNF_vect _VECTOR(15) /* High Byte Underflow Interrupt */ + +/* TCC0 interrupt vectors */ +#define TCC0_CCA_vect_num 16 +#define TCC0_CCA_vect _VECTOR(16) /* Compare or Capture A Interrupt */ + +/* TCC2 interrupt vectors */ +#define TCC2_LCMPA_vect_num 16 +#define TCC2_LCMPA_vect _VECTOR(16) /* Low Byte Compare A Interrupt */ + +/* TCC0 interrupt vectors */ +#define TCC0_CCB_vect_num 17 +#define TCC0_CCB_vect _VECTOR(17) /* Compare or Capture B Interrupt */ + +/* TCC2 interrupt vectors */ +#define TCC2_LCMPB_vect_num 17 +#define TCC2_LCMPB_vect _VECTOR(17) /* Low Byte Compare B Interrupt */ + +/* TCC0 interrupt vectors */ +#define TCC0_CCC_vect_num 18 +#define TCC0_CCC_vect _VECTOR(18) /* Compare or Capture C Interrupt */ + +/* TCC2 interrupt vectors */ +#define TCC2_LCMPC_vect_num 18 +#define TCC2_LCMPC_vect _VECTOR(18) /* Low Byte Compare C Interrupt */ + +/* TCC0 interrupt vectors */ +#define TCC0_CCD_vect_num 19 +#define TCC0_CCD_vect _VECTOR(19) /* Compare or Capture D Interrupt */ + +/* TCC2 interrupt vectors */ +#define TCC2_LCMPD_vect_num 19 +#define TCC2_LCMPD_vect _VECTOR(19) /* Low Byte Compare D Interrupt */ + +/* TCC1 interrupt vectors */ +#define TCC1_OVF_vect_num 20 +#define TCC1_OVF_vect _VECTOR(20) /* Overflow Interrupt */ +#define TCC1_ERR_vect_num 21 +#define TCC1_ERR_vect _VECTOR(21) /* Error Interrupt */ +#define TCC1_CCA_vect_num 22 +#define TCC1_CCA_vect _VECTOR(22) /* Compare or Capture A Interrupt */ +#define TCC1_CCB_vect_num 23 +#define TCC1_CCB_vect _VECTOR(23) /* Compare or Capture B Interrupt */ + +/* SPIC interrupt vectors */ +#define SPIC_INT_vect_num 24 +#define SPIC_INT_vect _VECTOR(24) /* SPI Interrupt */ + +/* USARTC0 interrupt vectors */ +#define USARTC0_RXC_vect_num 25 +#define USARTC0_RXC_vect _VECTOR(25) /* Reception Complete Interrupt */ +#define USARTC0_DRE_vect_num 26 +#define USARTC0_DRE_vect _VECTOR(26) /* Data Register Empty Interrupt */ +#define USARTC0_TXC_vect_num 27 +#define USARTC0_TXC_vect _VECTOR(27) /* Transmission Complete Interrupt */ + +/* USARTC1 interrupt vectors */ +#define USARTC1_RXC_vect_num 28 +#define USARTC1_RXC_vect _VECTOR(28) /* Reception Complete Interrupt */ +#define USARTC1_DRE_vect_num 29 +#define USARTC1_DRE_vect _VECTOR(29) /* Data Register Empty Interrupt */ +#define USARTC1_TXC_vect_num 30 +#define USARTC1_TXC_vect _VECTOR(30) /* Transmission Complete Interrupt */ + +/* AES interrupt vectors */ +#define AES_INT_vect_num 31 +#define AES_INT_vect _VECTOR(31) /* AES Interrupt */ + +/* NVM interrupt vectors */ +#define NVM_EE_vect_num 32 +#define NVM_EE_vect _VECTOR(32) /* EE Interrupt */ +#define NVM_SPM_vect_num 33 +#define NVM_SPM_vect _VECTOR(33) /* SPM Interrupt */ + +/* PORTB interrupt vectors */ +#define PORTB_INT0_vect_num 34 +#define PORTB_INT0_vect _VECTOR(34) /* External Interrupt 0 */ +#define PORTB_INT1_vect_num 35 +#define PORTB_INT1_vect _VECTOR(35) /* External Interrupt 1 */ + +/* ACB interrupt vectors */ +#define ACB_AC0_vect_num 36 +#define ACB_AC0_vect _VECTOR(36) /* AC0 Interrupt */ +#define ACB_AC1_vect_num 37 +#define ACB_AC1_vect _VECTOR(37) /* AC1 Interrupt */ +#define ACB_ACW_vect_num 38 +#define ACB_ACW_vect _VECTOR(38) /* ACW Window Mode Interrupt */ + +/* ADCB interrupt vectors */ +#define ADCB_CH0_vect_num 39 +#define ADCB_CH0_vect _VECTOR(39) /* Interrupt 0 */ +#define ADCB_CH1_vect_num 40 +#define ADCB_CH1_vect _VECTOR(40) /* Interrupt 1 */ +#define ADCB_CH2_vect_num 41 +#define ADCB_CH2_vect _VECTOR(41) /* Interrupt 2 */ +#define ADCB_CH3_vect_num 42 +#define ADCB_CH3_vect _VECTOR(42) /* Interrupt 3 */ + +/* PORTE interrupt vectors */ +#define PORTE_INT0_vect_num 43 +#define PORTE_INT0_vect _VECTOR(43) /* External Interrupt 0 */ +#define PORTE_INT1_vect_num 44 +#define PORTE_INT1_vect _VECTOR(44) /* External Interrupt 1 */ + +/* TWIE interrupt vectors */ +#define TWIE_TWIS_vect_num 45 +#define TWIE_TWIS_vect _VECTOR(45) /* TWI Slave Interrupt */ +#define TWIE_TWIM_vect_num 46 +#define TWIE_TWIM_vect _VECTOR(46) /* TWI Master Interrupt */ + +/* TCE0 interrupt vectors */ +#define TCE0_OVF_vect_num 47 +#define TCE0_OVF_vect _VECTOR(47) /* Overflow Interrupt */ + +/* TCE2 interrupt vectors */ +#define TCE2_LUNF_vect_num 47 +#define TCE2_LUNF_vect _VECTOR(47) /* Low Byte Underflow Interrupt */ + +/* TCE0 interrupt vectors */ +#define TCE0_ERR_vect_num 48 +#define TCE0_ERR_vect _VECTOR(48) /* Error Interrupt */ + +/* TCE2 interrupt vectors */ +#define TCE2_HUNF_vect_num 48 +#define TCE2_HUNF_vect _VECTOR(48) /* High Byte Underflow Interrupt */ + +/* TCE0 interrupt vectors */ +#define TCE0_CCA_vect_num 49 +#define TCE0_CCA_vect _VECTOR(49) /* Compare or Capture A Interrupt */ + +/* TCE2 interrupt vectors */ +#define TCE2_LCMPA_vect_num 49 +#define TCE2_LCMPA_vect _VECTOR(49) /* Low Byte Compare A Interrupt */ + +/* TCE0 interrupt vectors */ +#define TCE0_CCB_vect_num 50 +#define TCE0_CCB_vect _VECTOR(50) /* Compare or Capture B Interrupt */ + +/* TCE2 interrupt vectors */ +#define TCE2_LCMPB_vect_num 50 +#define TCE2_LCMPB_vect _VECTOR(50) /* Low Byte Compare B Interrupt */ + +/* TCE0 interrupt vectors */ +#define TCE0_CCC_vect_num 51 +#define TCE0_CCC_vect _VECTOR(51) /* Compare or Capture C Interrupt */ + +/* TCE2 interrupt vectors */ +#define TCE2_LCMPC_vect_num 51 +#define TCE2_LCMPC_vect _VECTOR(51) /* Low Byte Compare C Interrupt */ + +/* TCE0 interrupt vectors */ +#define TCE0_CCD_vect_num 52 +#define TCE0_CCD_vect _VECTOR(52) /* Compare or Capture D Interrupt */ + +/* TCE2 interrupt vectors */ +#define TCE2_LCMPD_vect_num 52 +#define TCE2_LCMPD_vect _VECTOR(52) /* Low Byte Compare D Interrupt */ + +/* TCE1 interrupt vectors */ +#define TCE1_OVF_vect_num 53 +#define TCE1_OVF_vect _VECTOR(53) /* Overflow Interrupt */ +#define TCE1_ERR_vect_num 54 +#define TCE1_ERR_vect _VECTOR(54) /* Error Interrupt */ +#define TCE1_CCA_vect_num 55 +#define TCE1_CCA_vect _VECTOR(55) /* Compare or Capture A Interrupt */ +#define TCE1_CCB_vect_num 56 +#define TCE1_CCB_vect _VECTOR(56) /* Compare or Capture B Interrupt */ + +/* SPIE interrupt vectors */ +#define SPIE_INT_vect_num 57 +#define SPIE_INT_vect _VECTOR(57) /* SPI Interrupt */ + +/* USARTE0 interrupt vectors */ +#define USARTE0_RXC_vect_num 58 +#define USARTE0_RXC_vect _VECTOR(58) /* Reception Complete Interrupt */ +#define USARTE0_DRE_vect_num 59 +#define USARTE0_DRE_vect _VECTOR(59) /* Data Register Empty Interrupt */ +#define USARTE0_TXC_vect_num 60 +#define USARTE0_TXC_vect _VECTOR(60) /* Transmission Complete Interrupt */ + +/* USARTE1 interrupt vectors */ +#define USARTE1_RXC_vect_num 61 +#define USARTE1_RXC_vect _VECTOR(61) /* Reception Complete Interrupt */ +#define USARTE1_DRE_vect_num 62 +#define USARTE1_DRE_vect _VECTOR(62) /* Data Register Empty Interrupt */ +#define USARTE1_TXC_vect_num 63 +#define USARTE1_TXC_vect _VECTOR(63) /* Transmission Complete Interrupt */ + +/* PORTD interrupt vectors */ +#define PORTD_INT0_vect_num 64 +#define PORTD_INT0_vect _VECTOR(64) /* External Interrupt 0 */ +#define PORTD_INT1_vect_num 65 +#define PORTD_INT1_vect _VECTOR(65) /* External Interrupt 1 */ + +/* PORTA interrupt vectors */ +#define PORTA_INT0_vect_num 66 +#define PORTA_INT0_vect _VECTOR(66) /* External Interrupt 0 */ +#define PORTA_INT1_vect_num 67 +#define PORTA_INT1_vect _VECTOR(67) /* External Interrupt 1 */ + +/* ACA interrupt vectors */ +#define ACA_AC0_vect_num 68 +#define ACA_AC0_vect _VECTOR(68) /* AC0 Interrupt */ +#define ACA_AC1_vect_num 69 +#define ACA_AC1_vect _VECTOR(69) /* AC1 Interrupt */ +#define ACA_ACW_vect_num 70 +#define ACA_ACW_vect _VECTOR(70) /* ACW Window Mode Interrupt */ + +/* ADCA interrupt vectors */ +#define ADCA_CH0_vect_num 71 +#define ADCA_CH0_vect _VECTOR(71) /* Interrupt 0 */ +#define ADCA_CH1_vect_num 72 +#define ADCA_CH1_vect _VECTOR(72) /* Interrupt 1 */ +#define ADCA_CH2_vect_num 73 +#define ADCA_CH2_vect _VECTOR(73) /* Interrupt 2 */ +#define ADCA_CH3_vect_num 74 +#define ADCA_CH3_vect _VECTOR(74) /* Interrupt 3 */ + +/* TCD0 interrupt vectors */ +#define TCD0_OVF_vect_num 77 +#define TCD0_OVF_vect _VECTOR(77) /* Overflow Interrupt */ + +/* TCD2 interrupt vectors */ +#define TCD2_LUNF_vect_num 77 +#define TCD2_LUNF_vect _VECTOR(77) /* Low Byte Underflow Interrupt */ + +/* TCD0 interrupt vectors */ +#define TCD0_ERR_vect_num 78 +#define TCD0_ERR_vect _VECTOR(78) /* Error Interrupt */ + +/* TCD2 interrupt vectors */ +#define TCD2_HUNF_vect_num 78 +#define TCD2_HUNF_vect _VECTOR(78) /* High Byte Underflow Interrupt */ + +/* TCD0 interrupt vectors */ +#define TCD0_CCA_vect_num 79 +#define TCD0_CCA_vect _VECTOR(79) /* Compare or Capture A Interrupt */ + +/* TCD2 interrupt vectors */ +#define TCD2_LCMPA_vect_num 79 +#define TCD2_LCMPA_vect _VECTOR(79) /* Low Byte Compare A Interrupt */ + +/* TCD0 interrupt vectors */ +#define TCD0_CCB_vect_num 80 +#define TCD0_CCB_vect _VECTOR(80) /* Compare or Capture B Interrupt */ + +/* TCD2 interrupt vectors */ +#define TCD2_LCMPB_vect_num 80 +#define TCD2_LCMPB_vect _VECTOR(80) /* Low Byte Compare B Interrupt */ + +/* TCD0 interrupt vectors */ +#define TCD0_CCC_vect_num 81 +#define TCD0_CCC_vect _VECTOR(81) /* Compare or Capture C Interrupt */ + +/* TCD2 interrupt vectors */ +#define TCD2_LCMPC_vect_num 81 +#define TCD2_LCMPC_vect _VECTOR(81) /* Low Byte Compare C Interrupt */ + +/* TCD0 interrupt vectors */ +#define TCD0_CCD_vect_num 82 +#define TCD0_CCD_vect _VECTOR(82) /* Compare or Capture D Interrupt */ + +/* TCD2 interrupt vectors */ +#define TCD2_LCMPD_vect_num 82 +#define TCD2_LCMPD_vect _VECTOR(82) /* Low Byte Compare D Interrupt */ + +/* TCD1 interrupt vectors */ +#define TCD1_OVF_vect_num 83 +#define TCD1_OVF_vect _VECTOR(83) /* Overflow Interrupt */ +#define TCD1_ERR_vect_num 84 +#define TCD1_ERR_vect _VECTOR(84) /* Error Interrupt */ +#define TCD1_CCA_vect_num 85 +#define TCD1_CCA_vect _VECTOR(85) /* Compare or Capture A Interrupt */ +#define TCD1_CCB_vect_num 86 +#define TCD1_CCB_vect _VECTOR(86) /* Compare or Capture B Interrupt */ + +/* SPID interrupt vectors */ +#define SPID_INT_vect_num 87 +#define SPID_INT_vect _VECTOR(87) /* SPI Interrupt */ + +/* USARTD0 interrupt vectors */ +#define USARTD0_RXC_vect_num 88 +#define USARTD0_RXC_vect _VECTOR(88) /* Reception Complete Interrupt */ +#define USARTD0_DRE_vect_num 89 +#define USARTD0_DRE_vect _VECTOR(89) /* Data Register Empty Interrupt */ +#define USARTD0_TXC_vect_num 90 +#define USARTD0_TXC_vect _VECTOR(90) /* Transmission Complete Interrupt */ + +/* USARTD1 interrupt vectors */ +#define USARTD1_RXC_vect_num 91 +#define USARTD1_RXC_vect _VECTOR(91) /* Reception Complete Interrupt */ +#define USARTD1_DRE_vect_num 92 +#define USARTD1_DRE_vect _VECTOR(92) /* Data Register Empty Interrupt */ +#define USARTD1_TXC_vect_num 93 +#define USARTD1_TXC_vect _VECTOR(93) /* Transmission Complete Interrupt */ + +/* PORTF interrupt vectors */ +#define PORTF_INT0_vect_num 104 +#define PORTF_INT0_vect _VECTOR(104) /* External Interrupt 0 */ +#define PORTF_INT1_vect_num 105 +#define PORTF_INT1_vect _VECTOR(105) /* External Interrupt 1 */ + +/* TCF0 interrupt vectors */ +#define TCF0_OVF_vect_num 108 +#define TCF0_OVF_vect _VECTOR(108) /* Overflow Interrupt */ + +/* TCF2 interrupt vectors */ +#define TCF2_LUNF_vect_num 108 +#define TCF2_LUNF_vect _VECTOR(108) /* Low Byte Underflow Interrupt */ + +/* TCF0 interrupt vectors */ +#define TCF0_ERR_vect_num 109 +#define TCF0_ERR_vect _VECTOR(109) /* Error Interrupt */ + +/* TCF2 interrupt vectors */ +#define TCF2_HUNF_vect_num 109 +#define TCF2_HUNF_vect _VECTOR(109) /* High Byte Underflow Interrupt */ + +/* TCF0 interrupt vectors */ +#define TCF0_CCA_vect_num 110 +#define TCF0_CCA_vect _VECTOR(110) /* Compare or Capture A Interrupt */ + +/* TCF2 interrupt vectors */ +#define TCF2_LCMPA_vect_num 110 +#define TCF2_LCMPA_vect _VECTOR(110) /* Low Byte Compare A Interrupt */ + +/* TCF0 interrupt vectors */ +#define TCF0_CCB_vect_num 111 +#define TCF0_CCB_vect _VECTOR(111) /* Compare or Capture B Interrupt */ + +/* TCF2 interrupt vectors */ +#define TCF2_LCMPB_vect_num 111 +#define TCF2_LCMPB_vect _VECTOR(111) /* Low Byte Compare B Interrupt */ + +/* TCF0 interrupt vectors */ +#define TCF0_CCC_vect_num 112 +#define TCF0_CCC_vect _VECTOR(112) /* Compare or Capture C Interrupt */ + +/* TCF2 interrupt vectors */ +#define TCF2_LCMPC_vect_num 112 +#define TCF2_LCMPC_vect _VECTOR(112) /* Low Byte Compare C Interrupt */ + +/* TCF0 interrupt vectors */ +#define TCF0_CCD_vect_num 113 +#define TCF0_CCD_vect _VECTOR(113) /* Compare or Capture D Interrupt */ + +/* TCF2 interrupt vectors */ +#define TCF2_LCMPD_vect_num 113 +#define TCF2_LCMPD_vect _VECTOR(113) /* Low Byte Compare D Interrupt */ + +/* USARTF0 interrupt vectors */ +#define USARTF0_RXC_vect_num 119 +#define USARTF0_RXC_vect _VECTOR(119) /* Reception Complete Interrupt */ +#define USARTF0_DRE_vect_num 120 +#define USARTF0_DRE_vect _VECTOR(120) /* Data Register Empty Interrupt */ +#define USARTF0_TXC_vect_num 121 +#define USARTF0_TXC_vect _VECTOR(121) /* Transmission Complete Interrupt */ + +/* USB interrupt vectors */ +#define USB_BUSEVENT_vect_num 125 +#define USB_BUSEVENT_vect _VECTOR(125) /* SOF, suspend, resume, reset bus event interrupts, crc, underflow, overflow and stall error interrupts */ +#define USB_TRNCOMPL_vect_num 126 +#define USB_TRNCOMPL_vect _VECTOR(126) /* Transaction complete interrupt */ + +#define _VECTOR_SIZE 4 /* Size of individual vector. */ +#define _VECTORS_SIZE (127 * _VECTOR_SIZE) + + +/* ========== Constants ========== */ + +#define PROGMEM_START (0x0000) +#define PROGMEM_SIZE (204800) +#define PROGMEM_END (PROGMEM_START + PROGMEM_SIZE - 1) + +#define APP_SECTION_START (0x0000) +#define APP_SECTION_SIZE (196608) +#define APP_SECTION_PAGE_SIZE (512) +#define APP_SECTION_END (APP_SECTION_START + APP_SECTION_SIZE - 1) + +#define APPTABLE_SECTION_START (0x2E000) +#define APPTABLE_SECTION_SIZE (8192) +#define APPTABLE_SECTION_PAGE_SIZE (512) +#define APPTABLE_SECTION_END (APPTABLE_SECTION_START + APPTABLE_SECTION_SIZE - 1) + +#define BOOT_SECTION_START (0x30000) +#define BOOT_SECTION_SIZE (8192) +#define BOOT_SECTION_PAGE_SIZE (512) +#define BOOT_SECTION_END (BOOT_SECTION_START + BOOT_SECTION_SIZE - 1) + +#define DATAMEM_START (0x0000) +#define DATAMEM_SIZE (24576) +#define DATAMEM_END (DATAMEM_START + DATAMEM_SIZE - 1) + +#define IO_START (0x0000) +#define IO_SIZE (4096) +#define IO_PAGE_SIZE (0) +#define IO_END (IO_START + IO_SIZE - 1) + +#define MAPPED_EEPROM_START (0x1000) +#define MAPPED_EEPROM_SIZE (2048) +#define MAPPED_EEPROM_PAGE_SIZE (0) +#define MAPPED_EEPROM_END (MAPPED_EEPROM_START + MAPPED_EEPROM_SIZE - 1) + +#define INTERNAL_SRAM_START (0x2000) +#define INTERNAL_SRAM_SIZE (16384) +#define INTERNAL_SRAM_PAGE_SIZE (0) +#define INTERNAL_SRAM_END (INTERNAL_SRAM_START + INTERNAL_SRAM_SIZE - 1) + +#define EEPROM_START (0x0000) +#define EEPROM_SIZE (2048) +#define EEPROM_PAGE_SIZE (32) +#define EEPROM_END (EEPROM_START + EEPROM_SIZE - 1) + +#define SIGNATURES_START (0x0000) +#define SIGNATURES_SIZE (3) +#define SIGNATURES_PAGE_SIZE (0) +#define SIGNATURES_END (SIGNATURES_START + SIGNATURES_SIZE - 1) + +#define FUSES_START (0x0000) +#define FUSES_SIZE (6) +#define FUSES_PAGE_SIZE (0) +#define FUSES_END (FUSES_START + FUSES_SIZE - 1) + +#define LOCKBITS_START (0x0000) +#define LOCKBITS_SIZE (1) +#define LOCKBITS_PAGE_SIZE (0) +#define LOCKBITS_END (LOCKBITS_START + LOCKBITS_SIZE - 1) + +#define USER_SIGNATURES_START (0x0000) +#define USER_SIGNATURES_SIZE (512) +#define USER_SIGNATURES_PAGE_SIZE (512) +#define USER_SIGNATURES_END (USER_SIGNATURES_START + USER_SIGNATURES_SIZE - 1) + +#define PROD_SIGNATURES_START (0x0000) +#define PROD_SIGNATURES_SIZE (52) +#define PROD_SIGNATURES_PAGE_SIZE (512) +#define PROD_SIGNATURES_END (PROD_SIGNATURES_START + PROD_SIGNATURES_SIZE - 1) + +#define FLASHSTART PROGMEM_START +#define FLASHEND PROGMEM_END +#define SPM_PAGESIZE 512 +#define RAMSTART INTERNAL_SRAM_START +#define RAMSIZE INTERNAL_SRAM_SIZE +#define RAMEND INTERNAL_SRAM_END +#define E2END EEPROM_END +#define E2PAGESIZE EEPROM_PAGE_SIZE + + +/* ========== Fuses ========== */ +#define FUSE_MEMORY_SIZE 6 + +/* Fuse Byte 0 */ +#define FUSE_JTAGUSERID0 (unsigned char)~_BV(0) /* JTAG User ID Bit 0 */ +#define FUSE_JTAGUSERID1 (unsigned char)~_BV(1) /* JTAG User ID Bit 1 */ +#define FUSE_JTAGUSERID2 (unsigned char)~_BV(2) /* JTAG User ID Bit 2 */ +#define FUSE_JTAGUSERID3 (unsigned char)~_BV(3) /* JTAG User ID Bit 3 */ +#define FUSE_JTAGUSERID4 (unsigned char)~_BV(4) /* JTAG User ID Bit 4 */ +#define FUSE_JTAGUSERID5 (unsigned char)~_BV(5) /* JTAG User ID Bit 5 */ +#define FUSE_JTAGUSERID6 (unsigned char)~_BV(6) /* JTAG User ID Bit 6 */ +#define FUSE_JTAGUSERID7 (unsigned char)~_BV(7) /* JTAG User ID Bit 7 */ +#define FUSE0_DEFAULT (0xFF) + +/* Fuse Byte 1 */ +#define FUSE_WDP0 (unsigned char)~_BV(0) /* Watchdog Timeout Period Bit 0 */ +#define FUSE_WDP1 (unsigned char)~_BV(1) /* Watchdog Timeout Period Bit 1 */ +#define FUSE_WDP2 (unsigned char)~_BV(2) /* Watchdog Timeout Period Bit 2 */ +#define FUSE_WDP3 (unsigned char)~_BV(3) /* Watchdog Timeout Period Bit 3 */ +#define FUSE_WDWP0 (unsigned char)~_BV(4) /* Watchdog Window Timeout Period Bit 0 */ +#define FUSE_WDWP1 (unsigned char)~_BV(5) /* Watchdog Window Timeout Period Bit 1 */ +#define FUSE_WDWP2 (unsigned char)~_BV(6) /* Watchdog Window Timeout Period Bit 2 */ +#define FUSE_WDWP3 (unsigned char)~_BV(7) /* Watchdog Window Timeout Period Bit 3 */ +#define FUSE1_DEFAULT (0xFF) + +/* Fuse Byte 2 */ +#define FUSE_BODPD0 (unsigned char)~_BV(0) /* BOD Operation in Power-Down Mode Bit 0 */ +#define FUSE_BODPD1 (unsigned char)~_BV(1) /* BOD Operation in Power-Down Mode Bit 1 */ +#define FUSE_TOSCSEL (unsigned char)~_BV(5) /* Timer Oscillator pin location */ +#define FUSE_BOOTRST (unsigned char)~_BV(6) /* Boot Loader Section Reset Vector */ +#define FUSE2_DEFAULT (0xFF) + +/* Fuse Byte 3 Reserved */ + +/* Fuse Byte 4 */ +#define FUSE_JTAGEN (unsigned char)~_BV(0) /* JTAG Interface Enable */ +#define FUSE_WDLOCK (unsigned char)~_BV(1) /* Watchdog Timer Lock */ +#define FUSE_SUT0 (unsigned char)~_BV(2) /* Start-up Time Bit 0 */ +#define FUSE_SUT1 (unsigned char)~_BV(3) /* Start-up Time Bit 1 */ +#define FUSE_RSTDISBL (unsigned char)~_BV(4) /* External Reset Disable */ +#define FUSE4_DEFAULT (0xFF) + +/* Fuse Byte 5 */ +#define FUSE_BODLVL0 (unsigned char)~_BV(0) /* Brownout Detection Voltage Level Bit 0 */ +#define FUSE_BODLVL1 (unsigned char)~_BV(1) /* Brownout Detection Voltage Level Bit 1 */ +#define FUSE_BODLVL2 (unsigned char)~_BV(2) /* Brownout Detection Voltage Level Bit 2 */ +#define FUSE_EESAVE (unsigned char)~_BV(3) /* Preserve EEPROM Through Chip Erase */ +#define FUSE_BODACT0 (unsigned char)~_BV(4) /* BOD Operation in Active Mode Bit 0 */ +#define FUSE_BODACT1 (unsigned char)~_BV(5) /* BOD Operation in Active Mode Bit 1 */ +#define FUSE5_DEFAULT (0xFF) + +/* ========== Lock Bits ========== */ +#define __LOCK_BITS_EXIST +#define __BOOT_LOCK_APPLICATION_TABLE_BITS_EXIST +#define __BOOT_LOCK_APPLICATION_BITS_EXIST +#define __BOOT_LOCK_BOOT_BITS_EXIST + +/* ========== Signature ========== */ +#define SIGNATURE_0 0x1E +#define SIGNATURE_1 0x97 +#define SIGNATURE_2 0x44 + +/* ========== Power Reduction Condition Definitions ========== */ + +/* PR.PRGEN */ +#define __AVR_HAVE_PRGEN (PR_USB_bm|PR_AES_bm|PR_EBI_bm|PR_RTC_bm|PR_EVSYS_bm|PR_DMA_bm) +#define __AVR_HAVE_PRGEN_USB +#define __AVR_HAVE_PRGEN_AES +#define __AVR_HAVE_PRGEN_EBI +#define __AVR_HAVE_PRGEN_RTC +#define __AVR_HAVE_PRGEN_EVSYS +#define __AVR_HAVE_PRGEN_DMA + +/* PR.PRPA */ +#define __AVR_HAVE_PRPA (PR_DAC_bm|PR_ADC_bm|PR_AC_bm) +#define __AVR_HAVE_PRPA_DAC +#define __AVR_HAVE_PRPA_ADC +#define __AVR_HAVE_PRPA_AC + +/* PR.PRPB */ +#define __AVR_HAVE_PRPB (PR_DAC_bm|PR_ADC_bm|PR_AC_bm) +#define __AVR_HAVE_PRPB_DAC +#define __AVR_HAVE_PRPB_ADC +#define __AVR_HAVE_PRPB_AC + +/* PR.PRPC */ +#define __AVR_HAVE_PRPC (PR_TWI_bm|PR_USART1_bm|PR_USART0_bm|PR_SPI_bm|PR_HIRES_bm|PR_TC1_bm|PR_TC0_bm) +#define __AVR_HAVE_PRPC_TWI +#define __AVR_HAVE_PRPC_USART1 +#define __AVR_HAVE_PRPC_USART0 +#define __AVR_HAVE_PRPC_SPI +#define __AVR_HAVE_PRPC_HIRES +#define __AVR_HAVE_PRPC_TC1 +#define __AVR_HAVE_PRPC_TC0 + +/* PR.PRPD */ +#define __AVR_HAVE_PRPD (PR_TWI_bm|PR_USART1_bm|PR_USART0_bm|PR_SPI_bm|PR_HIRES_bm|PR_TC1_bm|PR_TC0_bm) +#define __AVR_HAVE_PRPD_TWI +#define __AVR_HAVE_PRPD_USART1 +#define __AVR_HAVE_PRPD_USART0 +#define __AVR_HAVE_PRPD_SPI +#define __AVR_HAVE_PRPD_HIRES +#define __AVR_HAVE_PRPD_TC1 +#define __AVR_HAVE_PRPD_TC0 + +/* PR.PRPE */ +#define __AVR_HAVE_PRPE (PR_TWI_bm|PR_USART1_bm|PR_USART0_bm|PR_SPI_bm|PR_HIRES_bm|PR_TC1_bm|PR_TC0_bm) +#define __AVR_HAVE_PRPE_TWI +#define __AVR_HAVE_PRPE_USART1 +#define __AVR_HAVE_PRPE_USART0 +#define __AVR_HAVE_PRPE_SPI +#define __AVR_HAVE_PRPE_HIRES +#define __AVR_HAVE_PRPE_TC1 +#define __AVR_HAVE_PRPE_TC0 + +/* PR.PRPF */ +#define __AVR_HAVE_PRPF (PR_TWI_bm|PR_USART1_bm|PR_USART0_bm|PR_SPI_bm|PR_HIRES_bm|PR_TC1_bm|PR_TC0_bm) +#define __AVR_HAVE_PRPF_TWI +#define __AVR_HAVE_PRPF_USART1 +#define __AVR_HAVE_PRPF_USART0 +#define __AVR_HAVE_PRPF_SPI +#define __AVR_HAVE_PRPF_HIRES +#define __AVR_HAVE_PRPF_TC1 +#define __AVR_HAVE_PRPF_TC0 + + +#endif /* #ifdef _AVR_ATXMEGA192A3U_H_INCLUDED */ + diff --git a/src/avr/emu/src/avr/pgmspace.h b/src/avr/emu/src/avr/pgmspace.h new file mode 100644 index 0000000..5377eb9 --- /dev/null +++ b/src/avr/emu/src/avr/pgmspace.h @@ -0,0 +1,41 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#define PRPSTR "s" +#define PROGMEM +#define PGM_P const char * +#define PSTR(X) X +#define vfprintf_P vfprintf +#define printf_P printf +#define puts_P puts +#define sprintf_P sprintf +#define strcmp_P strcmp +#define pgm_read_ptr(x) *(x) +#define pgm_read_word(x) *(x) +#define pgm_read_byte(x) *(x) diff --git a/src/avr/emu/src/avr/wdt.h b/src/avr/emu/src/avr/wdt.h new file mode 100644 index 0000000..bcf6f99 --- /dev/null +++ b/src/avr/emu/src/avr/wdt.h @@ -0,0 +1,33 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#define WDTO_250MS 0 +#define wdt_enable(...) +#define wdt_disable() +#define wdt_reset() diff --git a/src/avr/emu/src/emu.c b/src/avr/emu/src/emu.c new file mode 100644 index 0000000..a0b06a5 --- /dev/null +++ b/src/avr/emu/src/emu.c @@ -0,0 +1,163 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + + +void __SPIC_INT_vect(); // DRV8711 SPI +void __I2C_ISR(); // I2C from RPi +void __ADCA_CH0_vect(); // Analog input +void __ADCA_CH1_vect(); // Analog input +void __RS485_DRE_vect(); // RS848 +void __RS485_TXC_vect(); // RS848 +void __RS485_RXC_vect(); // RS848 +void __SERIAL_DRE_vect(); // Serial to RPi +void __SERIAL_RXC_vect(); // Serial from RPi +void __STEP_LOW_LEVEL_ISR(); // Stepper lo interrupt +void __STEP_TIMER_ISR(); // Stepper hi interrupt +void __RTC_OVF_vect(); // RTC tick + +void motor_emulate_steps(int motor); + +extern int __argc; +extern char **__argv; + + +volatile uint8_t io_mem[4096] = {0}; + + +bool fast = false; +int serialByte = -1; +uint8_t i2cData[I2C_MAX_DATA]; +int i2cIndex = 0; +bool haveI2C = false; +fd_set readFDs; + + +void cli() {} +void sei() {} + + +void emu_init() { + // Parse command line args + for (int i = 0; i < __argc; i++) + if (strcmp(__argv[i], "--fast") == 0) fast = true; + + // Mark clocks ready + OSC.STATUS = OSC_XOSCRDY_bm | OSC_PLLRDY_bm | OSC_RC32KRDY_bm; + + // So usart_flush() returns + SERIAL_PORT.STATUS = USART_DREIF_bm | USART_TXCIF_bm; + + // Clear motor fault + PIN_PORT(MOTOR_FAULT_PIN)->IN |= PIN_BM(MOTOR_FAULT_PIN); + + FD_ZERO(&readFDs); +} + + +void emu_callback() { + fflush(stdout); + + if (RST.CTRL == RST_SWRST_bm) exit(0); + + struct timeval t = {0, fast ? 0 : 1000}; + bool readData = true; + while (readData) { + readData = false; + + // Try to read + FD_SET(0, &readFDs); + if (fcntl(3, F_GETFL) != -1) FD_SET(3, &readFDs); + + if (0 < select(4, &readFDs, 0, 0, &t)) { + uint8_t data; + + if (serialByte == -1 && FD_ISSET(0, &readFDs) && read(0, &data, 1) == 1) + serialByte = data; + + if (!haveI2C && FD_ISSET(3, &readFDs) && read(3, &data, 1) == 1) { + if (data == '\n') haveI2C = true; + else if (i2cIndex < I2C_MAX_DATA) i2cData[i2cIndex++] = data; + } + } + + // Send message to i2c port + if (haveI2C && (I2C_DEV.SLAVE.CTRLA & TWI_SLAVE_INTLVL_LO_gc)) { + // START + I2C_DEV.SLAVE.STATUS = TWI_SLAVE_APIF_bm | TWI_SLAVE_AP_bm; + __I2C_ISR(); + + // DATA + for (int i = 0; i < i2cIndex; i++) { + I2C_DEV.SLAVE.STATUS = TWI_SLAVE_DIF_bm; + I2C_DEV.SLAVE.DATA = i2cData[i]; + __I2C_ISR(); + } + + // STOP + I2C_DEV.SLAVE.STATUS = TWI_SLAVE_APIF_bm; + __I2C_ISR(); + + i2cIndex = 0; + haveI2C = false; + readData = true; + } + + // Send byte to serial port + if (serialByte != -1 && SERIAL_PORT.CTRLA & USART_RXCINTLVL_MED_gc) { + SERIAL_PORT.DATA = (uint8_t)serialByte; + __SERIAL_RXC_vect(); + + if (SERIAL_PORT.CTRLA & USART_RXCINTLVL_MED_gc) { + serialByte = -1; + readData = true; + } + } + } + + // Call stepper ISRs + if (ADCB_CH0_INTCTRL == ADC_CH_INTLVL_LO_gc) __STEP_LOW_LEVEL_ISR(); + for (int motor = 0; motor < 4; motor++) motor_emulate_steps(motor); + __STEP_TIMER_ISR(); + + // Call RTC + __RTC_OVF_vect(); + + // Throttle with remaining time + if (t.tv_usec) usleep(t.tv_usec); +} diff --git a/src/avr/emu/src/util/atomic.h b/src/avr/emu/src/util/atomic.h new file mode 100644 index 0000000..86dab20 --- /dev/null +++ b/src/avr/emu/src/util/atomic.h @@ -0,0 +1,31 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#define ATOMIC_BLOCK(x) +#define ATOMIC_RESTORESTATE diff --git a/src/avr/emu/src/util/crc16.h b/src/avr/emu/src/util/crc16.h new file mode 100644 index 0000000..e343874 --- /dev/null +++ b/src/avr/emu/src/util/crc16.h @@ -0,0 +1,30 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#define _crc16_update(...) 0 diff --git a/src/avr/emu/src/util/delay.h b/src/avr/emu/src/util/delay.h new file mode 100644 index 0000000..e67c76a --- /dev/null +++ b/src/avr/emu/src/util/delay.h @@ -0,0 +1,33 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include + +#define _delay_ms(x) usleep((x) * 1000) +#define _delay_us(x) usleep(x) diff --git a/src/avr/src/SCurve.cpp b/src/avr/src/SCurve.cpp new file mode 100644 index 0000000..73bbe30 --- /dev/null +++ b/src/avr/src/SCurve.cpp @@ -0,0 +1,175 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + + +#include "SCurve.h" + +#include + + +SCurve::SCurve(float maxV, float maxA, float maxJ) : + maxV(maxV), maxA(maxA), maxJ(maxJ), v(0), a(0), j(0) {} + + +unsigned SCurve::getPhase() const { + if (!v) return 0; + + // Handle negative velocity + float v = this->v; + float a = this->a; + if (v < 0) { + v = -v; + a = -a; + } + + if (0 < a) { + if (0 < j) return 1; + if (!j) return 2; + return 3; + } + + if (!a) return 4; + if (j < 0) return 5; + if (!j) return 6; + + return 7; +} + + +float SCurve::getStoppingDist() const {return stoppingDist(v, a, maxA, maxJ);} + + +float SCurve::next(float t, float targetV) { + // Compute next acceleration + float nextA = nextAccel(t, targetV, v, a, maxA, maxJ); + + // Compute next velocity + float deltaV = nextA * t; + if ((deltaV < 0 && targetV < v && v + deltaV < targetV) || + (0 < deltaV && v < targetV && targetV < v + deltaV)) { + nextA = (targetV - v) / t; + v = targetV; + + } else v += deltaV; + + // Compute jerk = delta accel / time + j = (nextA - a) / t; + a = nextA; + + return v; +} + + +float SCurve::stoppingDist(float v, float a, float maxA, float maxJ) { + // Already stopped + if (!v) return 0; + + // Handle negative velocity + if (v < 0) { + v = -v; + a = -a; + } + + float d = 0; + + // Compute distance and velocity change to accel = 0 + if (0 < a) { + // Compute distance to decrease accel to zero + float t = a / maxJ; + d += distance(t, v, a, -maxJ); + v += velocity(t, a, -maxJ); + a = 0; + } + + // Compute max deccel + float maxDeccel = -sqrt(v * maxJ + 0.5 * a * a); + if (maxDeccel < -maxA) maxDeccel = -maxA; + + // Compute distance and velocity change to max deccel + if (maxDeccel < a) { + float t = (a - maxDeccel) / maxJ; + d += distance(t, v, a, -maxJ); + v += velocity(t, a, -maxJ); + a = maxDeccel; + } + + // Compute velocity change over remaining accel + float deltaV = 0.5 * a * a / maxJ; + + // Compute constant deccel period + if (deltaV < v) { + float t = (v - deltaV) / -a; + d += distance(t, v, a, 0); + v += velocity(t, a, 0); + } + + // Compute distance to zero vel + d += distance(-a / maxJ, v, a, maxJ); + + return d; +} + + +float SCurve::nextAccel(float t, float targetV, float v, float a, float maxA, + float maxJ) { + bool increasing = v < targetV; + float deltaA = acceleration(t, maxJ); + + if (increasing && a < -deltaA) + return a + deltaA; // negative accel, increasing speed + + if (!increasing && deltaA < a) + return a - deltaA; // positive accel, decreasing speed + + float deltaV = fabs(targetV - v); + float targetA = sqrt(2 * deltaV * maxJ); + if (maxA < targetA) targetA = maxA; + + if (increasing) { + if (targetA < a + deltaA) return targetA; + return a + deltaA; + + } else { + if (a - deltaA < -targetA) return -targetA; + return a - deltaA; + } +} + + +float SCurve::distance(float t, float v, float a, float j) { + // v * t + 1/2 * a * t^2 + 1/6 * j * t^3 + return t * (v + t * (0.5 * a + 1.0 / 6.0 * j * t)); +} + + +float SCurve::velocity(float t, float a, float j) { + // a * t + 1/2 * j * t^2 + return t * (a + 0.5 * j * t); +} + + +float SCurve::acceleration(float t, float j) {return j * t;} diff --git a/src/avr/src/SCurve.h b/src/avr/src/SCurve.h new file mode 100644 index 0000000..d48786a --- /dev/null +++ b/src/avr/src/SCurve.h @@ -0,0 +1,66 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include + + +class SCurve { + float maxV; + float maxA; + float maxJ; + + float v; + float a; + float j; + +public: + SCurve(float maxV = 0, float maxA = 0, float maxJ = 0); + + float getMaxVelocity() const {return maxV;} + void setMaxVelocity(float v) {maxV = v;} + float getMaxAcceleration() const {return maxA;} + void setMaxAcceleration(float a) {maxA = a;} + float getMaxJerk() const {return maxJ;} + void setMaxJerk(float j) {maxJ = j;} + + float getVelocity() const {return v;} + float getAcceleration() const {return a;} + float getJerk() const {return j;} + + unsigned getPhase() const; + float getStoppingDist() const; + float next(float t, float targetV); + + static float stoppingDist(float v, float a, float maxA, float maxJ); + static float nextAccel(float t, float targetV, float v, float a, float maxA, + float maxJ); + static float distance(float t, float v, float a, float j); + static float velocity(float t, float a, float j); + static float acceleration(float t, float j); +}; diff --git a/src/avr/src/analog.c b/src/avr/src/analog.c new file mode 100644 index 0000000..918f4fa --- /dev/null +++ b/src/avr/src/analog.c @@ -0,0 +1,90 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "analog.h" + +#include "config.h" + +#include + +#include + + +typedef struct { + uint8_t pin; + uint16_t value; +} analog_port_t; + + +analog_port_t ports[] = { + {.pin = ANALOG_1_PIN}, + {.pin = ANALOG_2_PIN}, +}; + + +ISR(ADCA_CH0_vect) {ports[0].value = ADCA.CH0.RES;} +ISR(ADCA_CH1_vect) {ports[1].value = ADCA.CH1.RES;} + + +void analog_init() { + // Channel 0 + ADCA.CH0.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc; + ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN6_gc; + ADCA.CH0.INTCTRL = ADC_CH_INTLVL_LO_gc; + + // Channel 1 + ADCA.CH1.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc; + ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN7_gc; + ADCA.CH1.INTCTRL = ADC_CH_INTLVL_LO_gc; + + // ADC + ADCA.REFCTRL = ADC_REFSEL_INTVCC_gc; // 3.3V / 1.6 = 2.06V + ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc; + ADCA.EVCTRL = ADC_SWEEP_01_gc; + ADCA.CTRLA = ADC_FLUSH_bm | ADC_ENABLE_bm; +} + + +float analog_get(unsigned port) { + if (1 < port) return 0; + return ports[port].value * (1.0 / 0x1000); +} + + +void analog_rtc_callback() { + static uint8_t count = 0; + + // Every 1/4 sec + if (++count == 250) { + count = 0; + ADCA.CTRLA |= ADC_CH0START_bm | ADC_CH1START_bm; + } +} + + +// Var callbacks +float get_analog_input(int port) {return analog_get(port);} diff --git a/src/avr/src/analog.h b/src/avr/src/analog.h new file mode 100644 index 0000000..6aaf592 --- /dev/null +++ b/src/avr/src/analog.h @@ -0,0 +1,33 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +void analog_init(); +float analog_get(unsigned port); +void analog_rtc_callback(); diff --git a/src/avr/src/axis.c b/src/avr/src/axis.c new file mode 100644 index 0000000..1f891f5 --- /dev/null +++ b/src/avr/src/axis.c @@ -0,0 +1,124 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "axis.h" +#include "motor.h" +#include "switch.h" +#include "util.h" + +#include +#include +#include + + +int motor_map[AXES] = {-1, -1, -1, -1, -1, -1}; + + +typedef struct { + float velocity_max; // max velocity in mm/min or deg/min + float accel_max; // max acceleration in mm/min^2 + float jerk_max; // max jerk (Jm) in km/min^3 +} axis_t; + + +axis_t axes[MOTORS] = {}; + + +bool axis_is_enabled(int axis) { + int motor = axis_get_motor(axis); + return motor != -1 && motor_is_enabled(motor) && + !fp_ZERO(axis_get_velocity_max(axis)); +} + + +int axis_get_id(char axis) { + const char *axes = "XYZABCUVW"; + const char *ptr = strchr(axes, toupper(axis)); + return ptr == 0 ? -1 : (ptr - axes); +} + + +int axis_get_motor(int axis) {return motor_map[axis];} + + +bool axis_get_homed(int axis) { + return axis_is_enabled(axis) ? motor_get_homed(axis_get_motor(axis)) : false; +} + + +float axis_get_soft_limit(int axis, bool min) { + if (!axis_is_enabled(axis)) return min ? -INFINITY : INFINITY; + return motor_get_soft_limit(axis_get_motor(axis), min); +} + + +// Map axes to first matching motor +void axis_map_motors() { + for (int axis = 0; axis < AXES; axis++) + for (int motor = 0; motor < MOTORS; motor++) + if (motor_get_axis(motor) == axis) { + motor_map[axis] = motor; + break; + } +} + + +#define AXIS_VAR_GET(NAME, TYPE) \ + TYPE get_##NAME(int axis) {return axes[axis].NAME;} + + +#define AXIS_VAR_SET(NAME, TYPE) \ + void set_##NAME(int axis, TYPE value) {axes[axis].NAME = value;} + + +/// Velocity is scaled by 1,000 +float axis_get_velocity_max(int axis) { + int motor = axis_get_motor(axis); + return motor == -1 ? 0 : axes[motor].velocity_max * VELOCITY_MULTIPLIER; +} +AXIS_VAR_GET(velocity_max, float) + + +/// Acceleration is scaled by 1,000 +float axis_get_accel_max(int axis) { + int motor = axis_get_motor(axis); + return motor == -1 ? 0 : axes[motor].accel_max * ACCEL_MULTIPLIER; +} +AXIS_VAR_GET(accel_max, float) + + +/// Jerk is scaled by 1,000,000 +float axis_get_jerk_max(int axis) { + int motor = axis_get_motor(axis); + return motor == -1 ? 0 : axes[motor].jerk_max * JERK_MULTIPLIER; +} +AXIS_VAR_GET(jerk_max, float) + + +AXIS_VAR_SET(velocity_max, float) +AXIS_VAR_SET(accel_max, float) +AXIS_VAR_SET(jerk_max, float) diff --git a/src/avr/src/axis.h b/src/avr/src/axis.h new file mode 100644 index 0000000..50aae9b --- /dev/null +++ b/src/avr/src/axis.h @@ -0,0 +1,50 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "config.h" + +#include + + +enum { + AXIS_X, AXIS_Y, AXIS_Z, + AXIS_A, AXIS_B, AXIS_C, +}; + + +bool axis_is_enabled(int axis); +int axis_get_id(char axis); +int axis_get_motor(int axis); +bool axis_get_homed(int axis); +float axis_get_soft_limit(int axis, bool min); +void axis_map_motors(); + +float axis_get_velocity_max(int axis); +float axis_get_accel_max(int axis); +float axis_get_jerk_max(int axis); diff --git a/src/avr/src/base64.c b/src/avr/src/base64.c new file mode 100644 index 0000000..e6bbe99 --- /dev/null +++ b/src/avr/src/base64.c @@ -0,0 +1,151 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "base64.h" + +#include "util.h" + +#include + + +static const char *_b64_encode = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + +static const int8_t _b64_decode[] = { // 43-122 + 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 +}; + + +static int8_t _decode(char c) { + return (c < 43 || 122 < c) ? -1 : _b64_decode[c - 43]; +} + + +static char _encode(uint8_t b) {return _b64_encode[b & 63];} + + +static void _skip_space(const char **s, const char *end) { + while (*s < end && isspace(**s)) (*s)++; +} + + +static char _next(const char **s, const char *end) { + char c = *(*s)++; + _skip_space(s, end); + return c; +} + + +unsigned b64_encoded_length(unsigned len, bool pad) { + unsigned elen = len / 3 * 4; + + switch (len % 3) { + case 1: elen += pad ? 4 : 2; break; + case 2: elen += pad ? 4 : 3; break; + } + + return elen; +} + + +void b64_encode(const uint8_t *in, unsigned len, char *out, bool pad) { + const uint8_t *end = in + len; + int padding = 0; + uint8_t a, b, c; + + while (in < end) { + a = *in++; + + if (in < end) { + b = *in++; + + if (in < end) c = *in++; + else {c = 0; padding = 1;} + + } else {c = b = 0; padding = 2;} + + *out++ = _encode(63 & (a >> 2)); + *out++ = _encode(63 & (a << 4 | b >> 4)); + + if (pad && padding == 2) *out++ = '='; + else *out++ = _encode(63 & (b << 2 | c >> 6)); + + if (pad && padding) *out++ = '='; + else *out++ = _encode(63 & c); + } +} + + +bool b64_decode(const char *in, unsigned len, uint8_t *out) { + const char *end = in + len; + + _skip_space(&in, end); + + while (in < end) { + int8_t w = _decode(_next(&in, end)); + int8_t x = in < end ? _decode(_next(&in, end)) : -2; + int8_t y = in < end ? _decode(_next(&in, end)) : -2; + int8_t z = in < end ? _decode(_next(&in, end)) : -2; + + if (w == -2 || x == -2 || w == -1 || x == -1 || y == -1 || z == -1) + return false; + + *out++ = (uint8_t)(w << 2 | x >> 4); + if (y != -2) { + *out++ = (uint8_t)(x << 4 | y >> 2); + if (z != -2) *out++ = (uint8_t)(y << 6 | z); + } + } + + return true; +} + + +bool b64_decode_float(const char *s, float *f) { + union { + float f; + uint8_t b[4]; + } u; + + if (!b64_decode(s, 6, u.b)) return false; + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define XORSWAP(a, b) a ^= (b ^ (b ^= a) + XORSWAP(u.b[0], u.b[3]); + XORSWAP(u.b[1], u.b[2]); +#endif + + *f = u.f; + + return true; +} diff --git a/src/avr/src/base64.h b/src/avr/src/base64.h new file mode 100644 index 0000000..ed6e6a6 --- /dev/null +++ b/src/avr/src/base64.h @@ -0,0 +1,37 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include +#include + + +unsigned b64_encoded_length(unsigned len, bool pad); +void b64_encode(const uint8_t *in, unsigned len, char *out, bool pad); +bool b64_decode(const char *in, unsigned len, uint8_t *out); +bool b64_decode_float(const char *s, float *f); diff --git a/src/avr/src/command.c b/src/avr/src/command.c new file mode 100644 index 0000000..916c388 --- /dev/null +++ b/src/avr/src/command.c @@ -0,0 +1,278 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "command.h" + +#include "usart.h" +#include "hardware.h" +#include "vars.h" +#include "estop.h" +#include "i2c.h" +#include "config.h" +#include "pgmspace.h" +#include "state.h" +#include "exec.h" +#include "base64.h" +#include "rtc.h" +#include "stepper.h" +#include "cpp_magic.h" + +#include + +#include +#include +#include +#include + + +#define RING_BUF_NAME sync_q +#define RING_BUF_TYPE uint8_t +#define RING_BUF_INDEX_TYPE volatile uint16_t +#define RING_BUF_SIZE SYNC_QUEUE_SIZE +#define RING_BUF_ATOMIC_COPY 1 +#include "ringbuf.def" + + +static struct { + bool active; + uint16_t id; + uint32_t last_empty; + volatile uint16_t count; + float position[AXES]; +} cmd = {0,}; + + +// Define command callbacks +#define CMD(CODE, NAME, SYNC) \ + stat_t command_##NAME(char *); \ + IF(SYNC)(unsigned command_##NAME##_size();) \ + IF(SYNC)(void command_##NAME##_exec(void *);) +#include "command.def" +#undef CMD + + +// Name +#define CMD(CODE, NAME, SYNC) \ + static const char command_##NAME##_name[] PROGMEM = #NAME; +#include "command.def" +#undef CMD + + +static bool _is_synchronous(char code) { + switch (code) { +#define CMD(CODE, NAME, SYNC, ...) case COMMAND_##NAME: return SYNC; +#include "command.def" +#undef CMD + } + return false; +} + + +static stat_t _dispatch(char *s) { + switch (*s) { +#define CMD(CODE, NAME, SYNC, ...) \ + case COMMAND_##NAME: return command_##NAME(s); +#include "command.def" +#undef CMD + } + + return STAT_INVALID_COMMAND; +} + + +static unsigned _size(char code) { + switch (code) { +#define CMD(CODE, NAME, SYNC, ...) \ + IF(SYNC)(case COMMAND_##NAME: return command_##NAME##_size();) +#include "command.def" +#undef CMD + } + + return 0; +} + + +static void _exec_cb(char code, uint8_t *data) { + switch (code) { +#define CMD(CODE, NAME, SYNC, ...) \ + IF(SYNC)(case COMMAND_##NAME: command_##NAME##_exec(data); break;) +#include "command.def" +#undef CMD + } +} + + +static void _i2c_cb(uint8_t *data, uint8_t length) { + stat_t status = _dispatch((char *)data); + if (status) STATUS_ERROR(status, "i2c: %s", data); +} + + +void command_init() {i2c_set_read_callback(_i2c_cb);} +bool command_is_active() {return cmd.active;} +unsigned command_get_count() {return cmd.count;} + + +void command_print_json() { + bool first = true; + static const char fmt[] PROGMEM = "\"%c\":{\"name\":\"%" PRPSTR "\"}"; + +#define CMD(CODE, NAME, SYNC) \ + if (first) first = false; else putchar(','); \ + printf_P(fmt, CODE, command_##NAME##_name); + +#include "command.def" +#undef CMD +} + + +void command_flush_queue() { + sync_q_init(); + cmd.count = 0; + command_reset_position(); +} + + +void command_push(char code, void *_data) { + uint8_t *data = (uint8_t *)_data; + unsigned size = _size(code); + + if (!_is_synchronous(code)) estop_trigger(STAT_Q_INVALID_PUSH); + if (sync_q_space() <= size) estop_trigger(STAT_Q_OVERRUN); + + sync_q_push(code); + for (unsigned i = 0; i < size; i++) sync_q_push(*data++); + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) cmd.count++; +} + + +bool command_callback() { + static char *block = 0; + + if (!block) block = usart_readline(); + if (!block) return false; // No command + + stat_t status = STAT_OK; + + // Special processing for synchronous commands + if (_is_synchronous(*block)) { + if (estop_triggered()) status = STAT_MACHINE_ALARMED; + else if (state_is_flushing()) status = STAT_NOP; // Flush command + else if (state_is_resuming() || sync_q_space() <= _size(*block)) + return false; // Wait + } + + // Dispatch non-empty commands + if (*block && status == STAT_OK) { + status = _dispatch(block); + if (status == STAT_OK) cmd.active = true; // Disables LCD booting message + } + + switch (status) { + case STAT_OK: break; + case STAT_NOP: break; + case STAT_MACHINE_ALARMED: STATUS_WARNING(status, ""); break; + default: STATUS_ERROR(status, "%s", block); break; + } + + block = 0; // Command consumed + + return true; +} + + +void command_set_axis_position(int axis, const float p) { + cmd.position[axis] = p; +} + + +void command_set_position(const float position[AXES]) { + memcpy(cmd.position, position, sizeof(cmd.position)); +} + + +void command_get_position(float position[AXES]) { + memcpy(position, cmd.position, sizeof(cmd.position)); +} + + +void command_reset_position() { + float position[AXES]; + exec_get_position(position); + command_set_position(position); +} + + +char command_peek() {return (char)(cmd.count ? sync_q_peek() : 0);} + + +uint8_t *command_next() { + if (!cmd.count) return 0; + cmd.count--; + + if (sync_q_empty()) estop_trigger(STAT_Q_UNDERRUN); + + static uint8_t data[INPUT_BUFFER_LEN]; + + data[0] = sync_q_next(); + + if (!_is_synchronous((char)data[0])) estop_trigger(STAT_INVALID_QCMD); + + unsigned size = _size((char)data[0]); + for (unsigned i = 0; i < size; i++) + data[i + 1] = sync_q_next(); + + return data; +} + + +// Returns true if command queued +// Called by exec.c from low-level interrupt +bool command_exec() { + if (!cmd.count) { + cmd.last_empty = rtc_get_time(); + state_idle(); + return false; + } + + // On restart wait a bit to give queue a chance to fill + if (cmd.count < EXEC_FILL_TARGET && + !rtc_expired(cmd.last_empty + EXEC_DELAY)) return false; + + uint8_t *data = command_next(); + state_running(); + + _exec_cb((char)*data, data + 1); + + return true; +} + + +// Var callbacks +uint16_t get_id() {return cmd.id;} +void set_id(uint16_t id) {cmd.id = id;} diff --git a/src/avr/src/command.def b/src/avr/src/command.def new file mode 100644 index 0000000..c413a90 --- /dev/null +++ b/src/avr/src/command.def @@ -0,0 +1,50 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +//(CODE, NAME, SYNC) +CMD('$', var, 0) // Set or get variable +CMD('#', sync_var, 1) // Set variable synchronous +CMD('s', seek, 1) // [switch][flags:active|error] +CMD('a', set_axis, 1) // [axis][position] Set axis position +CMD('l', line, 1) // [targetVel][maxJerk][axes][times] +CMD('%', sync_speed, 1) // [offset][speed] Command synchronized speed +CMD('p', speed, 1) // [speed] Spindle speed +CMD('I', input, 1) // [a|d][port][mode][timeout] Read input +CMD('d', dwell, 1) // [seconds] +CMD('P', pause, 1) // [type] Pause control +CMD('S', stop, 0) // Stop move, spindle and load outputs +CMD('U', unpause, 0) // Unpause +CMD('j', jog, 0) // [axes] +CMD('r', report, 0) // <0|1>[var] Enable or disable var reporting +CMD('R', reboot, 0) // Reboot the controller +CMD('c', resume, 0) // Continue processing after a flush +CMD('E', estop, 0) // Emergency stop +CMD('X', shutdown, 0) // Power shutdown +CMD('C', clear, 0) // Clear estop +CMD('F', flush, 0) // Flush command queue +CMD('D', dump, 0) // Report all variables +CMD('h', help, 0) // Print this help screen diff --git a/src/avr/src/command.h b/src/avr/src/command.h new file mode 100644 index 0000000..24fa928 --- /dev/null +++ b/src/avr/src/command.h @@ -0,0 +1,58 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "config.h" +#include "status.h" + +#include +#include + + +// Commands +typedef enum { +#define CMD(CODE, NAME, ...) COMMAND_##NAME = CODE, +#include "command.def" +#undef CMD +} command_t; + + +void command_init(); +bool command_is_active(); +unsigned command_get_count(); +void command_print_json(); +void command_flush_queue(); +void command_push(char code, void *data); +bool command_callback(); +void command_set_axis_position(int axis, const float p); +void command_set_position(const float position[AXES]); +void command_get_position(float position[AXES]); +void command_reset_position(); +char command_peek(); +uint8_t *command_next(); +bool command_exec(); diff --git a/src/avr/src/command.json.in b/src/avr/src/command.json.in new file mode 100644 index 0000000..9e51ea9 --- /dev/null +++ b/src/avr/src/command.json.in @@ -0,0 +1,11 @@ +#include "cpp_magic.h" +{ +#define CMD(CODE, NAME, SYNC) \ + #NAME: { \ + "code": CODE, \ + "sync": IF_ELSE(SYNC)(true, false) \ + }, +#include "command.def" +#undef CMD + "_": {} +} diff --git a/src/avr/src/commands.c b/src/avr/src/commands.c new file mode 100644 index 0000000..8796b09 --- /dev/null +++ b/src/avr/src/commands.c @@ -0,0 +1,83 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "config.h" +#include "stepper.h" +#include "command.h" +#include "vars.h" +#include "base64.h" +#include "hardware.h" +#include "report.h" +#include "exec.h" + +#include + + +stat_t command_dwell(char *cmd) { + float seconds; + if (!b64_decode_float(cmd + 1, &seconds)) return STAT_BAD_FLOAT; + command_push(*cmd, &seconds); + return STAT_OK; +} + + +static stat_t _dwell_exec() { + exec_set_cb(0); // Immediately clear the callback + return STAT_OK; +} + + +unsigned command_dwell_size() {return sizeof(float);} + + +void command_dwell_exec(void *seconds) { + st_prep_dwell(*(float *)seconds); + exec_set_cb(_dwell_exec); // Command must set an exec callback +} + + +stat_t command_help(char *cmd) { + printf_P(PSTR("\n{\"commands\":{")); + command_print_json(); + printf_P(PSTR("},\"variables\":{")); + vars_print_json(); + printf_P(PSTR("}}\n")); + + return STAT_OK; +} + + +stat_t command_reboot(char *cmd) { + hw_request_hard_reset(); + return STAT_OK; +} + + +stat_t command_dump(char *cmd) { + report_request_full(); + return STAT_OK; +} diff --git a/src/avr/src/config.h b/src/avr/src/config.h new file mode 100644 index 0000000..7b01aac --- /dev/null +++ b/src/avr/src/config.h @@ -0,0 +1,222 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "pins.h" + +#include + + +// Pins +enum { + STALL_0_PIN = PIN_ID(PORT_A, 0), + STALL_1_PIN, + STALL_2_PIN, + STALL_3_PIN, + TOOL_DIR_PIN, + TOOL_ENABLE_PIN, + ANALOG_1_PIN, + ANALOG_2_PIN, + + MIN_0_PIN = PIN_ID(PORT_B, 0), + MAX_0_PIN, + MIN_3_PIN, + MAX_3_PIN, + MIN_1_PIN, + MAX_1_PIN, + MIN_2_PIN, + MAX_2_PIN, + + SDA_PIN = PIN_ID(PORT_C, 0), + SCL_PIN, + SERIAL_RX_PIN, + SERIAL_TX_PIN, + SERIAL_CTS_PIN, + SPI_CLK_PIN, + SPI_MISO_PIN, + SPI_MOSI_PIN, + + STEP_0_PIN = PIN_ID(PORT_D, 0), + SPI_CS_0_PIN, + SPI_CS_3_PIN, + SPI_CS_2_PIN, + PWM_PIN, + SWITCH_2_PIN, + RS485_RO_PIN, + RS485_DI_PIN, + + STEP_1_PIN = PIN_ID(PORT_E, 0), + SPI_CS_1_PIN, + DIR_0_PIN, + DIR_1_PIN, + STEP_3_PIN, + SWITCH_1_PIN, + DIR_2_PIN, + DIR_3_PIN, + + STEP_2_PIN = PIN_ID(PORT_F, 0), + RS485_RW_PIN, + FAULT_PIN, + ESTOP_PIN, + MOTOR_FAULT_PIN, + MOTOR_ENABLE_PIN, + TEST_PIN, + PROBE_PIN, +}; + +#define SPI_SS_PIN SERIAL_CTS_PIN // Needed for SPI configuration + + +#define AXES 6 // number of axes +#define MOTORS 4 // number of motors on the board +#define OUTS 6 // number of supported pin outputs +#define ANALOG 2 // number of supported analog inputs +#define VFDREG 32 // number of supported VFD modbus registers + +// Switch settings. See switch.c +#define SWITCH_DEBOUNCE 5 // ms, default value +#define SWITCH_LOCKOUT 250 // ms, default value +#define SWITCH_MAX_DEBOUNCE 5000 // ms +#define SWITCH_MAX_LOCKOUT 60000 // ms + + +// Motor ISRs +#define STALL_ISR_vect PORTA_INT1_vect +#define FAULT_ISR_vect PORTF_INT1_vect + + +/* Interrupt usage: + * + * HI Step timers stepper.c + * HI Serial RX usart.c + * MED Serial TX usart.c (* see note) + * MED Modbus serial interrupts modbus.c + * LO Segment execution SW interrupt stepper.c + * LO I2C Slave i2c.c + * LO Real-time clock interrupt rtc.c + * LO DRV8711 SPI drv8711.c + * LO A2D interrupts analog.c + * + * (*) The TX cannot run at LO level or exception reports and other prints + * called from a LO interrupt (as in prep_line()) will kill the system + * in a permanent loop call in usart_putc() (usart.c). + */ + +// Timer assignments +// NOTE, TCC1 is unused +#define TIMER_STEP TCC0 // Step timer (see stepper.h) +#define TIMER_PWM TCD1 // PWM timer (see pwm.c) + + +// Timer setup for stepper and dwells +#define STEP_TIMER_DIV 8 +#define STEP_TIMER_FREQ (F_CPU / STEP_TIMER_DIV) +#define STEP_TIMER_POLL ((uint16_t)(STEP_TIMER_FREQ * 0.001)) // 1ms +#define STEP_TIMER_ISR TCC0_OVF_vect +#define STEP_LOW_LEVEL_ISR ADCB_CH0_vect +#define STEP_PULSE_WIDTH (F_CPU * 0.000002) // 2uS w/ clk/1 +#define SEGMENT_MS 4 +#define SEGMENT_TIME (SEGMENT_MS / 60000.0) // mins + + +// DRV8711 settings +// NOTE, PWM frequency = 1 / (2 * DTIME + TBLANK + TOFF) +// We have PWM frequency = 1 / (2 * 850nS + 1uS + 6.5uS) ~= 110kHz +#define DRV8711_OFF 12 +#define DRV8711_BLANK (0x32 | DRV8711_BLANK_ABT_bm) +#define DRV8711_DECAY (DRV8711_DECAY_DECMOD_MIXED | 16) + +#define DRV8711_DRIVE (DRV8711_DRIVE_IDRIVEP_50 | \ + DRV8711_DRIVE_IDRIVEN_100 | \ + DRV8711_DRIVE_TDRIVEP_500 | \ + DRV8711_DRIVE_TDRIVEN_500 | \ + DRV8711_DRIVE_OCPDEG_1 | \ + DRV8711_DRIVE_OCPTH_500) +#define DRV8711_TORQUE DRV8711_TORQUE_SMPLTH_200 +// NOTE, Datasheet suggests 850ns DTIME with the optional gate resistor +// installed. See page 30 section 8.1.2 of DRV8711 datasheet. +#define DRV8711_CTRL (DRV8711_CTRL_ISGAIN_5 | \ + DRV8711_CTRL_DTIME_850) + + +// RS485 settings +#define RS485_PORT USARTD1 +#define RS485_DRE_vect USARTD1_DRE_vect +#define RS485_TXC_vect USARTD1_TXC_vect +#define RS485_RXC_vect USARTD1_RXC_vect + + +// Modbus settings +#define MODBUS_TIMEOUT 100 // ms. response timeout +#define MODBUS_RETRIES 4 // Number of retries before failure +#define MODBUS_BUF_SIZE 18 // Max bytes in rx/tx buffers +#define VFD_QUERY_DELAY 100 // ms + + +// Serial settings +#define SERIAL_BAUD USART_BAUD_230400 // 115200 +#define SERIAL_PORT USARTC0 +#define SERIAL_DRE_vect USARTC0_DRE_vect +#define SERIAL_RXC_vect USARTC0_RXC_vect +#define SERIAL_CTS_THRESH 4 + + +// PWM settings +#define POWER_MAX_UPDATES SEGMENT_MS + +// Input +#define INPUT_BUFFER_LEN 128 // text buffer size (255 max) + + +// Report +#define REPORT_RATE 250 // ms + + +// I2C +#define I2C_DEV TWIC +#define I2C_ISR TWIC_TWIS_vect +#define I2C_ADDR 0x2b +#define I2C_MAX_DATA 8 + + +// Motor +#define MOTOR_IDLE_TIMEOUT 0.25 // secs, motor off after this time +#define MIN_STEP_CORRECTION 2 + +#define MIN_VELOCITY 10 // mm/min +#define CURRENT_SENSE_RESISTOR 0.05 // ohms +#define CURRENT_SENSE_REF 2.75 // volts +#define MAX_CURRENT 6 // amps +#define MAX_IDLE_CURRENT 2 // amps +#define VELOCITY_MULTIPLIER 1000.0 +#define ACCEL_MULTIPLIER 1000000.0 +#define JERK_MULTIPLIER 1000000.0 +#define SYNC_QUEUE_SIZE 4096 +#define EXEC_FILL_TARGET 8 +#define EXEC_DELAY 250 // ms +#define JOG_STOPPING_UNDERSHOOT 1 // % of stopping distance diff --git a/src/avr/src/cpp_magic.h b/src/avr/src/cpp_magic.h new file mode 100644 index 0000000..f677c1a --- /dev/null +++ b/src/avr/src/cpp_magic.h @@ -0,0 +1,507 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +/* This header file contains a library of advanced C Pre-Processor (CPP) macros + * which implement various useful functions, such as iteration, in the + * pre-processor. + * + * Though the file name (quite validly) labels this as magic, there should be + * enough documentation in the comments for a reader only casually familiar + * with the CPP to be able to understand how everything works. + * + * The majority of the magic tricks used in this file are based on those + * described by pfultz2 in his "Cloak" library: + * + * https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms + * + * Major differences are a greater level of detailed explanation in this + * implementation and also a refusal to include any macros which require a O(N) + * macro definitions to handle O(N) arguments (with the exception of DEFERn). + */ + +#pragma once + +/** + * Force the pre-processor to expand the macro a large number of times. + * Usage: + * + * EVAL(expression) + * + * This is useful when you have a macro which evaluates to a valid + * macro expression which is not subsequently expanded in the same + * pass. A contrived, but easy to understand, example of such a macro + * follows. Note that though this example is contrived, this behaviour + * is abused to implement bounded recursion in macros such as FOR. + * + * #define A(x) x+1 + * #define EMPTY + * #define NOT_QUITE_RIGHT(x) A EMPTY (x) + * NOT_QUITE_RIGHT(999) + * + * Here's what happens inside the C preprocessor: + * + * 1. It sees a macro "NOT_QUITE_RIGHT" and performs a single macro + * expansion pass on its arguments. Since the argument is "999" and + * this isn't a macro, this is a boring step resulting in no + * change. + * + * 2. The NOT_QUITE_RIGHT macro is substituted for its definition + * giving "A EMPTY() (x)". + * + * 3. The expander moves from left-to-right trying to expand the + * macro: The first token, A, cannot be expanded since there are no + * brackets immediately following it. The second token EMPTY(), + * however, can be expanded (recursively in this manner) and is + * replaced with "". + * + * 4. Expansion continues from the start of the substituted test + * (which in this case is just empty), and sees "(999)" but since + * no macro name is present, nothing is done. This results in a + * final expansion of "A (999)". + * + * Unfortunately, this doesn't quite meet expectations since you may + * expect that "A (999)" would have been expanded into + * "999+1". Unfortunately this requires a second expansion pass but + * luckily we can force the macro processor to make more passes by + * abusing the first step of macro expansion: the preprocessor expands + * arguments in their own pass. If we define a macro which does + * nothing except produce its arguments e.g.: + * + * #define PASS_THROUGH(...) __VA_ARGS__ + * + * We can now do "PASS_THROUGH(NOT_QUITE_RIGHT(999))" causing + * "NOT_QUITE_RIGHT" to be expanded to "A (999)", as described above, + * when the arguments are expanded. Now when the body of PASS_THROUGH + * is expanded, "A (999)" gets expanded to "999+1". + * + * The EVAL defined below is essentially equivalent to a large nesting + * of "PASS_THROUGH(PASS_THROUGH(PASS_THROUGH(..." which results in + * the preprocessor making a large number of expansion passes over the + * given expression. + */ +#define EVAL(...) EVAL1024(__VA_ARGS__) +#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__)) +#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__)) +#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__)) +#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__)) +#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__)) +#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__)) +#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__)) +#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__)) +#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__)) +#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__)) +#define EVAL1(...) __VA_ARGS__ + + +// Macros which expand to common values +#define PASS(...) __VA_ARGS__ +#define EMPTY() +#define COMMA() , +#define SEMI() ; +#define PLUS() + +#define ZERO() 0 +#define ONE() 1 + +/** + * Causes a function-style macro to require an additional pass to be expanded. + * + * This is useful, for example, when trying to implement recursion since the + * recursive step must not be expanded in a single pass as the pre-processor + * will catch it and prevent it. + * + * Usage: + * + * DEFER1(IN_NEXT_PASS)(args, to, the, macro) + * + * How it works: + * + * 1. When DEFER1 is expanded, first its arguments are expanded which are + * simply IN_NEXT_PASS. Since this is a function-style macro and it has no + * arguments, nothing will happen. + * 2. The body of DEFER1 will now be expanded resulting in EMPTY() being + * deleted. This results in "IN_NEXT_PASS (args, to, the macro)". Note that + * since the macro expander has already passed IN_NEXT_PASS by the time it + * expands EMPTY() and so it won't spot that the brackets which remain can be + * applied to IN_NEXT_PASS. + * 3. At this point the macro expansion completes. If one more pass is made, + * IN_NEXT_PASS(args, to, the, macro) will be expanded as desired. + */ +#define DEFER1(id) id EMPTY() + +/** + * As with DEFER1 except here n additional passes are required for DEFERn. + * + * The mechanism is analogous. + * + * Note that there doesn't appear to be a way of combining DEFERn macros in + * order to achieve exponentially increasing defers e.g. as is done by EVAL. + */ +#define DEFER2(id) id EMPTY EMPTY()() +#define DEFER3(id) id EMPTY EMPTY EMPTY()()() +#define DEFER4(id) id EMPTY EMPTY EMPTY EMPTY()()()() +#define DEFER5(id) id EMPTY EMPTY EMPTY EMPTY EMPTY()()()()() +#define DEFER6(id) id EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY()()()()()() +#define DEFER7(id) id EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY()()()()()()() +#define DEFER8(id) \ + id EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY()()()()()()()() + + +/** + * Indirection around the standard ## concatenation operator. This simply + * ensures that the arguments are expanded (once) before concatenation. + */ +#define CAT(a, ...) a ## __VA_ARGS__ +#define CAT3(a, b, ...) a ## b ## __VA_ARGS__ + + +/** + * Get the first argument and ignore the rest. + */ +#define FIRST(a, ...) a + +/** + * Get the second argument and ignore the rest. + */ +#define SECOND(a, b, ...) b + +/** + * Expects a single input (not containing commas). Returns 1 if the input is + * PROBE() and otherwise returns 0. + * + * This can be useful as the basis of a NOT function. + * + * This macro abuses the fact that PROBE() contains a comma while other valid + * inputs must not. + */ +#define IS_PROBE(...) SECOND(__VA_ARGS__, 0) +#define PROBE() ~, 1 + + +/** + * Logical negation. 0 is defined as false and everything else as true. + * + * When 0, _NOT_0 will be found which evaluates to the PROBE. When 1 + * (or any other value) is given, an appropriately named macro won't + * be found and the concatenated string will be produced. IS_PROBE + * then simply checks to see if the PROBE was returned, cleanly + * converting the argument into a 1 or 0. + */ +#define NOT(x) IS_PROBE(CAT(_NOT_, x)) +#define _NOT_0 PROBE() + +/** + * Macro version of C's famous "cast to bool" operator (i.e. !!) which takes + * anything and casts it to 0 if it is 0 and 1 otherwise. + */ +#define BOOL(x) NOT(NOT(x)) + +/** + * Logical OR. Simply performs a lookup. + */ +#define OR(a,b) CAT3(_OR_, a, b) +#define _OR_00 0 +#define _OR_01 1 +#define _OR_10 1 +#define _OR_11 1 + +/** + * Logical AND. Simply performs a lookup. + */ +#define AND(a,b) CAT3(_AND_, a, b) +#define _AND_00 0 +#define _AND_01 0 +#define _AND_10 0 +#define _AND_11 1 + + +/** + * Macro if statement. Usage: + * + * IF(c)(expansion when true) + * + * Here's how: + * + * 1. The preprocessor expands the arguments to _IF casting the condition to '0' + * or '1'. + * 2. The casted condition is concatencated with _IF_ giving _IF_0 or _IF_1. + * 3. The _IF_0 and _IF_1 macros either returns the argument or doesn't (e.g. + * they implement the "choice selection" part of the macro). + * 4. Note that the "true" clause is in the extra set of brackets; thus these + * become the arguments to _IF_0 or _IF_1 and thus a selection is made! + */ +#define IF(c) _IF(BOOL(c)) +#define _IF(c) CAT(_IF_,c) +#define _IF_0(...) +#define _IF_1(...) __VA_ARGS__ + +/** + * Macro if/else statement. Usage: + * + * IF_ELSE(c)( \ + * expansion when true, \ + * expansion when false \ + * ) + * + * The mechanism is analogous to IF. + */ +#define IF_ELSE(c) _IF_ELSE(BOOL(c)) +#define _IF_ELSE(c) CAT(_IF_ELSE_,c) +#define _IF_ELSE_0(t,f) f +#define _IF_ELSE_1(t,f) t + + +/** + * Macro which checks if it has any arguments. Returns '0' if there are no + * arguments, '1' otherwise. + * + * Limitation: HAS_ARGS(,1,2,3) returns 0 -- this check essentially only checks + * that the first argument exists. + * + * This macro works as follows: + * + * 1. _END_OF_ARGUMENTS_ is concatenated with the first argument. + * 2. If the first argument is not present then only "_END_OF_ARGUMENTS_" will + * remain, otherwise "_END_OF_ARGUMENTS something_here" will remain. + * 3. In the former case, the _END_OF_ARGUMENTS_() macro expands to a + * 0 when it is expanded. In the latter, a non-zero result remains. + * 4. BOOL is used to force non-zero results into 1 giving the clean 0 or 1 + * output required. + */ +#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)()) +#define _END_OF_ARGUMENTS_() 0 + + +/** + * Macro map/list comprehension. Usage: + * + * MAP(op, sep, ...) + * + * Produces a 'sep()'-separated list of the result of op(arg) for each arg. + * + * Example Usage: + * + * #define MAKE_HAPPY(x) happy_##x + * #define COMMA() , + * MAP(MAKE_HAPPY, COMMA, 1,2,3) + * + * Which expands to: + * + * happy_1 , happy_2 , happy_3 + * + * How it works: + * + * 1. The MAP macro simply maps the inner MAP_INNER function in an EVAL which + * forces it to be expanded a large number of times, thus enabling many steps + * of iteration (see step 6). + * 2. The MAP_INNER macro is substituted for its body. + * 3. In the body, op(cur_val) is substituted giving the value for this + * iteration. + * 4. The IF macro expands according to whether further iterations are required. + * This expansion either produces _IF_0 or _IF_1. + * 5. Since the IF is followed by a set of brackets containing the "if true" + * clause, these become the argument to _IF_0 or _IF_1. At this point, the + * macro in the brackets will be expanded giving the separator followed by + * _MAP_INNER EMPTY()()(op, sep, __VA_ARGS__). + * 5. If the IF was not taken, the above will simply be discarded and everything + * stops. If the IF is taken, The expression is then processed a second time + * yielding "_MAP_INNER()(op, sep, __VA_ARGS__)". Note that this call looks + * very similar to the essentially the same as the original call except the + * first argument has been dropped. + * 6. At this point expansion of MAP_INNER will terminate. However, since we can + * force more rounds of expansion using EVAL1. In the argument-expansion pass + * of the EVAL1, _MAP_INNER() is expanded to MAP_INNER which is then expanded + * using the arguments which follow it as in step 2-5. This is followed by a + * second expansion pass as the substitution of EVAL1() is expanded executing + * 2-5 a second time. This results in up to two iterations occurring. Using + * many nested EVAL1 macros, i.e. the very-deeply-nested EVAL macro, will in + * this manner produce further iterations, hence the outer MAP macro doing + * this for us. + * + * Important tricks used: + * + * * If we directly produce "MAP_INNER" in an expansion of MAP_INNER, + * a special case in the preprocessor will prevent it being expanded + * in the future, even if we EVAL. As a result, the MAP_INNER macro + * carefully only expands to something containing "_MAP_INNER()" + * which requires a further expansion step to invoke MAP_INNER and + * thus implementing the recursion. + * + * * To prevent _MAP_INNER being expanded within the macro we must + * first defer its expansion during its initial pass as an argument + * to _IF_0 or _IF_1. We must then defer its expansion a second time + * as part of the body of the _IF_0. As a result hence the DEFER2. + * + * * _MAP_INNER seemingly gets away with producing itself because it + * actually only produces MAP_INNER. It just happens that when + * _MAP_INNER() is expanded in this case it is followed by some + * arguments which get consumed by MAP_INNER and produce a + * _MAP_INNER. As such, the macro expander never marks _MAP_INNER + * as expanding to itself and thus it will still be expanded in + * future productions of itself. + */ +#define MAP(...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(MAP_INNER(__VA_ARGS__))) +#define MAP_INNER(op,sep,cur_val, ...) \ + op(cur_val) \ + IF(HAS_ARGS(__VA_ARGS__))( \ + sep() DEFER2(_MAP_INNER)()(op, sep, ##__VA_ARGS__) \ + ) +#define _MAP_INNER() MAP_INNER + + +/** + * This is a variant of the MAP macro which also includes as an argument to the + * operation a valid C variable name which is different for each iteration. + * + * Usage: + * MAP_WITH_ID(op, sep, ...) + * + * Where op is a macro op(val, id) which takes a list value and an ID. This ID + * will simply be a unary number using the digit "I", that is, I, II, III, IIII, + * and so on. + * + * Example: + * + * #define MAKE_STATIC_VAR(type, name) static type name; + * MAP_WITH_ID(MAKE_STATIC_VAR, EMPTY, int, int, int, bool, char) + * + * Which expands to: + * + * static int I; static int II; static int III; static bool IIII; + * static char IIIII; + * + * The mechanism is analogous to the MAP macro. + */ +#define MAP_WITH_ID(op,sep,...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(MAP_WITH_ID_INNER(op,sep,I, ##__VA_ARGS__))) +#define MAP_WITH_ID_INNER(op,sep,id,cur_val, ...) \ + op(cur_val,id) \ + IF(HAS_ARGS(__VA_ARGS__))( \ + sep() DEFER2(_MAP_WITH_ID_INNER)()(op, sep, CAT(id,I), ##__VA_ARGS__) \ + ) +#define _MAP_WITH_ID_INNER() MAP_WITH_ID_INNER + + +/** + * This is a variant of the MAP macro which iterates over pairs rather than + * singletons. + * + * Usage: + * MAP_PAIRS(op, sep, ...) + * + * Where op is a macro op(val_1, val_2) which takes two list values. + * + * Example: + * + * #define MAKE_STATIC_VAR(type, name) static type name; + * MAP_PAIRS(MAKE_STATIC_VAR, EMPTY, char, my_char, int, my_int) + * + * Which expands to: + * + * static char my_char; static int my_int; + * + * The mechanism is analogous to the MAP macro. + */ +#define MAP_PAIRS(op,sep,...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(MAP_PAIRS_INNER(op,sep,__VA_ARGS__))) +#define MAP_PAIRS_INNER(op,sep,cur_val_1, cur_val_2, ...) \ + op(cur_val_1,cur_val_2) \ + IF(HAS_ARGS(__VA_ARGS__))( \ + sep() DEFER2(_MAP_PAIRS_INNER)()(op, sep, __VA_ARGS__) \ + ) +#define _MAP_PAIRS_INNER() MAP_PAIRS_INNER + +/** + * This is a variant of the MAP macro which iterates over a two-element sliding + * window. + * + * Usage: + * MAP_SLIDE(op, last_op, sep, ...) + * + * Where op is a macro op(val_1, val_2) which takes the two list values + * currently in the window. last_op is a macro taking a single value which is + * called for the last argument. + * + * Example: + * + * #define SIMON_SAYS_OP(simon, next) IF(NOT(simon()))(next) + * #define SIMON_SAYS_LAST_OP(val) last_but_not_least_##val + * #define SIMON_SAYS() 0 + * + * MAP_SLIDE(SIMON_SAYS_OP, SIMON_SAYS_LAST_OP, EMPTY, wiggle, SIMON_SAYS, + * dance, move, SIMON_SAYS, boogie, stop) + * + * Which expands to: + * + * dance boogie last_but_not_least_stop + * + * The mechanism is analogous to the MAP macro. + */ +#define MAP_SLIDE(op,last_op,sep,...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(MAP_SLIDE_INNER(op,last_op,sep,__VA_ARGS__))) +#define MAP_SLIDE_INNER(op,last_op,sep,cur_val, ...) \ + IF(HAS_ARGS(__VA_ARGS__))(op(cur_val,FIRST(__VA_ARGS__))) \ + IF(NOT(HAS_ARGS(__VA_ARGS__)))(last_op(cur_val)) \ + IF(HAS_ARGS(__VA_ARGS__))( \ + sep() DEFER2(_MAP_SLIDE_INNER)()(op, last_op, sep, __VA_ARGS__) \ + ) +#define _MAP_SLIDE_INNER() MAP_SLIDE_INNER + + +/** + * Strip any excess commas from a set of arguments. + */ +#define REMOVE_TRAILING_COMMAS(...) \ + MAP(PASS, COMMA, __VA_ARGS__) + + +/** + * Evaluates an array of macros passing 1 argument + */ +#define EMAP1(...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(EMAP1_INNER(__VA_ARGS__))) + +#define EMAP1_INNER(ARG1, OP, ...) \ + _##OP(ARG1) \ + IF(HAS_ARGS(__VA_ARGS__)) \ + (DEFER2(_EMAP1_INNER)()(ARG1, ##__VA_ARGS__)) + +#define _EMAP1_INNER() EMAP1_INNER + + +/** + * Evaluates an array of macros passing 2 arguments + */ +#define EMAP2(...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(EMAP2_INNER(__VA_ARGS__))) + +#define EMAP2_INNER(ARG1, ARG2, OP, ...) \ + _##OP(ARG1, ARG2) \ + IF(HAS_ARGS(__VA_ARGS__)) \ + (DEFER2(_EMAP2_INNER)()(ARG1, ARG2, ##__VA_ARGS__)) + +#define _EMAP2_INNER() EMAP2_INNER diff --git a/src/avr/src/drv8711.c b/src/avr/src/drv8711.c new file mode 100644 index 0000000..5db2355 --- /dev/null +++ b/src/avr/src/drv8711.c @@ -0,0 +1,546 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "drv8711.h" +#include "status.h" +#include "stepper.h" +#include "switch.h" +#include "estop.h" +#include "seek.h" + +#include +#include + +#include +#include +#include +#include + + +#define DRIVERS MOTORS + + +#define DRV8711_WORD_BYTE_PTR(WORD, LOW) (((uint8_t *)&(WORD)) + !(LOW)) + + +typedef enum { + SS_WRITE_OFF, + SS_WRITE_BLANK, + SS_WRITE_DECAY, + SS_WRITE_STALL, + SS_WRITE_DRIVE, + SS_WRITE_TORQUE, + SS_WRITE_CTRL, + SS_READ_OFF, + SS_READ_STATUS, + SS_CLEAR_STATUS, +} spi_state_t; + + +bool motor_fault = false; + + +typedef struct { + float current; + uint8_t torque; +} current_t; + + +typedef struct { + uint8_t cs_pin; + switch_id_t stall_sw; + + uint8_t status; + uint16_t flags; + bool reset_flags; + bool stalled; + + drv8711_state_t state; + current_t drive; + current_t idle; + current_t stall; + uint16_t stall_vdiv; + uint8_t stall_thresh; + uint8_t last_thresh; + uint16_t sample_time; + //float stall_current; + + uint8_t microstep; + stall_callback_t stall_cb; + + uint8_t last_torque; + uint8_t last_microstep; + + spi_state_t spi_state; +} drv8711_driver_t; + + +static drv8711_driver_t drivers[DRIVERS] = { + {.cs_pin = SPI_CS_0_PIN, .stall_sw = SW_STALL_0}, + {.cs_pin = SPI_CS_1_PIN, .stall_sw = SW_STALL_1}, + {.cs_pin = SPI_CS_2_PIN, .stall_sw = SW_STALL_2}, + {.cs_pin = SPI_CS_3_PIN, .stall_sw = SW_STALL_3}, +}; + + +typedef struct { + bool advance; + uint8_t disable_cs_pin; + + uint16_t command; + uint16_t response; + uint8_t driver; + bool low_byte; +} spi_t; + +static spi_t spi = {0}; + + +static void _current_set(current_t *c, float current) { + const float gain = DRV8711_CTRL_GAIN(DRV8711_CTRL); + const float max_current = CURRENT_SENSE_REF / (CURRENT_SENSE_RESISTOR * gain); + + // Limit to max configurable current (11A) + if (max_current < current) current = max_current; + + c->current = current; + c->torque = round(current * CURRENT_SENSE_RESISTOR / CURRENT_SENSE_REF * + gain * 255); +} + + +static bool _driver_fault(drv8711_driver_t *drv) {return drv->flags & 0x1f;} + + +static bool _driver_active(drv8711_driver_t *drv) { + return drv->state == DRV8711_ACTIVE; +} + + +static float _driver_get_current(drv8711_driver_t *drv) { + if (_driver_fault(drv)) return 0; + + switch (drv->state) { + case DRV8711_IDLE: return drv->idle.current; + case DRV8711_ACTIVE: return drv->drive.current; + default: return 0; // Off + } +} + + +static uint8_t _driver_get_torque(drv8711_driver_t *drv) { + if (estop_triggered()) return 0; + + if(seek_active()) return drv->stall.torque; + + switch (drv->state) { + case DRV8711_IDLE: return drv->idle.torque; + case DRV8711_ACTIVE: return drv->drive.torque; + default: return 0; // Off + } +} + + +static uint16_t _driver_spi_command(drv8711_driver_t *drv) { + switch (drv->spi_state) { + case SS_WRITE_OFF: return DRV8711_WRITE(DRV8711_OFF_REG, DRV8711_OFF); + case SS_WRITE_BLANK: return DRV8711_WRITE(DRV8711_BLANK_REG, DRV8711_BLANK); + case SS_WRITE_DECAY: return DRV8711_WRITE(DRV8711_DECAY_REG, DRV8711_DECAY); + + case SS_WRITE_STALL: { + //uint16_t reg = drv->stall_thresh | DRV8711_STALL_SDCNT_2 | drv->stall_vdiv; //ORIGINAL + //uint16_t reg = 50 | DRV8711_STALL_SDCNT_8 | DRV8711_STALL_VDIV_4; //WORKS + uint16_t reg = drv->stall_thresh | DRV8711_STALL_SDCNT_8 | drv->stall_vdiv; + drv->last_thresh = drv->stall_thresh; + return DRV8711_WRITE(DRV8711_STALL_REG, reg); + } + + case SS_WRITE_DRIVE: return DRV8711_WRITE(DRV8711_DRIVE_REG, DRV8711_DRIVE); + + case SS_WRITE_TORQUE: + drv->last_torque = _driver_get_torque(drv); + return DRV8711_WRITE(DRV8711_TORQUE_REG, DRV8711_TORQUE | drv->last_torque); + + case SS_WRITE_CTRL: { + // NOTE, we disable the driver if it's not active. The chip gets hot + // idling with the driver enabled. + bool enable = _driver_get_torque(drv); + drv->last_microstep = drv->microstep; + return DRV8711_WRITE(DRV8711_CTRL_REG, DRV8711_CTRL | + (drv->microstep << 3) | + (enable ? DRV8711_CTRL_ENBL_bm : 0)); + } + + case SS_READ_OFF: return DRV8711_READ(DRV8711_OFF_REG); + case SS_READ_STATUS: return DRV8711_READ(DRV8711_STATUS_REG); + + case SS_CLEAR_STATUS: + drv->reset_flags = false; + drv->flags = 0; + return DRV8711_WRITE(DRV8711_STATUS_REG, 0x0fff & ~drv->status); + } + + return 0; // Should not get here +} + + +static spi_state_t _driver_spi_next(drv8711_driver_t *drv) { + // Process response + switch (drv->spi_state) { + case SS_READ_OFF: + // We read back the OFF register to test for communication failure. + if ((spi.response & 0x1ff) != DRV8711_OFF) + drv->flags |= DRV8711_COMM_ERROR_bm; + else drv->flags &= ~DRV8711_COMM_ERROR_bm; + break; + + case SS_READ_STATUS: { + drv->status = spi.response; + + // NOTE If there is a power fault and the drivers are not powered + // then the status flags will read 0xff but the motor fault line will + // not be asserted. So, fault flags are not valid with out motor fault. + // Also, a real stall cannot occur if the driver is inactive. + bool active = _driver_active(drv); + uint8_t mask = + ((motor_fault && !drv->reset_flags) ? 0xff : 0) | (active ? 0xc0 : 0); + drv->flags = (drv->flags & 0xff00) | (mask & drv->status); + + // EStop on fatal driver faults + if (_driver_fault(drv)) estop_trigger(STAT_MOTOR_FAULT); + break; + } + + default: break; + } + + switch (drv->spi_state) { + case SS_READ_OFF: + if (drv->flags & DRV8711_COMM_ERROR_bm) return SS_WRITE_OFF; // Retry + break; + + case SS_READ_STATUS: + if (drv->reset_flags) return SS_CLEAR_STATUS; + if (drv->last_torque != _driver_get_torque(drv)) return SS_WRITE_TORQUE; + if (drv->last_microstep != drv->microstep) return SS_WRITE_CTRL; + if (drv->last_thresh != drv->stall_thresh) return SS_WRITE_STALL; + // Fall through + + case SS_CLEAR_STATUS: return SS_READ_OFF; + + default: break; + } + + return (spi_state_t)(drv->spi_state + 1); // Next +} + + +static void _spi_send() { + drv8711_driver_t *drv = &drivers[spi.driver]; + + // Flush any status errors (TODO check SPI errors) + uint8_t x = SPIC.STATUS; + x = x; + + // Read byte + *DRV8711_WORD_BYTE_PTR(spi.response, !spi.low_byte) = SPIC.DATA; + + // Advance state and process response + if (spi.advance) { + spi.advance = false; + + // Handle response and set next state + drv->spi_state = _driver_spi_next(drv); + + // Next driver + if (++spi.driver == DRIVERS) spi.driver = 0; // Wrap around + drv = &drivers[spi.driver]; + } + + // Disable CS + if (spi.disable_cs_pin) { + OUTCLR_PIN(spi.disable_cs_pin); // Set low (inactive) + _delay_us(1); + spi.disable_cs_pin = 0; + } + + if (spi.low_byte) { + spi.disable_cs_pin = drv->cs_pin; // Schedule next CS disable + spi.advance = true; // Word complete + + } else { + // Enable CS + OUTSET_PIN(drv->cs_pin); // Set high (active) + _delay_us(1); + + // Get next command + spi.command = _driver_spi_command(drv); + } + + // Write byte and prep next read + SPIC.DATA = *DRV8711_WORD_BYTE_PTR(spi.command, spi.low_byte); + + // Next byte + spi.low_byte = !spi.low_byte; +} + + +ISR(SPIC_INT_vect) {_spi_send();} + + +static void _stall_change(int driver, bool stalled) { + drivers[driver].stalled = stalled; + + // Call stall callback + if (stalled && drivers[driver].stall_cb) + drivers[driver].stall_cb(driver); +} + + +static void _stall_switch_cb(switch_id_t sw, bool active) { + switch (sw) { + case SW_STALL_0: _stall_change(0, active); break; + case SW_STALL_1: _stall_change(1, active); break; + case SW_STALL_2: _stall_change(2, active); break; + case SW_STALL_3: _stall_change(3, active); break; + default: break; + } +} + + +static void _motor_fault_switch_cb(switch_id_t sw, bool active) { + motor_fault = active; +} + + +void drv8711_init() { + // Setup pins + // Must set the SS pin either in/high or out/any for master mode to work + // Note, this pin is also used by the USART as the CTS line + DIRSET_PIN(SPI_SS_PIN); // Output + OUTSET_PIN(SPI_CLK_PIN); // High + DIRSET_PIN(SPI_CLK_PIN); // Output + DIRCLR_PIN(SPI_MISO_PIN); // Input + OUTSET_PIN(SPI_MOSI_PIN); // High + DIRSET_PIN(SPI_MOSI_PIN); // Output + + // Motor driver enable + OUTSET_PIN(MOTOR_ENABLE_PIN); // Active high + DIRSET_PIN(MOTOR_ENABLE_PIN); // Output + + for (int i = 0; i < DRIVERS; i++) { + uint8_t cs_pin = drivers[i].cs_pin; + OUTSET_PIN(cs_pin); // High + DIRSET_PIN(cs_pin); // Output + + switch_id_t stall_sw = drivers[i].stall_sw; + switch_set_type(stall_sw, SW_NORMALLY_OPEN); + switch_set_callback(stall_sw, _stall_switch_cb); + + drivers[i].reset_flags = true; // Reset flags once on startup + } + + switch_set_type(SW_MOTOR_FAULT, SW_NORMALLY_OPEN); + switch_set_callback(SW_MOTOR_FAULT, _motor_fault_switch_cb); + + // Configure SPI + PR.PRPC &= ~PR_SPI_bm; // Disable power reduction + SPIC.CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_MODE_0_gc | + SPI_PRESCALER_DIV16_gc; // enable, big endian, master, mode, clock div + PIN_PORT(SPI_CLK_PIN)->REMAP = PORT_SPI_bm; // Swap SCK and MOSI + SPIC.INTCTRL = SPI_INTLVL_LO_gc; // interupt level + + _spi_send(); // Kick it off +} + + +drv8711_state_t drv8711_get_state(int driver) { + if (driver < 0 || DRIVERS <= driver) return DRV8711_DISABLED; + return drivers[driver].state; +} + + +void drv8711_set_state(int driver, drv8711_state_t state) { + if (driver < 0 || DRIVERS <= driver) return; + drivers[driver].state = state; +} + + +void drv8711_set_microsteps(int driver, uint16_t msteps) { + if (driver < 0 || DRIVERS <= driver) return; + switch (msteps) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: case 256: + break; + default: return; // Invalid + } + + drivers[driver].microstep = round(logf(msteps) / logf(2)); +} + + + +void drv8711_set_stall_callback(int driver, stall_callback_t cb) { + drivers[driver].stall_cb = cb; +} + + +float get_drive_current(int driver) { + if (driver < 0 || DRIVERS <= driver) return 0; + return drivers[driver].drive.current; +} + + +void set_drive_current(int driver, float value) { + if (driver < 0 || DRIVERS <= driver || value < 0) return; + if (MAX_CURRENT < value) value = MAX_CURRENT; + _current_set(&drivers[driver].drive, value); +} + + +float get_idle_current(int driver) { + if (driver < 0 || DRIVERS <= driver) return 0; + return drivers[driver].idle.current; +} + + +void set_idle_current(int driver, float value) { + if (driver < 0 || DRIVERS <= driver || value < 0 || MAX_IDLE_CURRENT < value) + return; + + _current_set(&drivers[driver].idle, value); +} + + +float get_active_current(int driver) { + if (driver < 0 || DRIVERS <= driver) return 0; + return _driver_get_current(&drivers[driver]); +} + + +bool get_motor_fault() {return motor_fault;} + + +void set_driver_flags(int driver, uint16_t flags) { + drivers[driver].reset_flags = true; +} + + +uint16_t get_driver_flags(int driver) {return drivers[driver].flags;} +bool get_driver_stalled(int driver) {return drivers[driver].stalled;} + + +float get_stall_volts(int driver) { + if (driver < 0 || DRIVERS <= driver) return 0; + + float vdiv; + switch (drivers[driver].stall_vdiv) { + case DRV8711_STALL_VDIV_4: vdiv = 4; break; + case DRV8711_STALL_VDIV_8: vdiv = 8; break; + case DRV8711_STALL_VDIV_16: vdiv = 16; break; + default: vdiv = 32; break; + } + + return 1.8 / 256 * vdiv * drivers[driver].stall_thresh; +} + + +void set_stall_volts(int driver, float volts) { + if (driver < 0 || DRIVERS <= driver) return; + + uint16_t vdiv = DRV8711_STALL_VDIV_32; + uint16_t thresh = (uint16_t)(volts * 256 / 1.8); + + if (thresh < 4 << 8) { + thresh >>= 2; + vdiv = DRV8711_STALL_VDIV_4; + + } else if (thresh < 8 << 8) { + thresh >>= 3; + vdiv = DRV8711_STALL_VDIV_8; + + } else if (thresh < 16 << 8) { + thresh >>= 4; + vdiv = DRV8711_STALL_VDIV_16; + + } else { + if (thresh < 32 << 8) thresh >>= 5; + else thresh = 255; + } + + drivers[driver].stall_vdiv = vdiv; + drivers[driver].stall_thresh = thresh; +} + +void set_stall_sample_time(int driver, uint16_t sample_time) +{ + if (driver < 0 || DRIVERS <= driver) return; + + switch (sample_time) { + case 50: drivers[driver].sample_time = DRV8711_TORQUE_SMPLTH_50; break; + case 100: drivers[driver].sample_time = DRV8711_TORQUE_SMPLTH_100; break; + case 200: drivers[driver].sample_time = DRV8711_TORQUE_SMPLTH_200; break; + case 300: drivers[driver].sample_time = DRV8711_TORQUE_SMPLTH_300; break; + case 400: drivers[driver].sample_time = DRV8711_TORQUE_SMPLTH_400; break; + case 600: drivers[driver].sample_time = DRV8711_TORQUE_SMPLTH_600; break; + case 800: drivers[driver].sample_time = DRV8711_TORQUE_SMPLTH_800; break; + case 1000: drivers[driver].sample_time = DRV8711_TORQUE_SMPLTH_1000; break; + default: return; // Invalid + } + +} + +uint16_t get_stall_sample_time(int driver) +{ + if (driver < 0 || DRIVERS <= driver) return 0; + + uint16_t sample_time = 0; + switch (drivers[driver].sample_time) { + case DRV8711_TORQUE_SMPLTH_50: sample_time = 50; break; + case DRV8711_TORQUE_SMPLTH_100: sample_time = 100; break; + case DRV8711_TORQUE_SMPLTH_200: sample_time = 200; break; + case DRV8711_TORQUE_SMPLTH_300: sample_time = 300; break; + case DRV8711_TORQUE_SMPLTH_400: sample_time = 400; break; + case DRV8711_TORQUE_SMPLTH_600: sample_time = 600; break; + case DRV8711_TORQUE_SMPLTH_800: sample_time = 800; break; + case DRV8711_TORQUE_SMPLTH_1000: sample_time = 1000; break; + default: sample_time = 50; break; + } + + return sample_time; +} + +float get_stall_current(int driver) { + if (driver < 0 || DRIVERS <= driver) return 0; + return drivers[driver].stall.current; +} + + +void set_stall_current(int driver, float value) { + if (driver < 0 || DRIVERS <= driver || value < 0) return; + if (MAX_CURRENT < value) value = MAX_CURRENT; + _current_set(&drivers[driver].stall, value); +} diff --git a/src/avr/src/drv8711.h b/src/avr/src/drv8711.h new file mode 100644 index 0000000..78c3859 --- /dev/null +++ b/src/avr/src/drv8711.h @@ -0,0 +1,188 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +#include "config.h" +#include "status.h" +#include "motor.h" + +#include +#include + +enum { + DRV8711_CTRL_REG, + DRV8711_TORQUE_REG, + DRV8711_OFF_REG, + DRV8711_BLANK_REG, + DRV8711_DECAY_REG, + DRV8711_STALL_REG, + DRV8711_DRIVE_REG, + DRV8711_STATUS_REG, +}; + + +enum { + DRV8711_CTRL_ENBL_bm = 1 << 0, + DRV8711_CTRL_RDIR_bm = 1 << 1, + DRV8711_CTRL_RSTEP_bm = 1 << 2, + DRV8711_CTRL_MODE_1 = 0 << 3, + DRV8711_CTRL_MODE_2 = 1 << 3, + DRV8711_CTRL_MODE_4 = 2 << 3, + DRV8711_CTRL_MODE_8 = 3 << 3, + DRV8711_CTRL_MODE_16 = 4 << 3, + DRV8711_CTRL_MODE_32 = 5 << 3, + DRV8711_CTRL_MODE_64 = 6 << 3, + DRV8711_CTRL_MODE_128 = 7 << 3, + DRV8711_CTRL_MODE_256 = 8 << 3, + DRV8711_CTRL_EXSTALL_bm = 1 << 7, + DRV8711_CTRL_ISGAIN_5 = 0 << 8, + DRV8711_CTRL_ISGAIN_10 = 1 << 8, + DRV8711_CTRL_ISGAIN_20 = 2 << 8, + DRV8711_CTRL_ISGAIN_40 = 3 << 8, + DRV8711_CTRL_DTIME_400 = 0 << 10, + DRV8711_CTRL_DTIME_450 = 1 << 10, + DRV8711_CTRL_DTIME_650 = 2 << 10, + DRV8711_CTRL_DTIME_850 = 3 << 10, +}; + + +enum { + DRV8711_TORQUE_SMPLTH_50 = 0 << 8, + DRV8711_TORQUE_SMPLTH_100 = 1 << 8, + DRV8711_TORQUE_SMPLTH_200 = 2 << 8, + DRV8711_TORQUE_SMPLTH_300 = 3 << 8, + DRV8711_TORQUE_SMPLTH_400 = 4 << 8, + DRV8711_TORQUE_SMPLTH_600 = 5 << 8, + DRV8711_TORQUE_SMPLTH_800 = 6 << 8, + DRV8711_TORQUE_SMPLTH_1000 = 7 << 8, +}; + + +enum { + DRV8711_OFF_PWMMODE_bm = 1 << 8, +}; + + +enum { + DRV8711_BLANK_ABT_bm = 1 << 8, +}; + + +enum { + DRV8711_DECAY_DECMOD_SLOW = 0 << 8, + DRV8711_DECAY_DECMOD_OPT = 1 << 8, + DRV8711_DECAY_DECMOD_FAST = 2 << 8, + DRV8711_DECAY_DECMOD_MIXED = 3 << 8, + DRV8711_DECAY_DECMOD_AUTO_OPT = 4 << 8, + DRV8711_DECAY_DECMOD_AUTO_MIXED = 5 << 8, +}; + + +enum { + DRV8711_STALL_SDCNT_1 = 0 << 8, + DRV8711_STALL_SDCNT_2 = 1 << 8, + DRV8711_STALL_SDCNT_4 = 2 << 8, + DRV8711_STALL_SDCNT_8 = 3 << 8, + DRV8711_STALL_VDIV_32 = 0 << 10, + DRV8711_STALL_VDIV_16 = 1 << 10, + DRV8711_STALL_VDIV_8 = 2 << 10, + DRV8711_STALL_VDIV_4 = 3 << 10, +}; + + +enum { + DRV8711_DRIVE_OCPTH_250 = 0 << 0, + DRV8711_DRIVE_OCPTH_500 = 1 << 0, + DRV8711_DRIVE_OCPTH_750 = 2 << 0, + DRV8711_DRIVE_OCPTH_1000 = 3 << 0, + DRV8711_DRIVE_OCPDEG_1 = 0 << 2, + DRV8711_DRIVE_OCPDEG_2 = 1 << 2, + DRV8711_DRIVE_OCPDEG_4 = 2 << 2, + DRV8711_DRIVE_OCPDEG_8 = 3 << 2, + DRV8711_DRIVE_TDRIVEN_250 = 0 << 4, + DRV8711_DRIVE_TDRIVEN_500 = 1 << 4, + DRV8711_DRIVE_TDRIVEN_1000 = 2 << 4, + DRV8711_DRIVE_TDRIVEN_2000 = 3 << 4, + DRV8711_DRIVE_TDRIVEP_250 = 0 << 6, + DRV8711_DRIVE_TDRIVEP_500 = 1 << 6, + DRV8711_DRIVE_TDRIVEP_1000 = 2 << 6, + DRV8711_DRIVE_TDRIVEP_2000 = 3 << 6, + DRV8711_DRIVE_IDRIVEN_100 = 0 << 8, + DRV8711_DRIVE_IDRIVEN_200 = 1 << 8, + DRV8711_DRIVE_IDRIVEN_300 = 2 << 8, + DRV8711_DRIVE_IDRIVEN_400 = 3 << 8, + DRV8711_DRIVE_IDRIVEP_50 = 0 << 10, + DRV8711_DRIVE_IDRIVEP_100 = 1 << 10, + DRV8711_DRIVE_IDRIVEP_150 = 2 << 10, + DRV8711_DRIVE_IDRIVEP_200 = 3 << 10, +}; + + +enum { + DRV8711_STATUS_OTS_bm = 1 << 0, + DRV8711_STATUS_AOCP_bm = 1 << 1, + DRV8711_STATUS_BOCP_bm = 1 << 2, + DRV8711_STATUS_APDF_bm = 1 << 3, + DRV8711_STATUS_BPDF_bm = 1 << 4, + DRV8711_STATUS_UVLO_bm = 1 << 5, + DRV8711_STATUS_STD_bm = 1 << 6, + DRV8711_STATUS_STDLAT_bm = 1 << 7, + DRV8711_COMM_ERROR_bm = 1 << 8, +}; + + +#define DRV8711_READ(ADDR) ((1 << 15) | ((ADDR) << 12)) +#define DRV8711_WRITE(ADDR, DATA) (((ADDR) << 12) | ((DATA) & 0xfff)) +#define DRV8711_CMD_ADDR(CMD) (((CMD) >> 12) & 7) +#define DRV8711_CMD_IS_READ(CMD) ((1 << 15) & (CMD)) + +#define DRV8711_CTRL_ISGAIN_BM (3 << 8) +#define DRV8711_CTRL_GET_GAIN(CTRL) ((CTRL) & DRV8711_CTRL_ISGAIN_BM) + +#define DRV8711_CTRL_GAIN(CTRL) \ + (DRV8711_CTRL_GET_GAIN(CTRL) == DRV8711_CTRL_ISGAIN_5 ? 5 : \ + (DRV8711_CTRL_GET_GAIN(CTRL) == DRV8711_CTRL_ISGAIN_10 ? 10 : \ + (DRV8711_CTRL_GET_GAIN(CTRL) == DRV8711_CTRL_ISGAIN_20 ? 20 : 40))) + + +typedef enum { + DRV8711_DISABLED, + DRV8711_IDLE, + DRV8711_ACTIVE, +} drv8711_state_t; + + +typedef void (*stall_callback_t)(int driver); + + +void drv8711_init(); +drv8711_state_t drv8711_get_state(int driver); +void drv8711_set_state(int driver, drv8711_state_t state); +void drv8711_set_microsteps(int driver, uint16_t msteps); +void drv8711_set_stall_callback(int driver, stall_callback_t cb); diff --git a/src/avr/src/emu.h b/src/avr/src/emu.h new file mode 100644 index 0000000..cb3d605 --- /dev/null +++ b/src/avr/src/emu.h @@ -0,0 +1,36 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#ifdef __AVR__ +#define emu_init() +#define emu_callback() + +#else +void emu_init(); +void emu_callback(); + +#endif diff --git a/src/avr/src/estop.c b/src/avr/src/estop.c new file mode 100644 index 0000000..1e9bacb --- /dev/null +++ b/src/avr/src/estop.c @@ -0,0 +1,126 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "estop.h" +#include "motor.h" +#include "stepper.h" +#include "spindle.h" +#include "switch.h" +#include "hardware.h" +#include "config.h" +#include "state.h" +#include "outputs.h" +#include "jog.h" +#include "exec.h" + + +static stat_t estop_reason = STAT_OK; + + +static void _switch_callback(switch_id_t id, bool active) { + if (active) estop_trigger(STAT_ESTOP_SWITCH); + else estop_clear(); +} + + +void estop_init() { + switch_set_callback(SW_ESTOP, _switch_callback); + if (switch_is_active(SW_ESTOP)) estop_trigger(STAT_ESTOP_SWITCH); +} + + +bool estop_triggered() {return estop_reason != STAT_OK;} + + +void estop_trigger(stat_t reason) { + if (estop_triggered()) return; + estop_reason = reason; + + // Set fault signal + outputs_set_active(FAULT_PIN, true); + + // Shutdown peripherals + st_shutdown(); + spindle_estop(); + jog_stop(); + outputs_stop(); + + // Set machine state + state_estop(); +} + + +void estop_clear() { + // It is important that we don't clear the estop if it's not set because + // it can cause a reboot loop. + if (!estop_triggered()) return; + + // Check if estop switch is set + if (switch_is_active(SW_ESTOP)) { + estop_reason = STAT_ESTOP_SWITCH; + return; // Can't clear while estop switch is still active + } + + // Clear fault signal + outputs_set_active(FAULT_PIN, false); + + estop_reason = STAT_OK; + + // Reboot + // Note, hardware.c waits until any spindle stop command has been delivered + hw_request_hard_reset(); +} + + +// Var callbacks +bool get_estop() {return estop_triggered();} + + +void set_estop(bool value) { + if (value == estop_triggered()) return; + if (value) estop_trigger(STAT_ESTOP_USER); + else estop_clear(); +} + + +PGM_P get_estop_reason() {return status_to_pgmstr(estop_reason);} + + +// Command callbacks +stat_t command_estop(char *cmd) { + estop_trigger(STAT_ESTOP_USER); + return STAT_OK; +} + + +stat_t command_shutdown(char *cmd) { + estop_trigger(STAT_POWER_SHUTDOWN); + return STAT_OK; +} + + +stat_t command_clear(char *cmd) {estop_clear(); return STAT_OK;} diff --git a/src/avr/src/estop.h b/src/avr/src/estop.h new file mode 100644 index 0000000..652f5c0 --- /dev/null +++ b/src/avr/src/estop.h @@ -0,0 +1,42 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "status.h" + +#include + + +void estop_init(); +bool estop_triggered(); +void estop_trigger(stat_t reason); +void estop_clear(); + + +#define ESTOP_ASSERT(COND, CODE) \ + do {if (!(COND)) estop_trigger(CODE);} while (0) diff --git a/src/avr/src/exec.c b/src/avr/src/exec.c new file mode 100644 index 0000000..bc4d9df --- /dev/null +++ b/src/avr/src/exec.c @@ -0,0 +1,306 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "exec.h" + +#include "stepper.h" +#include "motor.h" +#include "axis.h" +#include "util.h" +#include "command.h" +#include "switch.h" +#include "seek.h" +#include "estop.h" +#include "state.h" +#include "spindle.h" +#include "config.h" +#include "SCurve.h" + + +static struct { + exec_cb_t cb; + + float position[AXES]; + float velocity; + float accel; + float jerk; + float peak_vel; + float peak_accel; + + float feed_override; + + struct { + float target[AXES]; + float time; + float vel; + float accel; + float max_accel; + float max_jerk; + power_update_t power_updates[2 * POWER_MAX_UPDATES]; + exec_cb_t cb; + } seg; +} ex; + + +static void _limit_switch_cb(switch_id_t sw, bool active) { + if (sw == seek_get_switch()) return; + if (ex.velocity && active) estop_trigger(STAT_ESTOP_SWITCH); +} + + +void exec_init() { + memset(&ex, 0, sizeof(ex)); + ex.feed_override = 1; // TODO implement feed override + + // Set callback for limit switches + for (int sw = SW_MIN_0; sw <= SW_MAX_3; sw++) + switch_set_callback((switch_id_t)sw, _limit_switch_cb); +} + + +void exec_get_position(float p[AXES]) { + memcpy(p, ex.position, sizeof(ex.position)); +} + + +float exec_get_axis_position(int axis) {return ex.position[axis];} + + +void exec_set_velocity(float v) { + ex.velocity = v; + if (ex.peak_vel < v) ex.peak_vel = v; +} + + +float exec_get_velocity() {return ex.velocity;} + + +void exec_set_acceleration(float a) { + ex.accel = a; + if (ex.peak_accel < a) ex.peak_accel = a; +} + + +float exec_get_acceleration() {return ex.accel;} +void exec_set_jerk(float j) {ex.jerk = j;} + + +void exec_set_cb(exec_cb_t cb) {ex.cb = cb;} + + +void exec_move_to_target(const float target[]) { + ESTOP_ASSERT(isfinite(target[AXIS_X]) && isfinite(target[AXIS_Y]) && + isfinite(target[AXIS_Z]) && isfinite(target[AXIS_A]) && + isfinite(target[AXIS_B]) && isfinite(target[AXIS_C]), + STAT_BAD_FLOAT); + + // Prep power updates + st_prep_power(ex.seg.power_updates); + + // Shift power updates + for (unsigned i = 0; i < POWER_MAX_UPDATES; i++) { + ex.seg.power_updates[i] = ex.seg.power_updates[i + POWER_MAX_UPDATES]; + ex.seg.power_updates[i + POWER_MAX_UPDATES].state = POWER_IGNORE; + } + + // Update position + copy_vector(ex.position, target); + + // Call the stepper prep function + st_prep_line(target); +} + + +stat_t _segment_exec() { + float t = ex.seg.time; + float v = ex.seg.vel; + float a = ex.seg.accel; + + // Handle pause + if (state_get() == STATE_STOPPING) { + a = SCurve::nextAccel(SEGMENT_TIME, 0, ex.velocity, ex.accel, + ex.seg.max_accel, ex.seg.max_jerk); + v = ex.velocity + SEGMENT_TIME * a; + t *= ex.seg.vel / v; + + if (v < MIN_VELOCITY || seek_switch_found()) { + t = v = 0; + ex.seg.cb = 0; + command_reset_position(); + state_holding(); + seek_end(); + spindle_update_speed(); + } + } + + // Wait for next seg if time is too short and we are still moving + if (t < SEGMENT_TIME && (!t || v)) { + if (!v) { + exec_set_velocity(0); + exec_set_acceleration(0); + exec_set_jerk(0); + ex.seg.time = 0; + } + ex.cb = ex.seg.cb; + return STAT_AGAIN; + } + + // Update velocity and accel + exec_set_velocity(v); + exec_set_acceleration(a); + + if (t <= SEGMENT_TIME) { + // Move + exec_move_to_target(ex.seg.target); + ex.seg.time = 0; + + } else { + // Compute next target + float ratio = SEGMENT_TIME / t; + float target[AXES]; + for (int axis = 0; axis < AXES; axis++) { + float diff = ex.seg.target[axis] - ex.position[axis]; + target[axis] = ex.position[axis] + ratio * diff; + } + + // Move + exec_move_to_target(target); + + // Update time + if (t == ex.seg.time) ex.seg.time -= SEGMENT_TIME; + else ex.seg.time -= SEGMENT_TIME * v / ex.seg.vel; + } + + // Check switch + if (seek_switch_found()) state_seek_hold(); + + return STAT_OK; +} + + +stat_t exec_segment(float time, const float target[], float vel, float accel, + float maxAccel, float maxJerk, + const power_update_t power_updates[]) { + // Copy power updates in to the correct position given the time offset + float nextT = ex.seg.time + time; + const float stepT = 1.0 / 60000; // 1ms in mins + float t = 0.5 / 60000; // 0.5ms in mins + unsigned j = 0; + for (unsigned i = 0; t < nextT && j < POWER_MAX_UPDATES; i++) { + if (ex.seg.time < t) ex.seg.power_updates[i] = power_updates[j++]; + t += stepT; + } + + copy_vector(ex.seg.target, target); + ex.seg.time = nextT; + ex.seg.vel = vel; + ex.seg.accel = accel; + ex.seg.max_accel = maxAccel; + ex.seg.max_jerk = maxJerk; + ex.seg.cb = ex.cb; + ex.cb = _segment_exec; + + // TODO To be precise, seek_end() should not be called until the current + // segment has completed execution. + if (!ex.seg.cb) seek_end(); // No callback when at line end + + return _segment_exec(); +} + + +// Called by stepper.c from low-level interrupt +stat_t exec_next() { + // Hold if we've reached zero velocity between commands and stopping + if (!ex.cb && !exec_get_velocity() && state_get() == STATE_STOPPING) + state_holding(); + + if (state_get() == STATE_HOLDING) return STAT_NOP; + if (!ex.cb && !command_exec()) return STAT_NOP; // Queue empty + if (!ex.cb) return STAT_AGAIN; // Non-exec command + return ex.cb(); // Exec +} + + +// Variable callbacks +float get_axis_position(int axis) {return ex.position[axis];} +float get_velocity() {return ex.velocity / VELOCITY_MULTIPLIER;} +float get_acceleration() {return ex.accel / ACCEL_MULTIPLIER;} +float get_jerk() {return ex.jerk / JERK_MULTIPLIER;} +float get_peak_vel() {return ex.peak_vel / VELOCITY_MULTIPLIER;} +void set_peak_vel(float x) {ex.peak_vel = 0;} +float get_peak_accel() {return ex.peak_accel / ACCEL_MULTIPLIER;} +void set_peak_accel(float x) {ex.peak_accel = 0;} +uint16_t get_feed_override() {return ex.feed_override * 1000;} +void set_feed_override(uint16_t value) {ex.feed_override = value / 1000.0;} + + +// Command callbacks +typedef struct { + uint8_t axis; + float position; +} set_axis_t; + + +stat_t command_set_axis(char *cmd) { + cmd++; // Skip command name + + // Decode axis + int axis = axis_get_id(*cmd++); + if (axis < 0) return STAT_INVALID_ARGUMENTS; + + // Decode position + float position; + if (!decode_float(&cmd, &position)) return STAT_BAD_FLOAT; + + // Check for end of command + if (*cmd) return STAT_INVALID_ARGUMENTS; + + // Update command + command_set_axis_position((uint8_t)axis, position); + + // Queue + set_axis_t set_axis = {(uint8_t)axis, position}; + command_push(COMMAND_set_axis, &set_axis); + + return STAT_OK; +} + + +unsigned command_set_axis_size() {return sizeof(set_axis_t);} + + +void command_set_axis_exec(void *data) { + set_axis_t *cmd = (set_axis_t *)data; + + // Update exec + ex.position[cmd->axis] = cmd->position; + + // Update motors + for (int motor = 0; motor < MOTORS; motor++) + if (motor_get_axis(motor) == cmd->axis) + motor_set_position(motor, cmd->position); +} diff --git a/src/avr/src/exec.h b/src/avr/src/exec.h new file mode 100644 index 0000000..af238cd --- /dev/null +++ b/src/avr/src/exec.h @@ -0,0 +1,59 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +#include "config.h" +#include "spindle.h" +#include "status.h" + +#include +#include + + +typedef stat_t (*exec_cb_t)(); + + +void exec_init(); + +void exec_get_position(float p[AXES]); +float exec_get_axis_position(int axis); +float exec_get_power_scale(); +void exec_set_velocity(float v); +float exec_get_velocity(); +void exec_set_acceleration(float a); +float exec_get_acceleration(); +void exec_set_jerk(float j); + +void exec_set_cb(exec_cb_t cb); + +void exec_move_to_target(const float target[]); +stat_t exec_segment(float time, const float target[], float vel, float accel, + float maxAccel, float maxJerk, + const power_update_t power_updates[]); +stat_t exec_next(); diff --git a/src/avr/src/hardware.c b/src/avr/src/hardware.c new file mode 100644 index 0000000..ec18f75 --- /dev/null +++ b/src/avr/src/hardware.c @@ -0,0 +1,158 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "hardware.h" +#include "rtc.h" +#include "usart.h" +#include "config.h" +#include "pgmspace.h" + +#include +#include +#include + +#include +#include + + +typedef struct { + char id[26]; + bool hard_reset; // flag to perform a hard reset +} hw_t; + +static hw_t hw = {{0}}; + + +#define PROD_SIGS (*(NVM_PROD_SIGNATURES_t *)0x0000) +#define HEXNIB(x) "0123456789abcdef"[(x) & 0xf] + + +static void _init_clock() { +#if 0 // 32Mhz Int RC + OSC.CTRL |= OSC_RC32MEN_bm | OSC_RC32KEN_bm; // Enable 32MHz & 32KHz osc + while (!(OSC.STATUS & OSC_RC32KRDY_bm)); // Wait for 32Khz oscillator + while (!(OSC.STATUS & OSC_RC32MRDY_bm)); // Wait for 32MHz oscillator + + // Defaults to calibrate against internal 32Khz clock + DFLLRC32M.CTRL = DFLL_ENABLE_bm; // Enable DFLL + CCP = CCP_IOREG_gc; // Disable register security + CLK.CTRL = CLK_SCLKSEL_RC32M_gc; // Switch to 32MHz clock + +#else + // 12-16 MHz crystal; 0.4-16 MHz XTAL w/ 16K CLK startup + OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc; + OSC.CTRL = OSC_XOSCEN_bm; // enable external crystal oscillator + while (!(OSC.STATUS & OSC_XOSCRDY_bm)); // wait for oscillator ready + + OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | 2; // PLL source, 2x (32 MHz sys clock) + OSC.CTRL = OSC_PLLEN_bm | OSC_XOSCEN_bm; // Enable PLL & External Oscillator + while (!(OSC.STATUS & OSC_PLLRDY_bm)); // wait for PLL ready + + CCP = CCP_IOREG_gc; + CLK.CTRL = CLK_SCLKSEL_PLL_gc; // switch to PLL clock +#endif + + OSC.CTRL &= ~OSC_RC2MEN_bm; // disable internal 2 MHz clock +} + + +#ifdef __AVR__ +static void _load_hw_id_byte(int i, register8_t *reg) { + NVM.CMD = NVM_CMD_READ_CALIB_ROW_gc; + uint8_t byte = pgm_read_byte(reg); + NVM.CMD = NVM_CMD_NO_OPERATION_gc; + + hw.id[i] = HEXNIB(byte >> 4); + hw.id[i + 1] = HEXNIB(byte); +} +#endif // __AVR__ + + +static void _read_hw_id() { +#ifdef __AVR__ + int i = 0; + _load_hw_id_byte(i, &PROD_SIGS.LOTNUM5); i += 2; + _load_hw_id_byte(i, &PROD_SIGS.LOTNUM4); i += 2; + _load_hw_id_byte(i, &PROD_SIGS.LOTNUM3); i += 2; + _load_hw_id_byte(i, &PROD_SIGS.LOTNUM2); i += 2; + _load_hw_id_byte(i, &PROD_SIGS.LOTNUM1); i += 2; + _load_hw_id_byte(i, &PROD_SIGS.LOTNUM0); i += 2; + hw.id[i++] = '-'; + _load_hw_id_byte(i, &PROD_SIGS.WAFNUM); i += 2; + hw.id[i++] = '-'; + _load_hw_id_byte(i, &PROD_SIGS.COORDX1); i += 2; + _load_hw_id_byte(i, &PROD_SIGS.COORDX0); i += 2; + hw.id[i++] = '-'; + _load_hw_id_byte(i, &PROD_SIGS.COORDY1); i += 2; + _load_hw_id_byte(i, &PROD_SIGS.COORDY0); i += 2; + hw.id[i] = 0; + +#else // __AVR__ + for (int i = 0; i < 25; i++) + hw.id[i] = '0'; + hw.id[25] = 0; +#endif // __AVR__ +} + + +/// Lowest level hardware init +void hw_init() { + _init_clock(); // set system clock + rtc_init(); // real time counter + _read_hw_id(); + + // Round-robin, interrupts in application section, all interupts levels + CCP = CCP_IOREG_gc; + PMIC.CTRL = + PMIC_RREN_bm | PMIC_HILVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_LOLVLEN_bm; +} + + +void hw_request_hard_reset() {hw.hard_reset = true;} + + +static void _hard_reset() { + usart_flush(); + cli(); + CCP = CCP_IOREG_gc; + RST.CTRL = RST_SWRST_bm; +} + + +/// Controller's rest handler +void hw_reset_handler() { + if (!hw.hard_reset) return; + + // Flush serial port and wait for EEPROM writes to finish + while (!usart_tx_empty() || !eeprom_is_ready()) + continue; + + _hard_reset(); +} + + +const char *get_hw_id() {return hw.id;} diff --git a/src/avr/src/hardware.h b/src/avr/src/hardware.h new file mode 100644 index 0000000..e2be012 --- /dev/null +++ b/src/avr/src/hardware.h @@ -0,0 +1,40 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "status.h" + +#include + + +void hw_init(); +void hw_request_hard_reset(); +void hw_reset_handler(); + +uint8_t hw_disable_watchdog(); +void hw_restore_watchdog(uint8_t state); diff --git a/src/avr/src/huanyang.c b/src/avr/src/huanyang.c new file mode 100644 index 0000000..26481e1 --- /dev/null +++ b/src/avr/src/huanyang.c @@ -0,0 +1,301 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "huanyang.h" +#include "config.h" +#include "modbus.h" +#include "estop.h" + +#include + +#include +#include + + +/* + Huanyang is not quite Modbus compliant. + + Message format is: + + [id][func][length][data][checksum] + + Where: + + id - 1-byte Peer ID + func - 1-byte One of hy_func_t + length - 1-byte Length data in bytes + data - length bytes - Command arguments + checksum - 16-bit CRC: x^16 + x^15 + x^2 + 1 (0xa001) initial: 0xffff +*/ + + +// See VFD manual pg56 3.1.3 +typedef enum { + HUANYANG_FUNC_READ = 1, // [len=1][hy_addr_t] + HUANYANG_FUNC_WRITE, // [len=3][hy_addr_t][data] + HUANYANG_CTRL_WRITE, // [len=1][hy_ctrl_state_t] + HUANYANG_CTRL_READ, // [len=1][hy_ctrl_addr_t] + HUANYANG_FREQ_WRITE, // [len=2][freq] + HUANYANG_RESERVED_1, + HUANYANG_RESERVED_2, + HUANYANG_LOOP_TEST, +} hy_func_t; + + +// Sent in HUANYANG_CTRL_WRITE +// See VFD manual pg57 3.1.3.c +typedef enum { + HUANYANG_RUN = 1 << 0, + HUANYANG_FORWARD = 1 << 1, + HUANYANG_REVERSE = 1 << 2, + HUANYANG_STOP = 1 << 3, + HUANYANG_REV_FWD = 1 << 4, + HUANYANG_JOG = 1 << 5, + HUANYANG_JOG_FORWARD = 1 << 6, + HUANYANG_JOG_REVERSE = 1 << 7, +} hy_ctrl_state_t; + + +// Returned by HUANYANG_CTRL_WRITE +// See VFD manual pg57 3.1.3.c +typedef enum { + HUANYANG_STATUS_RUN = 1 << 0, + HUANYANG_STATUS_JOG = 1 << 1, + HUANYANG_STATUS_COMMAND_REV = 1 << 2, + HUANYANG_STATUS_RUNNING = 1 << 3, + HUANYANG_STATUS_JOGGING = 1 << 4, + HUANYANG_STATUS_JOGGING_REV = 1 << 5, + HUANYANG_STATUS_BRAKING = 1 << 6, + HUANYANG_STATUS_TRACK_START = 1 << 7, +} hy_ctrl_status_t; + + +// Sent in HUANYANG_CTRL_READ +// See VFD manual pg57 3.1.3.d +typedef enum { + HUANYANG_TARGET_FREQ, + HUANYANG_ACTUAL_FREQ, + HUANYANG_ACTUAL_CURRENT, + HUANYANG_ACTUAL_RPM, + HUANYANG_DCV, + HUANYANG_ACV, + HUANYANG_COUNTER, + HUANYANG_TEMPERATURE, +} hy_ctrl_addr_t; + + +static struct { + uint8_t state; + + deinit_cb_t deinit_cb; + bool shutdown; + bool changed; + float power; + + float actual_freq; + float actual_current; + uint16_t actual_rpm; + uint16_t temperature; + + float max_freq; + float min_freq; + uint16_t rated_rpm; + + uint8_t status; +} hy; + + +static void _func_read_response(hy_addr_t addr, uint16_t value) { + switch (addr) { + case HY_PD005_MAX_FREQUENCY: hy.max_freq = value * 0.01; break; + case HY_PD011_FREQUENCY_LOWER_LIMIT: hy.min_freq = value * 0.01; break; + case HY_PD144_RATED_MOTOR_RPM: hy.rated_rpm = value; break; + default: break; + } +} + + +static void _ctrl_read_response(hy_ctrl_addr_t addr, uint16_t value) { + switch (addr) { + case HUANYANG_ACTUAL_FREQ: hy.actual_freq = value * 0.01; break; + case HUANYANG_ACTUAL_CURRENT: hy.actual_current = value * 0.01; break; + case HUANYANG_ACTUAL_RPM: hy.actual_rpm = value; break; + case HUANYANG_TEMPERATURE: hy.temperature = value; break; + default: break; + } +} + + +static uint16_t _read_word(const uint8_t *data) { + return (uint16_t)data[0] << 8 | data[1]; +} + + +static void _handle_response(hy_func_t func, const uint8_t *data) { + switch (func) { + case HUANYANG_FUNC_READ: + _func_read_response((hy_addr_t)*data, _read_word(data + 1)); + break; + + case HUANYANG_FUNC_WRITE: break; + case HUANYANG_CTRL_WRITE: hy.status = *data; break; + + case HUANYANG_CTRL_READ: + _ctrl_read_response((hy_ctrl_addr_t)*data, _read_word(data + 1)); + break; + + case HUANYANG_FREQ_WRITE: break; + default: break; + } +} + + +static void _next_command(); + + +static bool _shutdown() { + if (!hy.shutdown && !estop_triggered()) return false; + modbus_deinit(); + if (hy.deinit_cb) hy.deinit_cb(); + return true; +} + + +static void _modbus_cb(uint8_t func, uint8_t bytes, const uint8_t *data) { + if (!data) { // Modbus command failed + if (_shutdown()) return; + hy.state = 0; + + } else if (bytes == *data + 1) { + _handle_response((hy_func_t)func, data + 1); + + if (func == HUANYANG_CTRL_WRITE && _shutdown()) return; + + // Next command + if (++hy.state == 9) { + if (hy.shutdown || hy.changed) hy.state = 0; + else hy.state = 5; + hy.changed = false; + } + } + + _next_command(); +} + + +static void _func_read(hy_addr_t addr) { + uint8_t data[2] = {1, addr}; + modbus_func(HUANYANG_FUNC_READ, 2, data, 4, _modbus_cb); +} + + +static void _ctrl_write(hy_ctrl_state_t state) { + uint8_t data[2] = {1, state}; + modbus_func(HUANYANG_CTRL_WRITE, 2, data, 2, _modbus_cb); +} + + +static void _ctrl_read(hy_ctrl_addr_t addr) { + uint8_t data[2] = {1, addr}; + modbus_func(HUANYANG_CTRL_READ, 2, data, 4, _modbus_cb); +} + + +static void _freq_write(uint16_t freq) { + uint8_t data[3] = {2, (uint8_t)(freq >> 8), (uint8_t)freq}; + modbus_func(HUANYANG_FREQ_WRITE, 3, data, 3, _modbus_cb); +} + + +static void _next_command() { + switch (hy.state) { + case 0: { // Update direction + hy_ctrl_state_t state = HUANYANG_STOP; + if (!hy.shutdown && !estop_triggered()) { + if (0 < hy.power) + state = (hy_ctrl_state_t)(HUANYANG_RUN | HUANYANG_FORWARD); + else if (hy.power < 0) + state = (hy_ctrl_state_t)(HUANYANG_RUN | HUANYANG_REV_FWD); + } + + _ctrl_write(state); + break; + } + + case 1: _func_read(HY_PD005_MAX_FREQUENCY); break; + case 2: _func_read(HY_PD011_FREQUENCY_LOWER_LIMIT); break; + case 3: _func_read(HY_PD144_RATED_MOTOR_RPM); break; + + case 4: { // Update freqency + // Compute frequency in Hz + float freq = fabs(hy.power * hy.max_freq); + + // Frequency write command + _freq_write(freq * 100); + break; + } + + case 5: _ctrl_read(HUANYANG_ACTUAL_FREQ); break; + case 6: _ctrl_read(HUANYANG_ACTUAL_CURRENT); break; + case 7: _ctrl_read(HUANYANG_ACTUAL_RPM); break; + case 8: _ctrl_read(HUANYANG_TEMPERATURE); break; + } +} + + +void huanyang_init() { + modbus_init(); + memset(&hy, 0, sizeof(hy)); + _next_command(); +} + + +void huanyang_deinit(deinit_cb_t cb) { + hy.deinit_cb = cb; + hy.shutdown = true; +} + + +void huanyang_set(float power) { + if (hy.power != power && !hy.shutdown) + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + hy.power = power; + hy.changed = true; + } +} + + +float huanyang_get() {return hy.actual_freq / hy.max_freq;} +uint8_t huanyang_get_status() {return hy.status;} + +// Variable callbacks +float get_hy_freq() {return hy.actual_freq;} +float get_hy_current() {return hy.actual_current;} +uint16_t get_hy_temp() {return hy.temperature;} +float get_hy_max_freq() {return hy.max_freq;} +float get_hy_min_freq() {return hy.min_freq;} +uint16_t get_hy_rated_rpm() {return hy.rated_rpm;} diff --git a/src/avr/src/huanyang.h b/src/avr/src/huanyang.h new file mode 100644 index 0000000..ccad449 --- /dev/null +++ b/src/avr/src/huanyang.h @@ -0,0 +1,226 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "spindle.h" + + +void huanyang_init(); +void huanyang_deinit(deinit_cb_t cb); +void huanyang_set(float power); +float huanyang_get(); +uint8_t huanyang_get_status(); + + +/// See Huanyang VFD user manual +typedef enum { + HY_PD000_PARAMETER_LOCK, + HY_PD001_SOURCE_OF_OPERATION_COMMANDS, + HY_PD002_SOURCE_OF_OPERATING_FREQUENCY, + HY_PD003_MAIN_FREQUENCY, + HY_PD004_BASE_FREQUENCY, + HY_PD005_MAX_FREQUENCY, + HY_PD006_INTERMEDIATE_FREQUENCY, + HY_PD007_MIN_FREQUENCY, + HY_PD008_MAX_VOLTAGE, + HY_PD009_INTERMEDIATE_VOLTAGE, + HY_PD010_MIN_VOLTAGE, + HY_PD011_FREQUENCY_LOWER_LIMIT, + HY_PD012_RESERVED, + HY_PD013_PARAMETER_RESET, + HY_PD014_ACCEL_TIME_1, + HY_PD015_DECEL_TIME_1, + HY_PD016_ACCEL_TIME_2, + HY_PD017_DECEL_TIME_2, + HY_PD018_ACCEL_TIME_3, + HY_PD019_DECEL_TIME_3, + HY_PD020_ACCEL_TIME_4, + HY_PD021_DECEL_TIME_4, + HY_PD022_FACTORY_RESERVED, + HY_PD023_REV_ROTATION_SELECT, + HY_PD024_STOP_KEY, + HY_PD025_STARTING_MODE, + HY_PD026_STOPPING_MODE, + HY_PD027_STARTING_FREQUENCY, + HY_PD028_STOPPING_FREQUENCY, + HY_PD029_DC_BRAKING_TIME_AT_START, + HY_PD030_DC_BRAKING_TIME_AT_STOP, + HY_PD031_DC_BRAKING_VOLTAGE_LEVEL, + HY_PD032_FREQUENCY_TRACK_TIME, + HY_PD033_CURRENT_LEVEL_FOR_FREQUENCY_TRACK, + HY_PD034_VOLTAGE_RISE_TIME_FOR_FREQUENCY_TRACK, + HY_PD035_FREQUENCY_STEP_LENGTH, + HY_PD036, + HY_PD037, + HY_PD038, + HY_PD039, + HY_PD040, + HY_PD041_CARRIER_FREQUENCY, + HY_PD042_JOGGING_FREQUENCY, + HY_PD043_S_CURVE_TIME, + HY_PD044_MULTI_INPUT_FOR, + HY_PD045_MULTI_INPUT_REV, + HY_PD046_MULTI_INPUT_RST, + HY_PD047_MULTI_INPUT_SPH, + HY_PD048_MULTI_INPUT_SPM, + HY_PD049_MULTI_INPUT_SPL, + HY_PD050_MULTI_OUTPUT_DRV, + HY_PD051_MULTI_OUTPUT_UPF, + HY_PD052_MULTI_OUTPUT_FA_FB_FC, + HY_PD053_MULTI_OUTPUT_KA_KB, + HY_PD054_MULTI_OUTPUT_AM, + HY_PD055_AM_ANALOG_OUTPUT_GAIN, + HY_PD056_SKIP_FREQUENCY_1, + HY_PD057_SKIP_FREQUENCY_2, + HY_PD058_SKIP_FREQUENCY_3, + HY_PD059_SKIP_FREQUENCY_RANGE, + HY_PD060_UNIFORM_FREQUENCY_1, + HY_PD061_UNIFORM_FREQUENCY_2, + HY_PD062_UNIFORM_FREQUENCY_RANGE, + HY_PD063_TIMER_1_TIME, + HY_PD064_TIMER_2_TIME, + HY_PD065_COUNTING_VALUE, + HY_PD066_INTERMEDIATE_COUNTER, + HY_PD067, + HY_PD068, + HY_PD069, + HY_PD070_ANALOG_INPUT, + HY_PD071_ANALOG_FILTERING_CONSTANT, + HY_PD072_HIGHER_ANALOG_FREQUENCY, + HY_PD073_LOWER_ANALOG_FREQUENCY, + HY_PD074_BIAS_DIRECTION_AT_HIGHER_FREQUENCY, + HY_PD075_BIAS_DIRECTION_AT_LOWER_FREQUENCY, + HY_PD076_ANALOG_NEGATIVE_BIAS_REVERSE, + HY_PD077_UP_DOWN_FUNCTION, + HY_PD078_UP_DOWN_SPEED, + HY_PD079, + HY_PD080_PLC_OPERATION, + HY_PD081_AUTO_PLC, + HY_PD082_PLC_RUNNING_DIRECTION, + HY_PD083, + HY_PD084_PLC_RAMP_TIME, + HY_PD085, + HY_PD086_FREQUENCY_2, + HY_PD087_FREQUENCY_3, + HY_PD088_FREQUENCY_4, + HY_PD089_FREQUENCY_5, + HY_PD090_FREQUENCY_6, + HY_PD091_FREQUENCY_7, + HY_PD092_FREQUENCY_8, + HY_PD093, + HY_PD094, + HY_PD095, + HY_PD096, + HY_PD097, + HY_PD098, + HY_PD099, + HY_PD100, + HY_PD101_TIMER_1, + HY_PD102_TIMER_2, + HY_PD103_TIMER_3, + HY_PD104_TIMER_4, + HY_PD105_TIMER_5, + HY_PD106_TIMER_6, + HY_PD107_TIMER_7, + HY_PD108_TIMER_8, + HY_PD109, + HY_PD110, + HY_PD111, + HY_PD112, + HY_PD113, + HY_PD114, + HY_PD115, + HY_PD116, + HY_PD117_AUTOPLC_MEMORY_FUNCTION, + HY_PD118_OVER_VOLTAGE_STALL_PREVENTION, + HY_PD119_STALL_PREVENTION_LEVEL_AT_RAMP_UP, + HY_PD120_STALL_PREVENTION_LEVEL_AT_CONSTANT_SPEED, + HY_PD121_DECEL_TIME_FOR_STALL_PREVENTION_AT_CONSTANT_SPEED, + HY_PD122_STALL_PREVENTION_LEVEL_AT_DECELERATION, + HY_PD123_OVER_TORQUE_DETECT_MODE, + HY_PD124_OVER_TORQUE_DETECT_LEVEL, + HY_PD125_OVER_TORQUE_DETECT_TIME, + HY_PD126, + HY_PD127, + HY_PD128, + HY_PD129, + HY_PD130_NUMBER_OF_AUXILIARY_PUMP, + HY_PD131_CONTINUOUS_RUNNING_TIME_OF_AUXILIARY_PUMPS, + HY_PD132_INTERLOCKING_TIME_OF_AUXILIARY_PUMP, + HY_PD133_HIGH_SPEED_RUNNING_TIME, + HY_PD134_LOW_SPEED_RUNNING_TIME, + HY_PD135_STOPPING_VOLTAGE_LEVEL, + HY_PD136_LASTING_TIME_OF_STOPPING_VOLTAGE_LEVEL, + HY_PD137_WAKEUP_VOLTAGE_LEVEL, + HY_PD138_SLEEP_FREQUENCY, + HY_PD139_LASTING_TIME_OF_SLEEP_FREQUENCY, + HY_PD140, + HY_PD141_RATED_MOTOR_VOLTAGE, + HY_PD142_RATED_MOTOR_CURRENT, + HY_PD143_MOTOR_POLE_NUMBER, + HY_PD144_RATED_MOTOR_RPM, + HY_PD145_AUTO_TORQUE_COMPENSATION, + HY_PD146_MOTOR_NO_LOAD_CURRENT, + HY_PD147_MOTOR_SLIP_COMPENSATION, + HY_PD148, + HY_PD149, + HY_PD150_AUTO_VOLTAGE_REGULATION, + HY_PD151_AUTO_ENERGY_SAVING, + HY_PD152_FAULT_RESTART_TIME, + HY_PD153_RESTART_AFTER_INSTANTANEOUS_STOP, + HY_PD154_ALLOWABLE_POWER_BREAKDOWN_TIME, + HY_PD155_NUMBER_OF_ABNORMAL_RESTART, + HY_PD156_PROPORTIONAL_CONSTANT, + HY_PD157_INTEGRAL_TIME, + HY_PD158_DIFFERENTIAL_TIME, + HY_PD159_TARGET_VALUE, + HY_PD160_PID_TARGET_VALUE, + HY_PD161_PID_UPPER_LIMIT, + HY_PD162_PID_LOWER_LIMIT, + HY_PD163_COMMUNICATION_ADDRESSES, + HY_PD164_COMMUNICATION_BAUD_RATE, + HY_PD165_COMMUNICATION_DATA_METHOD, + HY_PD166, + HY_PD167, + HY_PD168, + HY_PD169, + HY_PD170_DISPLAY_ITEMS, + HY_PD171_DISPLAY_ITEMS_OPEN, + HY_PD172_FAULT_CLEAR, + HY_PD173, + HY_PD174_RATED_CURRENT_OF_INVERTER, + HY_PD175_INVERTER_MODEL, + HY_PD176_INVERTER_FREQUENCY_STANDARD, + HY_PD177_FAULT_RECORD_1, + HY_PD178_FAULT_RECORD_2, + HY_PD179_FAULT_RECORD_3, + HY_PD180_FAULT_RECORD_4, + HY_PD181_SOFTWARE_VERSION, + HY_PD182_MANUFACTURE_DATE, + HY_PD183_SERIAL_NO, +} hy_addr_t; diff --git a/src/avr/src/i2c.c b/src/avr/src/i2c.c new file mode 100644 index 0000000..d115a1b --- /dev/null +++ b/src/avr/src/i2c.c @@ -0,0 +1,129 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "i2c.h" + +#include + +#include + + +typedef struct { + i2c_read_cb_t read_cb; + i2c_write_cb_t write_cb; + uint8_t data[I2C_MAX_DATA + 1]; + uint8_t length; + bool done; + bool write; +} i2c_t; + +static i2c_t i2c = {0}; + + +static void _i2c_reset_command() { + i2c.length = 0; + i2c.done = true; + i2c.write = false; +} + + +static void _i2c_end_command() { + if (i2c.length && !i2c.write && i2c.read_cb) { + i2c.data[i2c.length] = 0; // Null terminate + i2c.read_cb(i2c.data, i2c.length); + } + + _i2c_reset_command(); +} + + +static void _i2c_command_byte(uint8_t byte) { + i2c.data[i2c.length++] = byte; +} + + +ISR(I2C_ISR) { + uint8_t status = I2C_DEV.SLAVE.STATUS; + + // Error or collision + if (status & (TWI_SLAVE_BUSERR_bm | TWI_SLAVE_COLL_bm)) { + _i2c_reset_command(); + return; // Ignore + + } else if ((status & TWI_SLAVE_APIF_bm) && (status & TWI_SLAVE_AP_bm)) { + // START + address match + I2C_DEV.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; // ACK address byte + _i2c_end_command(); // Handle repeated START + + } else if (status & TWI_SLAVE_APIF_bm) { + // STOP + I2C_DEV.SLAVE.STATUS = TWI_SLAVE_APIF_bm; // Clear interrupt flag + _i2c_end_command(); + + } else if (status & TWI_SLAVE_DIF_bm) { + i2c.write = status & TWI_SLAVE_DIR_bm; + + // DATA + if (i2c.write) { // Write + // Check if master ACKed last byte sent + if (i2c.length && (status & TWI_SLAVE_RXACK_bm || i2c.done)) + I2C_DEV.SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; // End transaction + + else { + // Send some data + i2c.done = false; + I2C_DEV.SLAVE.DATA = i2c.write_cb(i2c.length++, &i2c.done); + I2C_DEV.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; // Continue transaction + } + + } else { // Read + _i2c_command_byte(I2C_DEV.SLAVE.DATA); + + // ACK and continue transaction + I2C_DEV.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; + } + } +} + + +static uint8_t _i2c_default_write_cb(uint8_t offset, bool *done) { + *done = true; + return 0; +} + + +void i2c_init() { + i2c_set_write_callback(_i2c_default_write_cb); + + I2C_DEV.SLAVE.CTRLA = TWI_SLAVE_INTLVL_LO_gc | TWI_SLAVE_DIEN_bm | + TWI_SLAVE_ENABLE_bm | TWI_SLAVE_APIEN_bm | TWI_SLAVE_PIEN_bm; + I2C_DEV.SLAVE.ADDR = I2C_ADDR << 1; +} + + +void i2c_set_read_callback(i2c_read_cb_t cb) {i2c.read_cb = cb;} +void i2c_set_write_callback(i2c_write_cb_t cb) {i2c.write_cb = cb;} diff --git a/src/avr/src/i2c.h b/src/avr/src/i2c.h new file mode 100644 index 0000000..7cd55ca --- /dev/null +++ b/src/avr/src/i2c.h @@ -0,0 +1,41 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "config.h" + +#include +#include + +typedef void (*i2c_read_cb_t)(uint8_t *data, uint8_t length); +typedef uint8_t (*i2c_write_cb_t)(uint8_t offset, bool *done); + + +void i2c_init(); +void i2c_set_read_callback(i2c_read_cb_t cb); +void i2c_set_write_callback(i2c_write_cb_t cb); diff --git a/src/avr/src/io.c b/src/avr/src/io.c new file mode 100644 index 0000000..94ffcda --- /dev/null +++ b/src/avr/src/io.c @@ -0,0 +1,118 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "io.h" + +#include "status.h" +#include "util.h" +#include "command.h" +#include "exec.h" +#include "rtc.h" +#include "analog.h" + +#include +#include +#include + + +typedef struct { + int8_t port; + bool digital; + input_mode_t mode; + float timeout; +} input_cmd_t; + + +static input_cmd_t active_cmd = {-1,}; +static uint32_t timeout; + + +void io_callback() { + if (active_cmd.port == -1) return; + + bool done = false; + float result = 0; + if (active_cmd.mode == INPUT_IMMEDIATE || rtc_expired(timeout)) done = true; + + // TODO handle modes + + if (done) { + if (active_cmd.digital) { // TODO + } else result = analog_get(active_cmd.port); + + printf_P(PSTR("{\"result\": %f}\n"), (double)result); + active_cmd.port = -1; + } +} + + +static stat_t _exec_cb() { + if (active_cmd.port == -1) exec_set_cb(0); + return STAT_NOP; +} + + +// Command callbacks +stat_t command_input(char *cmd) { + input_cmd_t input_cmd; + cmd++; // Skip command + + // Analog or digital + if (*cmd == 'd') input_cmd.digital = true; + else if (*cmd == 'a') input_cmd.digital = false; + else return STAT_INVALID_ARGUMENTS; + cmd++; + + // Port index + if (!isdigit(*cmd)) return STAT_INVALID_ARGUMENTS; + input_cmd.port = *cmd - '0'; + cmd++; + + // Mode + if (!isdigit(*cmd)) return STAT_INVALID_ARGUMENTS; + input_cmd.mode = (input_mode_t)(*cmd - '0'); + if (INPUT_LOW < input_cmd.mode) return STAT_INVALID_ARGUMENTS; + cmd++; + + // Timeout + if (!decode_float(&cmd, &input_cmd.timeout)) return STAT_BAD_FLOAT; + + command_push(COMMAND_input, &input_cmd); + + return STAT_OK; +} + + +unsigned command_input_size() {return sizeof(input_cmd_t);} + + +void command_input_exec(void *data) { + active_cmd = *(input_cmd_t *)data; + + timeout = rtc_get_time() + active_cmd.timeout * 1000; + exec_set_cb(_exec_cb); +} diff --git a/src/avr/src/io.h b/src/avr/src/io.h new file mode 100644 index 0000000..1f8948b --- /dev/null +++ b/src/avr/src/io.h @@ -0,0 +1,40 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +typedef enum { + INPUT_IMMEDIATE, + INPUT_RISE, + INPUT_FALL, + INPUT_HIGH, + INPUT_LOW, +} input_mode_t; + + +void io_callback(); diff --git a/src/avr/src/jog.c b/src/avr/src/jog.c new file mode 100644 index 0000000..64df035 --- /dev/null +++ b/src/avr/src/jog.c @@ -0,0 +1,169 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "jog.h" + +#include "axis.h" +#include "util.h" +#include "exec.h" +#include "state.h" +#include "command.h" +#include "config.h" +#include "SCurve.h" + +#include +#include +#include +#include +#include + + +typedef struct { + bool holding; + bool writing; + + SCurve scurves[AXES]; + float next[AXES]; + float targetV[AXES]; +} jr_t; + +jr_t jr = {0}; + + +stat_t jog_exec() { + bool done = true; + + // Compute per axis velocities and target positions + float target[AXES] = {0,}; + float velocity_sqr = 0; + + for (int axis = 0; axis < AXES; axis++) { + if (!axis_is_enabled(axis)) continue; + + // Load next velocity + if (!jr.writing) + jr.targetV[axis] = jr.next[axis] * axis_get_velocity_max(axis); + + float p = exec_get_axis_position(axis); + float vel = jr.scurves[axis].getVelocity(); + float targetV = jr.targetV[axis]; + float min = axis_get_soft_limit(axis, true); + float max = axis_get_soft_limit(axis, false); + bool softLimited = min != max && axis_get_homed(axis); + + // Apply soft limits, if enabled and homed + if (softLimited && MIN_VELOCITY < fabs(targetV)) { + float dist = jr.scurves[axis].getStoppingDist() * + (1 + (JOG_STOPPING_UNDERSHOOT / 100.0)); + + if (vel < 0 && p - dist <= min) targetV = -MIN_VELOCITY; + if (0 < vel && max <= p + dist) targetV = MIN_VELOCITY; + } + + // Compute next velocity + float v = jr.scurves[axis].next(SEGMENT_TIME, targetV); + + // Don't overshoot soft limits + float deltaP = v * SEGMENT_TIME; + if (softLimited && 0 < deltaP && max < p + deltaP) p = max; + else if (softLimited && deltaP < 0 && p + deltaP < min) p = min; + else p += deltaP; + + // Not done jogging if still moving + if (MIN_VELOCITY < fabs(v) || MIN_VELOCITY < fabs(targetV)) done = false; + + velocity_sqr += square(v); + target[axis] = p; + } + + // Check if we are done + if (done) { + command_reset_position(); + exec_set_velocity(0); + exec_set_cb(0); + if (jr.holding) state_holding(); + else state_idle(); + + return STAT_NOP; // Done, no move executed + } + + // Set velocity and target + exec_set_velocity(sqrt(velocity_sqr)); + exec_move_to_target(target); + + return STAT_OK; +} + + +void jog_stop() { + if (state_get() != STATE_JOGGING) return; + jr.writing = true; + for (int axis = 0; axis < AXES; axis++) jr.next[axis] = 0; + jr.writing = false; +} + + +stat_t command_jog(char *cmd) { + // Ignore jog commands when not READY, HOLDING or JOGGING + if (state_get() != STATE_READY && state_get() != STATE_HOLDING && + state_get() != STATE_JOGGING) + return STAT_NOP; + + // Skip over command code + cmd++; + + // Get velocities + float velocity[AXES] = {0,}; + stat_t status = decode_axes(&cmd, velocity); + if (status) return status; + + // Check for end of command + if (*cmd) return STAT_INVALID_ARGUMENTS; + + // Start jogging + if (state_get() != STATE_JOGGING) { + memset(&jr, 0, sizeof(jr)); + + jr.holding = state_get() == STATE_HOLDING; + + for (int axis = 0; axis < AXES; axis++) + if (axis_is_enabled(axis)) + jr.scurves[axis] = + SCurve(axis_get_velocity_max(axis), axis_get_accel_max(axis), + axis_get_jerk_max(axis)); + + state_jogging(); + exec_set_cb(jog_exec); + } + + // Set next velocities + jr.writing = true; + for (int axis = 0; axis < AXES; axis++) jr.next[axis] = velocity[axis]; + jr.writing = false; + + return STAT_OK; +} diff --git a/src/avr/src/jog.h b/src/avr/src/jog.h new file mode 100644 index 0000000..ea093ab --- /dev/null +++ b/src/avr/src/jog.h @@ -0,0 +1,34 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "status.h" + + +stat_t jog_exec(); +void jog_stop(); diff --git a/src/avr/src/lcd.c b/src/avr/src/lcd.c new file mode 100644 index 0000000..7802eb4 --- /dev/null +++ b/src/avr/src/lcd.c @@ -0,0 +1,143 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "lcd.h" +#include "rtc.h" +#include "hardware.h" +#include "command.h" + +#include +#include +#include + +#include + + +void lcd_init(uint8_t addr) { + // Enable I2C master + TWIC.MASTER.BAUD = F_CPU / 2 / 100000 - 5; // 100 KHz + TWIC.MASTER.CTRLA = TWI_MASTER_ENABLE_bm; + TWIC.MASTER.CTRLB = TWI_MASTER_TIMEOUT_DISABLED_gc; + TWIC.MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc; + + _delay_ms(50); + lcd_nibble(addr, 3 << 4); // Home + _delay_ms(50); + lcd_nibble(addr, 3 << 4); // Home + _delay_ms(50); + lcd_nibble(addr, 3 << 4); // Home + lcd_nibble(addr, 2 << 4); // 4-bit + + lcd_write(addr, + LCD_FUNCTION_SET | LCD_2_LINE | LCD_5x8_DOTS | LCD_4_BIT_MODE, 0); + lcd_write(addr, LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON, 0); + lcd_write(addr, LCD_ENTRY_MODE_SET | LCD_ENTRY_SHIFT_INC, 0); + + lcd_write(addr, LCD_CLEAR_DISPLAY, 0); + lcd_write(addr, LCD_RETURN_HOME, 0); +} + + +static void _master_wait() { +#ifdef __AVR__ + while (!(TWIC.MASTER.STATUS & TWI_MASTER_WIF_bm)) continue; +#endif +} + + +static void _write_i2c(uint8_t addr, uint8_t data) { + data |= BACKLIGHT_BIT; + + TWIC.MASTER.ADDR = addr << 1; + _master_wait(); + + TWIC.MASTER.DATA = data; + _master_wait(); + + TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc; + + _delay_us(100); +} + + +void lcd_nibble(uint8_t addr, uint8_t data) { + _write_i2c(addr, data); + _write_i2c(addr, data | ENABLE_BIT); + _delay_us(500); + _write_i2c(addr, data & ~ENABLE_BIT); + _delay_us(100); +} + + +void lcd_write(uint8_t addr, uint8_t cmd, uint8_t flags) { + lcd_nibble(addr, flags | (cmd & 0xf0)); + lcd_nibble(addr, flags | ((cmd << 4) & 0xf0)); +} + + +void lcd_goto(uint8_t addr, uint8_t x, uint8_t y) { + static uint8_t row[] = {0, 64, 20, 84}; + lcd_write(addr, LCD_SET_DDRAM_ADDR | (row[y] + x), 0); +} + + +void lcd_putchar(uint8_t addr, uint8_t c) { + lcd_write(addr, c, REG_SELECT_BIT); +} + + +void lcd_pgmstr(uint8_t addr, const char *s) { + while (true) { + char c = pgm_read_byte(s++); + if (!c) break; + lcd_putchar(addr, c); + } +} + + +void _splash(uint8_t addr) { + lcd_init(addr); + lcd_goto(addr, 1, 1); + lcd_pgmstr(addr, PSTR("Controller booting")); + lcd_goto(addr, 3, 2); + lcd_pgmstr(addr, PSTR("Please wait...")); +} + + +void lcd_splash() { + wdt_disable(); + _splash(0x27); + _splash(0x3f); + wdt_enable(WDTO_250MS); +} + + +void lcd_rtc_callback() { + // Display the splash if we haven't gotten any commands in 1sec since boot + if (!command_is_active() && rtc_get_time() == 1000) + lcd_splash(); +} diff --git a/src/avr/src/lcd.h b/src/avr/src/lcd.h new file mode 100644 index 0000000..b7369f0 --- /dev/null +++ b/src/avr/src/lcd.h @@ -0,0 +1,103 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "pgmspace.h" + +#include + + +// Control flags +enum { + REG_SELECT_BIT = 1 << 0, + READ_BIT = 1 << 1, + ENABLE_BIT = 1 << 2, + BACKLIGHT_BIT = 1 << 3, +}; + + +// Commands +enum { + LCD_CLEAR_DISPLAY = 1 << 0, + LCD_RETURN_HOME = 1 << 1, + LCD_ENTRY_MODE_SET = 1 << 2, + LCD_DISPLAY_CONTROL = 1 << 3, + LCD_CURSOR_SHIFT = 1 << 4, + LCD_FUNCTION_SET = 1 << 5, + LCD_SET_CGRAM_ADDR = 1 << 6, + LCD_SET_DDRAM_ADDR = 1 << 7, +}; + + +// Entry Mode Set flags +#define LCD_ENTRY_SHIFT_DISPLAY (1 << 0) +#define LCD_ENTRY_SHIFT_INC (1 << 1) +#define LCD_ENTRY_SHIFT_DEC (0 << 1) + + +// Display Control flags +#define LCD_BLINK_ON (1 << 0) +#define LCD_BLINK_OFF (0 << 0) +#define LCD_CURSOR_ON (1 << 1) +#define LCD_CURSOR_OFF (0 << 1) +#define LCD_DISPLAY_ON (1 << 2) +#define LCD_DISPLAY_OFF (0 << 2) + + +// Cursor Shift flags +#define LCD_SHIFT_RIGHT (1 << 2) +#define LCD_SHIFT_LEFT (0 << 2) +#define LCD_SHIFT_DISPLAY (1 << 3) +#define LCD_SHIFT_CURSOR (0 << 3) + + +// Function Set flags +#define LCD_5x11_DOTS (1 << 2) +#define LCD_5x8_DOTS (0 << 2) +#define LCD_2_LINE (1 << 3) +#define LCD_1_LINE (0 << 3) +#define LCD_8_BIT_MODE (1 << 4) +#define LCD_4_BIT_MODE (0 << 4) + + +// Text justification flags +enum { + JUSTIFY_LEFT = 0, + JUSTIFY_RIGHT = 1, + JUSTIFY_CENTER = 2, +}; + + +void lcd_init(uint8_t addr); +void lcd_nibble(uint8_t addr, uint8_t data); +void lcd_write(uint8_t addr, uint8_t cmd, uint8_t flags); +void lcd_goto(uint8_t addr, uint8_t x, uint8_t y); +void lcd_putchar(uint8_t addr, uint8_t c); +void lcd_pgmstr(uint8_t addr, const char *s); +void lcd_splash(); +void lcd_rtc_callback(); diff --git a/src/avr/src/line.c b/src/avr/src/line.c new file mode 100644 index 0000000..132c14f --- /dev/null +++ b/src/avr/src/line.c @@ -0,0 +1,274 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "config.h" +#include "exec.h" +#include "command.h" +#include "spindle.h" +#include "util.h" +#include "SCurve.h" + +#include +#include +#include + + +typedef struct { + float start[AXES]; + float target[AXES]; + float times[7]; + float target_vel; + float max_accel; + float max_jerk; + + float unit[AXES]; + float length; +} line_t; + + +static struct { + line_t line; + + int section; + int seg; + + float iD; // Initial section distance + float iV; // Initial section velocity + float iA; // Initial section acceleration + float jerk; + float lV; // Last velocity + float lD; // Last distance + + power_update_t power_updates[POWER_MAX_UPDATES]; +} l; + + +static void _segment_target(float target[AXES], float d) { + for (int axis = 0; axis < AXES; axis++) + target[axis] = l.line.start[axis] + l.line.unit[axis] * d; +} + + +static float _segment_distance(float t) { + return l.iD + SCurve::distance(t, l.iV, l.iA, l.jerk); +} + + +static float _segment_velocity(float t) { + return l.iV + SCurve::velocity(t, l.iA, l.jerk); +} + + +static float _segment_accel(float t) { + return l.iA + SCurve::acceleration(t, l.jerk); +} + + +static bool _section_next() { + while (++l.section < 7) { + if (!l.line.times[l.section]) continue; + + // Jerk + switch (l.section) { + case 0: case 6: l.jerk = l.line.max_jerk; break; + case 2: case 4: l.jerk = -l.line.max_jerk; break; + default: l.jerk = 0; + } + exec_set_jerk(l.jerk); + + // Acceleration + switch (l.section) { + case 1: case 2: l.iA = l.line.max_jerk * l.line.times[0]; break; + case 5: case 6: l.iA = -l.line.max_jerk * l.line.times[4]; break; + default: l.iA = 0; + } + + return true; + } + + return false; +} + + +static stat_t _exec_segment(float time, const float target[], float vel, + float accel) { + return exec_segment(time, target, vel, accel, l.line.max_accel, + l.line.max_jerk, l.power_updates); +} + + +static stat_t _line_exec() { + // Compute times + float section_time = l.line.times[l.section]; + float seg_time = SEGMENT_TIME; + float t = ++l.seg * SEGMENT_TIME; + + // Don't exceed section time + if (section_time < t) { + seg_time = section_time - (l.seg - 1) * SEGMENT_TIME; + t = section_time; + } + + // Compute distance and velocity + float d = _segment_distance(t); + float v = _segment_velocity(t); + float a = _segment_accel(t); + + // Don't allow overshoot + if (l.line.length < d) d = l.line.length; + + // Handle synchronous speeds + spindle_load_power_updates(l.power_updates, l.lD, d); + l.lD = d; + + // Check if section complete + if (t == section_time) { + if (_section_next()) { + // Setup next section + l.seg = 0; + l.iD = d; + l.iV = v; + + } else { + exec_set_cb(0); + + // Last segment of last section + // Use exact target values to correct for floating-point errors + return _exec_segment(seg_time, l.line.target, l.line.target_vel, a); + } + } + + // Compute target position from distance + float target[AXES]; + _segment_target(target, d); + + // Segment move + return _exec_segment(seg_time, target, v, a); +} + + +stat_t command_line(char *cmd) { + line_t line = {}; + + cmd++; // Skip command code + + // Get start position + command_get_position(line.start); + + // Get target velocity + if (!decode_float(&cmd, &line.target_vel)) return STAT_BAD_FLOAT; + if (line.target_vel < 0) return STAT_INVALID_ARGUMENTS; + + // Get max accel + if (!decode_float(&cmd, &line.max_accel)) return STAT_BAD_FLOAT; + if (line.max_accel < 0) return STAT_INVALID_ARGUMENTS; + + // Get max jerk + if (!decode_float(&cmd, &line.max_jerk)) return STAT_BAD_FLOAT; + if (line.max_jerk < 0) return STAT_INVALID_ARGUMENTS; + + // Get target position + copy_vector(line.target, line.start); + stat_t status = decode_axes(&cmd, line.target); + if (status) return status; + + // Get times + bool has_time = false; + while (*cmd) { + if (*cmd < '0' || '6' < *cmd) break; + int section = *cmd - '0'; + cmd++; + + float time; + if (!decode_float(&cmd, &time)) return STAT_BAD_FLOAT; + + if (time < 0) return STAT_NEGATIVE_SCURVE_TIME; + line.times[section] = time; + if (time) has_time = true; + } + + if (!has_time) return STAT_ALL_ZERO_SCURVE_TIMES; + + // Check for end of command + if (*cmd) return STAT_INVALID_ARGUMENTS; + + // Set next start position + command_set_position(line.target); + + // Compute direction vector + for (int axis = 0; axis < AXES; axis++) { + line.unit[axis] = line.target[axis] - line.start[axis]; + line.length += line.unit[axis] * line.unit[axis]; + } + + line.length = sqrt(line.length); + for (int axis = 0; axis < AXES; axis++) + if (line.unit[axis]) line.unit[axis] /= line.length; + + // Queue + command_push(COMMAND_line, &line); + + return STAT_OK; +} + + +unsigned command_line_size() {return sizeof(line_t);} + + +void command_line_exec(void *data) { + l.line = *(line_t *)data; + + // Setup first section + l.seg = 0; + l.iD = 0; + l.lD = 0; + // If current velocity is non-zero use last target velocity + l.iV = exec_get_velocity() ? l.lV : 0; + l.lV = l.line.target_vel; + + // Find first section + l.section = -1; + if (!_section_next()) return; + +#if 0 + // Compare start position to actual position + float diff[AXES]; + bool report = false; + exec_get_position(diff); + for (int i = 0; i < AXES; i++) { + diff[i] -= l.line.start[i]; + if (0.1 < fabs(diff[i])) report = true; + } + + if (report) + STATUS_DEBUG("diff: %.4f %.4f %.4f %.4f", + diff[0], diff[1], diff[2], diff[3]); +#endif + + // Set callback + exec_set_cb(_line_exec); +} diff --git a/src/avr/src/main.c b/src/avr/src/main.c new file mode 100644 index 0000000..21a7296 --- /dev/null +++ b/src/avr/src/main.c @@ -0,0 +1,101 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "hardware.h" +#include "stepper.h" +#include "motor.h" +#include "switch.h" +#include "usart.h" +#include "drv8711.h" +#include "vars.h" +#include "rtc.h" +#include "report.h" +#include "command.h" +#include "estop.h" +#include "i2c.h" +#include "pgmspace.h" +#include "outputs.h" +#include "analog.h" +#include "modbus.h" +#include "io.h" +#include "exec.h" +#include "state.h" +#include "emu.h" + +#include + +#include +#include + + +// For emu +int __argc; +char **__argv; + + +int main(int argc, char *argv[]) { + __argc = argc; + __argv = argv; + + wdt_enable(WDTO_250MS); + + // Init + cli(); // disable interrupts + + emu_init(); // Init emulator + hw_init(); // hardware setup - must be first + outputs_init(); // output pins + switch_init(); // switches + estop_init(); // emergency stop handler + analog_init(); // analog input pins + usart_init(); // serial port + i2c_init(); // i2c port + drv8711_init(); // motor drivers + stepper_init(); // steppers + motor_init(); // motors + exec_init(); // motion exec + vars_init(); // configuration variables + command_init(); // command queue + + sei(); // enable interrupts + + // Splash + printf_P(PSTR("\n{\"firmware\":\"Buildbotics AVR\"}\n")); + + // Main loop + while (true) { + emu_callback(); // Emulator callback + hw_reset_handler(); // handle hard reset requests + state_callback(); // manage state + command_callback(); // process next command + modbus_callback(); // handle modbus events + io_callback(); // handle io input + report_callback(); // report changes + } + + return 0; +} diff --git a/src/avr/src/messages.def b/src/avr/src/messages.def new file mode 100644 index 0000000..2f9aa33 --- /dev/null +++ b/src/avr/src/messages.def @@ -0,0 +1,62 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +STAT_MSG(OK, "OK") +STAT_MSG(AGAIN, "Run command again") +STAT_MSG(NOP, "No op") +STAT_MSG(INTERNAL_ERROR, "Internal error") +STAT_MSG(ESTOP_USER, "User triggered EStop") +STAT_MSG(ESTOP_SWITCH, "Switch triggered EStop") +STAT_MSG(POWER_SHUTDOWN, "Power shutdown") +STAT_MSG(UNRECOGNIZED_NAME, "Unrecognized command or variable name") +STAT_MSG(INVALID_COMMAND, "Invalid command") +STAT_MSG(INVALID_ARGUMENTS, "Invalid arguments") +STAT_MSG(TOO_MANY_ARGUMENTS, "Too many arguments") +STAT_MSG(TOO_FEW_ARGUMENTS, "Too few arguments") +STAT_MSG(MACHINE_ALARMED, "Machine alarmed - Command not processed") +STAT_MSG(EXPECTED_MOVE, "A move expected but none queued") +STAT_MSG(BAD_FLOAT, "Failed to parse float") +STAT_MSG(BAD_INT, "Failed to parse integer") +STAT_MSG(INVALID_VALUE, "Invalid value") +STAT_MSG(INVALID_TYPE, "Invalid type") +STAT_MSG(READ_ONLY, "Variable is read only") +STAT_MSG(ALL_ZERO_SCURVE_TIMES, "All zero s-curve times") +STAT_MSG(NEGATIVE_SCURVE_TIME, "Negative s-curve time") +STAT_MSG(SEEK_NOT_ENABLED, "Switch not enabled") +STAT_MSG(SEEK_NOT_FOUND, "Switch not found") +STAT_MSG(MOTOR_ID_INVALID, "Invalid motor ID") +STAT_MSG(MOTOR_NOT_PREPPED, "Motor move not prepped") +STAT_MSG(MOTOR_NOT_READY, "Motor not ready for move") +STAT_MSG(MOTOR_FAULT, "Motor fault") +STAT_MSG(STEPPER_NULL_MOVE, "Null move in stepper driver") +STAT_MSG(STEPPER_NOT_READY, "Stepper driver not ready for move") +STAT_MSG(LONG_SEG_TIME, "Long segment time") +STAT_MSG(MODBUS_BUF_LENGTH, "Modbus invalid buffer length") +STAT_MSG(INVALID_QCMD, "Invalid command in queue") +STAT_MSG(Q_OVERRUN, "Command queue overrun") +STAT_MSG(Q_UNDERRUN, "Command queue underrun") +STAT_MSG(Q_INVALID_PUSH, "Invalid command pushed to queue") diff --git a/src/avr/src/messages.json.in b/src/avr/src/messages.json.in new file mode 100644 index 0000000..d12e38f --- /dev/null +++ b/src/avr/src/messages.json.in @@ -0,0 +1,7 @@ +#include "cpp_magic.h" +[ +#define STAT_MSG(NAME, MSG) [#NAME, MSG], +#include "messages.def" +#undef CMD + [] +] diff --git a/src/avr/src/modbus.c b/src/avr/src/modbus.c new file mode 100644 index 0000000..2e63c1e --- /dev/null +++ b/src/avr/src/modbus.c @@ -0,0 +1,486 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "modbus.h" +#include "usart.h" +#include "status.h" +#include "rtc.h" +#include "util.h" +#include "estop.h" +#include "config.h" + +#include +#include +#include + +#include +#include + + +typedef enum { + MODBUS_DISCONNECTED, + MODBUS_OK, + MODBUS_CRC, + MODBUS_INVALID, + MODBUS_TIMEDOUT, +} modbus_status_t; + + +static struct { + bool debug; + uint8_t id; + baud_t baud; + parity_t parity; +} cfg = {false, 1, USART_BAUD_9600, USART_NONE}; + + +static struct { + uint8_t bytes; + uint8_t command[MODBUS_BUF_SIZE]; + uint8_t command_length; + uint8_t response[MODBUS_BUF_SIZE]; + uint8_t response_length; + + uint16_t addr; + modbus_rw_cb_t rw_cb; + modbus_cb_t receive_cb; + + uint32_t last_write; + uint32_t last_read; + uint8_t retry; + uint8_t status; + uint16_t crc_errs; + bool write_ready; + bool response_ready; + bool transmit_complete; + bool busy; +} state = {0}; + + +static uint16_t _crc16(const uint8_t *buffer, unsigned length) { + uint16_t crc = 0xffff; + + for (unsigned i = 0; i < length; i++) + crc = _crc16_update(crc, buffer[i]); + + return crc; +} + + +static void _set_write(bool x) {SET_PIN(RS485_RW_PIN, x);} + + +#define INTLVL_SET(REG, NAME, LEVEL) \ + REG = ((REG) & ~NAME##INTLVL_gm) | NAME##INTLVL_##LEVEL##_gc + + +#define INTLVL_ENABLE(REG, NAME, LEVEL, ENABLE) do { \ + if (ENABLE) INTLVL_SET(REG, NAME, LEVEL); \ + else INTLVL_SET(REG, NAME, OFF); \ + } while (0) + + +static void _set_dre_interrupt(bool enable) { + INTLVL_ENABLE(RS485_PORT.CTRLA, USART_DRE, MED, enable); +} + + +static void _set_txc_interrupt(bool enable) { + INTLVL_ENABLE(RS485_PORT.CTRLA, USART_TXC, MED, enable); +} + + +static void _set_rxc_interrupt(bool enable) { + INTLVL_ENABLE(RS485_PORT.CTRLA, USART_RXC, MED, enable); +} + + +static void _write_word(uint8_t *dst, uint16_t value, bool little_endian) { + dst[!little_endian] = value; + dst[little_endian] = value >> 8; +} + + +static uint16_t _read_word(const uint8_t *data, bool little_endian) { + return (uint16_t)data[little_endian] << 8 | data[!little_endian]; +} + + +static bool _check_response() { + // Check CRC + uint16_t computed = _crc16(state.response, state.response_length - 2); + uint16_t expected = + _read_word(state.response + state.response_length - 2, true); + + if (computed != expected) { + if (cfg.debug) { + char sent[state.command_length * 2 + 1]; + char response[state.response_length * 2 + 1]; + format_hex_buf(sent, state.command, state.command_length); + format_hex_buf(response, state.response, state.response_length); + + STATUS_WARNING(STAT_OK, "modbus: invalid CRC, received=0x%04x " + "computed=0x%04x sent=0x%s received=0x%s", + expected, computed, sent, response); + } + + state.crc_errs++; + state.status = MODBUS_CRC; + return false; + } + + // Check that slave id matches + if (state.command[0] != state.response[0]) { + STATUS_WARNING(STAT_OK, "modbus: unexpected slave id, expected=%u got=%u", + state.command[0], state.response[0]); + state.status = MODBUS_INVALID; + return false; + } + + // Check that function code matches + if (state.command[1] != state.response[1]) { + STATUS_WARNING(STAT_OK, "modbus: invalid function code, expected=%u got=%u", + state.command[1], state.response[1]); + state.status = MODBUS_INVALID; + return false; + } + + return true; +} + + +static void _notify(uint8_t func, uint8_t bytes, const uint8_t *data) { + if (!state.receive_cb) return; + + modbus_cb_t cb = state.receive_cb; + state.receive_cb = 0; // May be set in callback + + cb(func, bytes, data); +} + + +static void _handle_response() { + if (!state.response_ready) return; + state.response_ready = false; + + if (!_check_response()) return; + + state.last_write = 0; // Clear timeout timer + state.last_read = rtc_get_time(); // Set delay timer + state.retry = 0; // Reset retry counter + state.status = MODBUS_OK; + state.busy = false; + + _notify(state.response[1], state.response_length - 4, state.response + 2); +} + + +/// Data register empty interrupt +ISR(RS485_DRE_vect) { + RS485_PORT.DATA = state.command[state.bytes++]; + + if (state.bytes == state.command_length) { + _set_dre_interrupt(false); + _set_txc_interrupt(true); + state.bytes = 0; + } +} + + +/// Transmit complete interrupt +ISR(RS485_TXC_vect) { + _set_txc_interrupt(false); + _set_rxc_interrupt(true); + _set_write(false); // Switch to read mode + state.transmit_complete = true; +} + + +/// Data received interrupt +ISR(RS485_RXC_vect) { + state.response[state.bytes] = RS485_PORT.DATA; + + // Ignore leading zeros + if (state.bytes || state.response[0]) state.bytes++; + + if (state.bytes == state.response_length) { + _set_rxc_interrupt(false); + _set_write(true); // Back to write mode + state.bytes = 0; + state.response_ready = true; + } +} + + +static void _read_cb(uint8_t func, uint8_t bytes, const uint8_t *data) { + if (func == MODBUS_READ_OUTPUT_REG && data[0] == bytes - 1) { + if (state.rw_cb) + for (uint8_t i = 0; i < bytes >> 1; i++) + state.rw_cb(true, state.addr + i, _read_word(data + i * 2 + 1, false)); + return; + } + + if (state.rw_cb) state.rw_cb(false, state.addr, 0); +} + + +static void _write_cb(uint8_t func, uint8_t bytes, const uint8_t *data) { + if ((func == MODBUS_WRITE_OUTPUT_REG || + func == MODBUS_WRITE_OUTPUT_REGS) && bytes == 4 && + _read_word(data, false) == state.addr) { + if (state.rw_cb) + state.rw_cb(true, state.addr, _read_word(state.command + 4, false)); + return; + } + + if (state.rw_cb) state.rw_cb(false, state.addr, 0); +} + + +static void _reset() { + _set_dre_interrupt(false); + _set_txc_interrupt(false); + _set_rxc_interrupt(false); + _set_write(true); // RS485 write mode + + // Flush USART + uint8_t x = RS485_PORT.DATA; + x = RS485_PORT.STATUS; + x = x; + + // Clear state + state.write_ready = false; + state.busy = false; +} + + +static void _retry() { + state.last_write = 0; + state.bytes = 0; + state.retry++; + + _set_write(true); // RS485 write mode + + _set_txc_interrupt(false); + _set_rxc_interrupt(false); + _set_dre_interrupt(true); + + // Try changing pin polarity + if (state.retry == MODBUS_RETRIES) { + PINCTRL_PIN(RS485_RO_PIN) ^= PORT_INVEN_bm; + PINCTRL_PIN(RS485_DI_PIN) ^= PORT_INVEN_bm; + } + + if (cfg.debug) STATUS_DEBUG("modbus: retry %d", state.retry); +} + + +static void _timeout() { + if (cfg.debug) STATUS_DEBUG("modbus: timedout"); + + if (state.status == MODBUS_OK || state.status == MODBUS_DISCONNECTED) + state.status = MODBUS_TIMEDOUT; + + _reset(); + _notify(state.command[1], 0, 0); +} + + +static stop_t _get_stop() { + // RTU mode characters must always be 11-bits long + return cfg.parity == USART_NONE ? USART_2STOP : USART_1STOP; +} + + +void modbus_init() { + PR.PRPD &= ~PR_USART1_bm; // Disable power reduction + + DIRCLR_PIN(RS485_RO_PIN); // Input + OUTSET_PIN(RS485_DI_PIN); // High + DIRSET_PIN(RS485_DI_PIN); // Output + OUTSET_PIN(RS485_RW_PIN); // High + DIRSET_PIN(RS485_RW_PIN); // Output + + _reset(); + memset(&state, 0, sizeof(state)); + state.status = MODBUS_DISCONNECTED; + + usart_init_port(&RS485_PORT, cfg.baud, cfg.parity, USART_8BITS, _get_stop()); +} + + +void modbus_deinit() { + _reset(); + memset(&state, 0, sizeof(state)); + state.status = MODBUS_DISCONNECTED; + + // Disable USART + RS485_PORT.CTRLB &= ~(USART_RXEN_bm | USART_TXEN_bm); + + // Float write pins + DIRCLR_PIN(RS485_DI_PIN); + DIRCLR_PIN(RS485_RW_PIN); +} + + +bool modbus_busy() {return state.busy;} + + +static void _start_write() { + if (!state.write_ready) return; + + // The minimum delay between modbus messages is 3.5 characters. There are + // 11-bits per character in RTU mode. Character time is calculated as + // follows: + // + // char time = 11-bits / baud * 3.5 + // + // At 9600 baud the minimum delay is 4.01ms. At 19200 it's 2.005ms. All + // supported higher baud rates require the 1.75ms minimum delay. We round up + // and add 1ms to ensure the delay is never less than the required minimum. + if (state.last_read && + !rtc_expired(state.last_read + (cfg.baud == USART_BAUD_9600 ? 5 : 3))) + return; + state.last_read = 0; + + state.write_ready = false; + _set_dre_interrupt(true); +} + + +void modbus_func(uint8_t func, uint8_t send, const uint8_t *data, + uint8_t receive, modbus_cb_t receive_cb) { + state.bytes = 0; + state.command_length = send + 4; + state.response_length = receive + 4; + state.receive_cb = receive_cb; + state.last_write = 0; + state.retry = 0; + + ESTOP_ASSERT(state.command_length <= MODBUS_BUF_SIZE, STAT_MODBUS_BUF_LENGTH); + ESTOP_ASSERT(state.response_length <= MODBUS_BUF_SIZE, + STAT_MODBUS_BUF_LENGTH); + + state.command[0] = cfg.id; + state.command[1] = func; + memcpy(state.command + 2, data, send); + _write_word(state.command + send + 2, _crc16(state.command, send + 2), true); + + state.busy = true; + state.write_ready = true; + _start_write(); +} + + +void modbus_read(uint16_t addr, uint16_t count, modbus_rw_cb_t cb) { + state.rw_cb = cb; + state.addr = addr; + uint8_t cmd[4]; + _write_word(cmd, addr, false); + _write_word(cmd + 2, count, false); + modbus_func(MODBUS_READ_OUTPUT_REG, 4, cmd, 2 * count + 1, _read_cb); +} + + +void modbus_write(uint16_t addr, uint16_t value, modbus_rw_cb_t cb) { + state.rw_cb = cb; + state.addr = addr; + uint8_t cmd[4]; + _write_word(cmd, addr, false); + _write_word(cmd + 2, value, false); + modbus_func(MODBUS_WRITE_OUTPUT_REG, 4, cmd, 4, _write_cb); +} + + +void modbus_multi_write(uint16_t addr, uint16_t value, modbus_rw_cb_t cb) { + state.rw_cb = cb; + state.addr = addr; + uint8_t cmd[7]; + _write_word(cmd, addr, false); // Start address + _write_word(cmd + 2, 1, false); // Number of regs + cmd[4] = 2; // Number of bytes + _write_word(cmd + 5, value, false); // Value + modbus_func(MODBUS_WRITE_OUTPUT_REGS, 7, cmd, 4, _write_cb); +} + + +void modbus_callback() { + if (state.transmit_complete) { + state.last_write = rtc_get_time(); + state.transmit_complete = false; + } + + _handle_response(); + _start_write(); + + // Timeout out writes + if (!state.last_write || !rtc_expired(state.last_write + MODBUS_TIMEOUT)) + return; + state.last_write = 0; + + if (cfg.debug && state.bytes) { + char sent[state.command_length * 2 + 1]; + char received[state.bytes * 2 + 1]; + + format_hex_buf(sent, state.command, state.command_length); + format_hex_buf(received, state.response, state.bytes); + + STATUS_DEBUG("modbus: sent 0x%s received 0x%s expected %u bytes", + sent, received, state.response_length); + } + + if (state.retry < 2 * MODBUS_RETRIES) _retry(); + else _timeout(); +} + + +// Variable callbacks +bool get_mb_debug() {return cfg.debug;} +void set_mb_debug(bool value) {cfg.debug = value;} +uint8_t get_mb_id() {return cfg.id;} +void set_mb_id(uint8_t id) {cfg.id = id;} +uint8_t get_mb_baud() {return cfg.baud;} + + +void set_mb_baud(uint8_t baud) { + cfg.baud = (baud_t)baud; + usart_set_baud(&RS485_PORT, cfg.baud); +} + + +uint8_t get_mb_parity() {return cfg.parity;} + + +void set_mb_parity(uint8_t parity) { + cfg.parity = (parity_t)parity; + usart_set_parity(&RS485_PORT, cfg.parity); + usart_set_stop(&RS485_PORT, _get_stop()); +} + + +uint8_t get_mb_status() {return state.status;} +uint16_t get_mb_crc_errs() {return state.crc_errs;} diff --git a/src/avr/src/modbus.h b/src/avr/src/modbus.h new file mode 100644 index 0000000..89b2cbb --- /dev/null +++ b/src/avr/src/modbus.h @@ -0,0 +1,112 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +/******************************************************************************\ + + Modbus RTU command format is as follows: + + Function 01: Read Coil Status + Function 02: Read Input Status + + Send: [id][func][addr][count][crc] + Receive: [id][func][bytes][flags][crc] + + Function 03: Read Holding Registers + Function 04: Read Input Registers + + Send: [id][func][addr][count][crc] + Receive: [id][func][bytes][regs][crc] + + Function 05: Force Single Coil + Function 06: Preset Single Register + + Send: [id][func][addr][value][crc] + Receive: [id][func][addr][value][crc] + + Function 15: Force Multiple Coils + + Send: [id][func][addr][count][bytes][flags][crc] + Receive: [id][func][addr][count][crc] + + Function 16: Preset Multiple Registers + + Send: [id][func][addr][count][bytes][regs][crc] + Receive: [id][func][addr][count][crc] + + Where: + + id: 1-byte Slave ID + func: 1-byte Function code + addr: 2-byte Data address + count: 2-byte Address count + bytes: 1-byte Number of bytes to follow + value: 2-byte Value read or written + bits: n-bytes Flags indicating on/off + regs: n-bytes register values to write + checksum: 16-bit CRC: x^16 + x^15 + x^2 + 1 (0x8005) initial: 0xffff + +\******************************************************************************/ + +#pragma once + + +#include +#include + + +typedef enum { + MODBUS_READ_COILS = 1, + MODBUS_READ_CONTACTS = 2, + MODBUS_READ_OUTPUT_REG = 3, + MODBUS_READ_INPUT_REG = 4, + MODBUS_WRITE_COIL = 5, + MODBUS_WRITE_OUTPUT_REG = 6, + MODBUS_WRITE_COILS = 15, + MODBUS_WRITE_OUTPUT_REGS = 16, +} modbus_function_code_t; + + +typedef enum { + MODBUS_COIL_BASE = 0, + MODBUS_CONTACT_BASE = 10000, + MODBUS_INPUT_REG_BASE = 30000, + MODBUS_OUTPUT_REG_BASE = 40000, +} modbus_base_addrs_t; + + +typedef void (*modbus_cb_t)(uint8_t func, uint8_t bytes, const uint8_t *data); +typedef void (*modbus_rw_cb_t)(bool ok, uint16_t addr, uint16_t value); + +void modbus_init(); +void modbus_deinit(); +bool modbus_busy(); +void modbus_func(uint8_t func, uint8_t send, const uint8_t *data, + uint8_t receive, modbus_cb_t cb); +void modbus_read(uint16_t addr, uint16_t count, modbus_rw_cb_t cb); +void modbus_write(uint16_t addr, uint16_t value, modbus_rw_cb_t cb); +void modbus_multi_write(uint16_t addr, uint16_t value, modbus_rw_cb_t cb); +void modbus_callback(); diff --git a/src/avr/src/motor.c b/src/avr/src/motor.c new file mode 100644 index 0000000..adb0b63 --- /dev/null +++ b/src/avr/src/motor.c @@ -0,0 +1,497 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "motor.h" +#include "config.h" +#include "hardware.h" +#include "cpp_magic.h" +#include "rtc.h" +#include "report.h" +#include "stepper.h" +#include "drv8711.h" +#include "estop.h" +#include "axis.h" +#include "util.h" +#include "pgmspace.h" +#include "exec.h" + +#include + +#include +#include +#include +#include + + +typedef struct { + // Config + uint8_t axis; // map motor to axis + uint8_t step_pin; + uint8_t dir_pin; + TC0_t *timer; + DMA_CH_t *dma; + uint8_t dma_trigger; + + bool slave; + uint16_t microsteps; // microsteps per full step + uint16_t stall_microsteps; + bool reverse; + bool enabled; + float step_angle; // degrees per whole step + float travel_rev; // mm or deg of travel per motor revolution + float min_soft_limit; + float max_soft_limit; + bool homed; + + // Computed + float steps_per_unit; + + // Runtime state + uint32_t power_timeout; + int32_t commanded; + int32_t encoder; + int16_t error; + bool last_negative; + + // Move prep + bool prepped; + uint8_t clock; + uint16_t timer_period; + bool negative; + int32_t position; +} motor_t; + + +static motor_t motors[MOTORS] = { + { + .axis = AXIS_X, + .step_pin = STEP_0_PIN, + .dir_pin = DIR_0_PIN, + .timer = &TCD0, + .dma = &DMA.CH0, + .dma_trigger = DMA_CH_TRIGSRC_TCD0_CCA_gc, + }, { + .axis = AXIS_Y, + .step_pin = STEP_1_PIN, + .dir_pin = DIR_1_PIN, + .timer = &TCE0, + .dma = &DMA.CH1, + .dma_trigger = DMA_CH_TRIGSRC_TCE0_CCA_gc, + }, { + .axis = AXIS_Z, + .step_pin = STEP_2_PIN, + .dir_pin = DIR_2_PIN, + .timer = &TCF0, + .dma = &DMA.CH2, + .dma_trigger = DMA_CH_TRIGSRC_TCF0_CCA_gc, + }, { + .axis = AXIS_A, + .step_pin = STEP_3_PIN, + .dir_pin = DIR_3_PIN, + .timer = (TC0_t *)&TCE1, + .dma = &DMA.CH3, + .dma_trigger = DMA_CH_TRIGSRC_TCE1_CCA_gc, + } +}; + + +static uint8_t _dummy; + + +static void _update_config(int motor) { + motor_t *m = &motors[motor]; + + m->steps_per_unit = 360.0 * m->microsteps / m->travel_rev / m->step_angle; +} + + +void motor_init() { + // Enable DMA + DMA.CTRL = DMA_RESET_bm; + DMA.CTRL = DMA_ENABLE_bm; + DMA.INTFLAGS = 0xff; // clear all pending interrupts + + for (int motor = 0; motor < MOTORS; motor++) { + motor_t *m = &motors[motor]; + + // Default soft limits + m->min_soft_limit = -INFINITY; + m->max_soft_limit = INFINITY; + + _update_config(motor); + + // Setup motor timer + m->timer->CTRLB = TC_WGMODE_SINGLESLOPE_gc | TC1_CCAEN_bm; + m->timer->CCA = STEP_PULSE_WIDTH; + + // Setup DMA channel as timer event counter + m->dma->ADDRCTRL = DMA_CH_SRCDIR_FIXED_gc | DMA_CH_DESTDIR_FIXED_gc; + m->dma->TRIGSRC = m->dma_trigger; + + // Note, the DMA transfer must read CCA to clear the trigger + m->dma->SRCADDR0 = (((uintptr_t)&m->timer->CCA) >> 0) & 0xff; + m->dma->SRCADDR1 = (((uintptr_t)&m->timer->CCA) >> 8) & 0xff; + m->dma->SRCADDR2 = 0; + + m->dma->DESTADDR0 = (((uintptr_t)&_dummy) >> 0) & 0xff; + m->dma->DESTADDR1 = (((uintptr_t)&_dummy) >> 8) & 0xff; + m->dma->DESTADDR2 = 0; + + m->dma->CTRLA = DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_1BYTE_gc; + + // IO pins + PINCTRL_PIN(m->step_pin) = PORT_INVEN_bm; // Inverted output + DIRSET_PIN(m->step_pin); // Output + DIRSET_PIN(m->dir_pin); // Output + } + + axis_map_motors(); +} + + +bool motor_is_enabled(int motor) {return motors[motor].enabled;} +int motor_get_axis(int motor) {return motors[motor].axis;} + + +static int32_t _position_to_steps(int motor, float position) { + return (int32_t)round(position * motors[motor].steps_per_unit); +} + + +void motor_set_position(int motor, float position) { + motor_t *m = &motors[motor]; + m->commanded = m->encoder = m->position = _position_to_steps(motor, position); + m->error = 0; +} + + +float motor_get_soft_limit(int motor, bool min) { + return min ? motors[motor].min_soft_limit : motors[motor].max_soft_limit; +} + + +bool motor_get_homed(int motor) {return motors[motor].homed;} + + +static void _update_power(int motor) { + motor_t *m = &motors[motor]; + + if (m->enabled) { + bool timedout = rtc_expired(m->power_timeout); + // NOTE, we have ~5ms to update the driver config + drv8711_set_state(motor, timedout ? DRV8711_IDLE : DRV8711_ACTIVE); + + } else drv8711_set_state(motor, DRV8711_DISABLED); +} + + +/// Callback to manage motor power sequencing and power-down timing. +stat_t motor_rtc_callback() { // called by controller + for (int motor = 0; motor < MOTORS; motor++) + _update_power(motor); + + return STAT_OK; +} + + +void motor_emulate_steps(int motor) { + motor_t *m = &motors[motor]; + m->dma->TRFCNT = 0xffff - abs(m->commanded - m->encoder); +} + + +void motor_end_move(int motor) { + motor_t &m = motors[motor]; + + if (!m.timer->CTRLA) return; + + // Stop clock + m.timer->CTRLA = 0; + + // Wait for pending DMA transfers + while (m.dma->CTRLB & DMA_CH_CHPEND_bm) continue; + + // Get actual step count from DMA channel + const int32_t steps = 0xffff - m.dma->TRFCNT; + + // Accumulate encoder & compute error + m.encoder += m.last_negative ? -steps : steps; + m.error = m.commanded - m.encoder; +} + + +void motor_load_move(int motor) { + motor_t &m = motors[motor]; + + // Clear move + ESTOP_ASSERT(m.prepped, STAT_MOTOR_NOT_PREPPED); + m.prepped = false; + + motor_end_move(motor); + + if (!m.timer_period) return; // Leave clock stopped + + // Set direction, compensating for polarity but only when moving + const bool dir = m.negative ^ m.reverse; + if (dir != IN_PIN(m.dir_pin)) { + SET_PIN(m.dir_pin, dir); + + // We need at least 200ns between direction change and next step. + if (m.timer->CCA < m.timer->CNT) m.timer->CNT = m.timer->CCA + 1; + } + + // Reset DMA step counter + m.dma->CTRLA &= ~DMA_CH_ENABLE_bm; + m.dma->TRFCNT = 0xffff; + m.dma->CTRLA |= DMA_CH_ENABLE_bm; + + // To avoid causing couter wrap around, it is important to start the clock + // before setting PERBUF. If PERBUF is set before the clock is started PER + // updates immediately and possibly mid step. + + // Set clock and period + m.timer->CTRLA = m.clock; // Start clock + m.timer->PERBUF = m.timer_period; // Set next frequency + m.last_negative = m.negative; + m.commanded = m.position; +} + + +void motor_prep_move(int motor, float target) { + // Validate input + ESTOP_ASSERT(0 <= motor && motor < MOTORS, STAT_MOTOR_ID_INVALID); + ESTOP_ASSERT(isfinite(target), STAT_BAD_FLOAT); + + motor_t &m = motors[motor]; + ESTOP_ASSERT(!m.prepped, STAT_MOTOR_NOT_READY); + + // Travel in steps + int32_t position = _position_to_steps(motor, target); + int24_t steps = position - m.position; + m.position = position; + + // Error correction + int16_t correction = abs(m.error); + if (MIN_STEP_CORRECTION <= correction) { + // Dampen correction oscillation + correction >>= 1; + + // Make correction + steps += m.error < 0 ? -correction : correction; + } + + // Positive steps from here on + m.negative = steps < 0; + if (m.negative) steps = -steps; + + // Start with clock / 2 + const float seg_clocks = SEGMENT_TIME * (F_CPU * 60 / 2); + float ticks_per_step = seg_clocks / steps; + + // Use faster clock with faster step rates for increased resolution. + if (ticks_per_step < 0x7fff) { + ticks_per_step *= 2; + m.clock = TC_CLKSEL_DIV1_gc; + + // Limit clock if step rate is too fast + // We allow a slight fudge here (i.e. 1.9 instead 2) because the motor + // driver is able to handle it and otherwise we could not actually hit + // an average rate of 250k usteps/sec. + if (ticks_per_step < STEP_PULSE_WIDTH * 1.9) + ticks_per_step = STEP_PULSE_WIDTH * 1.9; // Too fast + + } else m.clock = TC_CLKSEL_DIV2_gc; // NOTE, pulse width will be twice as long + + // Disable clock if too slow + if (0xffff <= ticks_per_step) ticks_per_step = 0; + + m.timer_period = steps ? round(ticks_per_step) : 0; + + // Power motor + if (!m.enabled) { + m.timer_period = 0; + m.encoder = m.commanded = m.position; + m.error = 0; + + } else if (m.timer_period) // Motor is moving so reset power timeout + m.power_timeout = rtc_get_time() + MOTOR_IDLE_TIMEOUT * 1000; + _update_power(motor); + + // Queue move + m.prepped = true; +} + + +// Var callbacks +bool get_motor_enabled(int motor) {return motors[motor].enabled;} + + +void set_motor_enabled(int motor, bool enabled) { + if (motors[motor].slave) return; + + for (int m = motor; m < MOTORS; m++) + if (motors[m].axis == motors[motor].axis) + motors[m].enabled = enabled; +} + + +float get_step_angle(int motor) {return motors[motor].step_angle;} + + +void set_step_angle(int motor, float value) { + if (motors[motor].slave) return; + + for (int m = motor; m < MOTORS; m++) + if (motors[m].axis == motors[motor].axis) { + motors[m].step_angle = value; + _update_config(m); + } +} + + +float get_travel(int motor) {return motors[motor].travel_rev;} + + +void set_travel(int motor, float value) { + if (motors[motor].slave) return; + + for (int m = motor; m < MOTORS; m++) + if (motors[m].axis == motors[motor].axis) { + motors[m].travel_rev = value; + _update_config(m); + } +} + + +uint16_t get_microstep(int motor) {return motors[motor].microsteps;} + +void set_microstep(int motor, uint16_t value) { + switch (value) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: case 256: + break; + default: return; + } + + if (motors[motor].slave) return; + + for (int m = motor; m < MOTORS; m++) + if (motors[m].axis == motors[motor].axis) { + motors[m].microsteps = value; + _update_config(m); + drv8711_set_microsteps(m, value); + } +} + +uint16_t get_stall_microstep(int motor) {return motors[motor].stall_microsteps;} + +void set_stall_microstep(int motor, uint16_t value) { + switch (value) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: case 256: + break; + default: return; + } + + if (motors[motor].slave) return; + + for (int m = motor; m < MOTORS; m++) + if (motors[m].axis == motors[motor].axis) { + motors[m].stall_microsteps = value; + //_update_config(m); + //drv8711_set_stall_microsteps(m, value); + } +} + +void enable_stall_microstep(int m) +{ + //for (int m = 0; m < MOTORS; m++) + //{ + motor_t *motorTemp = &motors[m]; + motorTemp->steps_per_unit = 360.0 * motorTemp->stall_microsteps / motorTemp->travel_rev / motorTemp->step_angle; + drv8711_set_microsteps(m, motors[m].stall_microsteps); + //} +} + +void disable_stall_microstep() +{ + for(int m =0; m < MOTORS; m++) + { + _update_config(m); //Restore the steps_per_unit + drv8711_set_microsteps(m, motors[m].microsteps); + } +} + +char get_motor_axis(int motor) {return motors[motor].axis;} + + +void set_motor_axis(int motor, uint8_t axis) { + if (MOTORS <= motor || AXES <= axis || axis == motors[motor].axis) return; + motors[motor].axis = axis; + axis_map_motors(); + + // Reset encoder + motor_set_position(motor, exec_get_axis_position(axis)); + + // Check if this is now a slave motor + motors[motor].slave = false; + for (int m = 0; m < motor; m++) + if (motors[m].axis == motors[motor].axis) { + // Sync with master + set_step_angle(motor, motors[m].step_angle); + set_travel(motor, motors[m].travel_rev); + set_microstep(motor, motors[m].microsteps); + set_stall_microstep(motor, motors[m].stall_microsteps); + set_motor_enabled(motor, motors[m].enabled); + motors[motor].slave = true; // Must be last + break; + } +} + + +bool get_reverse(int motor) {return motors[motor].reverse;} +void set_reverse(int motor, bool value) {motors[motor].reverse = value;} + + +float get_min_soft_limit(int motor) {return motors[motor].min_soft_limit;} +float get_max_soft_limit(int motor) {return motors[motor].max_soft_limit;} + + +void set_min_soft_limit(int motor, float limit) { + motors[motor].min_soft_limit = limit; +} + + +void set_max_soft_limit(int motor, float limit) { + motors[motor].max_soft_limit = limit; +} + + +bool get_homed(int motor) {return motors[motor].homed;} +void set_homed(int motor, bool homed) {motors[motor].homed = homed;} + + +int32_t get_encoder(int m) {return motors[m].encoder;} +int32_t get_error(int m) {return motors[m].error;} diff --git a/src/avr/src/motor.h b/src/avr/src/motor.h new file mode 100644 index 0000000..242e771 --- /dev/null +++ b/src/avr/src/motor.h @@ -0,0 +1,61 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "status.h" + +#include +#include + + +typedef enum { + MOTOR_DISABLED, // motor enable is deactivated + MOTOR_ALWAYS_POWERED, // motor is always powered while machine is ON + MOTOR_POWERED_IN_CYCLE, // motor fully powered during cycles, + // de-powered out of cycle + MOTOR_POWERED_ONLY_WHEN_MOVING, // idles shortly after stopped, even in cycle +} motor_power_mode_t; + + +void motor_init(); + +bool motor_is_enabled(int motor); +int motor_get_axis(int motor); +void motor_set_position(int motor, float position); +float motor_get_soft_limit(int motor, bool min); +bool motor_get_homed(int motor); + +stat_t motor_rtc_callback(); + +void motor_end_move(int motor); +void motor_load_move(int motor); +void motor_prep_move(int motor, float target); + +void enable_stall_microstep(int m); +void disable_stall_microstep(); + diff --git a/src/avr/src/outputs.c b/src/avr/src/outputs.c new file mode 100644 index 0000000..f989ec7 --- /dev/null +++ b/src/avr/src/outputs.c @@ -0,0 +1,161 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "outputs.h" +#include "config.h" + + +typedef struct { + uint8_t pin; + bool active; + output_state_t state; + output_mode_t mode; +} output_t; + + +output_t outputs[OUTS] = { + {TOOL_ENABLE_PIN}, + {TOOL_DIR_PIN}, + {SWITCH_1_PIN}, + {SWITCH_2_PIN}, + {FAULT_PIN}, + {TEST_PIN}, +}; + + +static output_t *_get_output(uint8_t pin) { + switch (pin) { + case TOOL_ENABLE_PIN: return &outputs[0]; + case TOOL_DIR_PIN: return &outputs[1]; + case SWITCH_1_PIN: return &outputs[2]; + case SWITCH_2_PIN: return &outputs[3]; + case FAULT_PIN: return &outputs[4]; + case TEST_PIN: return &outputs[5]; + } + + return 0; +} + + +static void _update_state(output_t *output) { + switch (output->mode) { + case OUT_DISABLED: output->state = OUT_TRI; break; + case OUT_LO_HI: output->state = output->active ? OUT_HI : OUT_LO; break; + case OUT_HI_LO: output->state = output->active ? OUT_LO : OUT_HI; break; + case OUT_TRI_LO: output->state = output->active ? OUT_LO : OUT_TRI; break; + case OUT_TRI_HI: output->state = output->active ? OUT_HI : OUT_TRI; break; + case OUT_LO_TRI: output->state = output->active ? OUT_TRI : OUT_LO; break; + case OUT_HI_TRI: output->state = output->active ? OUT_TRI : OUT_HI; break; + } + + switch (output->state) { + case OUT_TRI: DIRCLR_PIN(output->pin); break; + case OUT_HI: OUTSET_PIN(output->pin); DIRSET_PIN(output->pin); break; + case OUT_LO: OUTCLR_PIN(output->pin); DIRSET_PIN(output->pin); break; + } +} + + +void outputs_init() { + for (int i = 0; i < OUTS; i++) _update_state(&outputs[i]); +} + + +bool outputs_is_active(uint8_t pin) { + output_t *output = _get_output(pin); + return output ? output->active : false; +} + + +void outputs_set_active(uint8_t pin, bool active) { + output_t *output = _get_output(pin); + if (!output) return; + + output->active = active; + _update_state(output); +} + + +bool outputs_toggle(uint8_t pin) { + output_t *output = _get_output(pin); + if (!output) return false; + + output->active = !output->active; + _update_state(output); + return output->active; +} + + +void outputs_set_mode(uint8_t pin, output_mode_t mode) { + output_t *output = _get_output(pin); + if (!output) return; + output->mode = mode; + _update_state(output); +} + + +output_state_t outputs_get_state(uint8_t pin) { + output_t *output = _get_output(pin); + if (output) return OUT_TRI; + return output->state; +} + + +void outputs_stop() { + outputs_set_active(SWITCH_1_PIN, false); + outputs_set_active(SWITCH_2_PIN, false); +} + + +// Var callbacks +uint8_t get_output_state(int id) { + return OUTS <= id ? OUT_TRI : outputs[id].state; +} + + +bool get_output_active(int id) { + return OUTS <= id ? false : outputs[id].active; +} + + +void set_output_active(int id, bool active) { + if (OUTS <= id) return; + outputs[id].active = active; + _update_state(&outputs[id]); +} + + +uint8_t get_output_mode(int id) { + return OUTS <= id ? OUT_DISABLED : outputs[id].mode; +} + + +void set_output_mode(int id, uint8_t mode) { + if (OUTS <= id) return; + outputs[id].mode = (output_mode_t)mode; + _update_state(&outputs[id]); +} diff --git a/src/avr/src/outputs.h b/src/avr/src/outputs.h new file mode 100644 index 0000000..e0f6636 --- /dev/null +++ b/src/avr/src/outputs.h @@ -0,0 +1,59 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include +#include + + +typedef enum { + OUT_LO, + OUT_HI, + OUT_TRI, +} output_state_t; + + +/// OUT__ +typedef enum { + OUT_DISABLED, + OUT_LO_HI, + OUT_HI_LO, + OUT_TRI_LO, + OUT_TRI_HI, + OUT_LO_TRI, + OUT_HI_TRI, +} output_mode_t; + + +void outputs_init(); +bool outputs_is_active(uint8_t pin); +void outputs_set_active(uint8_t pin, bool active); +bool outputs_toggle(uint8_t pin); +void outputs_set_mode(uint8_t pin, output_mode_t mode); +output_state_t outputs_get_state(uint8_t pin); +void outputs_stop(); diff --git a/src/avr/src/pgmspace.h b/src/avr/src/pgmspace.h new file mode 100644 index 0000000..9d7f25f --- /dev/null +++ b/src/avr/src/pgmspace.h @@ -0,0 +1,36 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include + +#ifdef __AVR__ +#define PRPSTR "S" +#else +#define PRPSTR "s" +#endif diff --git a/src/avr/src/pins.c b/src/avr/src/pins.c new file mode 100644 index 0000000..65782fd --- /dev/null +++ b/src/avr/src/pins.c @@ -0,0 +1,31 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "pins.h" + + +PORT_t *pin_ports[] = {&PORTA, &PORTB, &PORTC, &PORTD, &PORTE, &PORTF}; diff --git a/src/avr/src/pins.h b/src/avr/src/pins.h new file mode 100644 index 0000000..6d6cd8b --- /dev/null +++ b/src/avr/src/pins.h @@ -0,0 +1,57 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +enum {PORT_A = 1, PORT_B, PORT_C, PORT_D, PORT_E, PORT_F}; + +#define PIN_INDEX(PIN) (PIN & 7) +#define PORT_INDEX(PIN) ((PIN >> 3) - 1) + +#define PIN_PORT(PIN) pin_ports[PORT_INDEX(PIN)] +#define PIN_BM(PIN) (1 << PIN_INDEX(PIN)) + +#define PIN_ID(PORT, PIN) (PORT << 3 | (PIN & 7)) + +#include + +extern PORT_t *pin_ports[]; + +#define DIRSET_PIN(PIN) PIN_PORT(PIN)->DIRSET = PIN_BM(PIN) +#define DIRCLR_PIN(PIN) PIN_PORT(PIN)->DIRCLR = PIN_BM(PIN) +#define OUTCLR_PIN(PIN) PIN_PORT(PIN)->OUTCLR = PIN_BM(PIN) +#define OUTSET_PIN(PIN) PIN_PORT(PIN)->OUTSET = PIN_BM(PIN) +#define OUTTGL_PIN(PIN) PIN_PORT(PIN)->OUTTGL = PIN_BM(PIN) +#define OUT_PIN(PIN) (!!(PIN_PORT(PIN)->OUT & PIN_BM(PIN))) +#define IN_PIN(PIN) (!!(PIN_PORT(PIN)->IN & PIN_BM(PIN))) +#define PINCTRL_PIN(PIN) ((&PIN_PORT(PIN)->PIN0CTRL)[PIN_INDEX(PIN)]) + +#define SET_PIN(PIN, X) \ + do {if (X) OUTSET_PIN(PIN); else OUTCLR_PIN(PIN);} while (0); + +#define PIN_EVSYS_CHMUX(PIN) \ + (EVSYS_CHMUX_PORTA_PIN0_gc + (8 * PORT_INDEX(PIN)) + PIN_INDEX(PIN)) diff --git a/src/avr/src/pwm.c b/src/avr/src/pwm.c new file mode 100644 index 0000000..1659964 --- /dev/null +++ b/src/avr/src/pwm.c @@ -0,0 +1,221 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "pwm.h" + +#include "config.h" +#include "estop.h" +#include "outputs.h" + +#include + + +typedef struct { + bool initialized; + float freq; // base frequency for PWM driver, in Hz + float min_duty; + float max_duty; + float power; +} pwm_t; + + +static pwm_t pwm = {0}; + + +static void _set_enable(bool enable) { + outputs_set_active(TOOL_ENABLE_PIN, enable); +} + + +static void _set_dir(bool clockwise) { + outputs_set_active(TOOL_DIR_PIN, !clockwise); +} + + +static void _update_clock(uint16_t period) { + if (estop_triggered()) period = 0; + + // Disable + if (!period) { + TIMER_PWM.CTRLB = 0; // Disable clock control of pin + OUTCLR_PIN(PWM_PIN); + _set_enable(false); + return; + } + _set_enable(true); + + // 100% duty + if (period == 0xffff) { + TIMER_PWM.CTRLB = 0; // Disable clock control of pin + OUTSET_PIN(PWM_PIN); + return; + } + + // Configure clock + TIMER_PWM.CTRLB = TC1_CCAEN_bm | TC_WGMODE_SINGLESLOPE_gc; + TIMER_PWM.CCA = period; +} + + +static float _compute_duty(float power) { + power = fabsf(power); + if (!power) return 0; // 0% duty + if (power == 1 && pwm.max_duty == 1) return 1; // 100% duty + return power * (pwm.max_duty - pwm.min_duty) + pwm.min_duty; +} + + +static uint16_t _compute_period(float duty) {return TIMER_PWM.PER * duty;} + + +static void _update_pwm() { + if (pwm.initialized) + _update_clock(_compute_period(_compute_duty(pwm.power))); +} + + +static void _update_freq() { + // Set clock period and optimal prescaler value + float prescale = (F_CPU >> 16) / pwm.freq; + + if (prescale <= 1) { + TIMER_PWM.PER = F_CPU / pwm.freq; + TIMER_PWM.CTRLA = TC_CLKSEL_DIV1_gc; + + } else if (prescale <= 2) { + TIMER_PWM.PER = F_CPU / 2 / pwm.freq; + TIMER_PWM.CTRLA = TC_CLKSEL_DIV2_gc; + + } else if (prescale <= 4) { + TIMER_PWM.PER = F_CPU / 4 / pwm.freq; + TIMER_PWM.CTRLA = TC_CLKSEL_DIV4_gc; + + } else if (prescale <= 8) { + TIMER_PWM.PER = F_CPU / 8 / pwm.freq; + TIMER_PWM.CTRLA = TC_CLKSEL_DIV8_gc; + + } else if (prescale <= 64) { + TIMER_PWM.PER = F_CPU / 64 / pwm.freq; + TIMER_PWM.CTRLA = TC_CLKSEL_DIV64_gc; + + } else TIMER_PWM.CTRLA = 0; + + _update_pwm(); +} + + +void pwm_init() { + pwm.initialized = true; + pwm.power = 0; + + // Configure IO + _set_dir(true); + _set_enable(false); + _update_freq(); + + // PWM output + OUTCLR_PIN(PWM_PIN); + DIRSET_PIN(PWM_PIN); +} + + +float pwm_get() {return pwm.power;} + + +void pwm_deinit(deinit_cb_t cb) { + pwm.initialized = false; + + _set_enable(false); + + // Float PWM output pin + DIRCLR_PIN(PWM_PIN); + + // Disable clock + TIMER_PWM.CTRLA = 0; + + cb(); +} + + +power_update_t pwm_get_update(float power) { + power_update_t update = { + 0 <= power ? POWER_FORWARD : POWER_REVERSE, + power, + _compute_period(_compute_duty(power)) + }; + + return update; +} + + +// Called from hi-priority stepper interrupt, must be very fast +void pwm_update(const power_update_t &update) { + if (!pwm.initialized || update.state == POWER_IGNORE) return; + _update_clock(update.period); + if (update.period) _set_dir(update.state == POWER_FORWARD); + pwm.power = update.power; +} + + +// Var callbacks +float get_pwm_min_duty() {return pwm.min_duty * 100;} + + +void set_pwm_min_duty(float value) { + pwm.min_duty = value * 0.01; + _update_pwm(); +} + + +float get_pwm_max_duty() {return pwm.max_duty * 100;} + + +void set_pwm_max_duty(float value) { + pwm.max_duty = value * 0.01; + _update_pwm(); +} + + +float get_pwm_duty() {return _compute_duty(pwm.power);} +float get_pwm_freq() {return pwm.freq;} + + +void set_pwm_freq(float value) { + if (value < 8) value = 8; + if (320000 < value) value = 320000; + pwm.freq = value; + _update_freq(); +} + + +bool get_pwm_invert() {return PINCTRL_PIN(PWM_PIN) & PORT_INVEN_bm;} + + +void set_pwm_invert(bool invert) { + if (invert) PINCTRL_PIN(PWM_PIN) |= PORT_INVEN_bm; + else PINCTRL_PIN(PWM_PIN) &= ~PORT_INVEN_bm; +} diff --git a/src/avr/src/pwm.h b/src/avr/src/pwm.h new file mode 100644 index 0000000..23c723b --- /dev/null +++ b/src/avr/src/pwm.h @@ -0,0 +1,37 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "spindle.h" + + +void pwm_init(); +float pwm_get(); +void pwm_deinit(deinit_cb_t cb); +power_update_t pwm_get_update(float power); +void pwm_update(const power_update_t &update); diff --git a/src/avr/src/report.c b/src/avr/src/report.c new file mode 100644 index 0000000..5e9d032 --- /dev/null +++ b/src/avr/src/report.c @@ -0,0 +1,54 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "report.h" +#include "config.h" +#include "usart.h" +#include "rtc.h" +#include "vars.h" + + +static bool _full = false; +static uint32_t _last = 0; + + +void report_request_full() {_full = true;} + + +void report_callback() { + // Wait until output buffer is empty + if (!usart_tx_empty()) return; + + // Limit frequency + uint32_t now = rtc_get_time(); + if (now - _last < REPORT_RATE) return; + _last = now; + + // Report vars + vars_report(_full); + _full = false; +} diff --git a/src/avr/src/report.h b/src/avr/src/report.h new file mode 100644 index 0000000..4a7dcff --- /dev/null +++ b/src/avr/src/report.h @@ -0,0 +1,34 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +#include "status.h" + +void report_request_full(); +void report_callback(); diff --git a/src/avr/src/ringbuf.def b/src/avr/src/ringbuf.def new file mode 100644 index 0000000..1f4bad9 --- /dev/null +++ b/src/avr/src/ringbuf.def @@ -0,0 +1,225 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +/* This file defines an X-Macro ring buffer. It can be used like this: + * + * #define RING_BUF_NAME tx_buf + * #define RING_BUF_SIZE 256 + * #include "ringbuf.def" + * + * This will define the following functions: + * + * void _init(); + * int _empty(); + * int _full(); + * _peek(); + * void _pop(); + * void _push( data); + * + * Where is defined by RING_BUF_NAME and by RING_BUF_TYPE. + * RING_BUF_SIZE defines the length of the ring buffer and must be a power of 2. + * + * The data type and index type both default to uint8_t but can be changed by + * defining RING_BUF_TYPE and RING_BUF_INDEX_TYPE respectively. + * + * By default these functions are declared static inline but this can be changed + * by defining RING_BUF_FUNC. + */ + +#include +#include + +#include + + +#ifndef RING_BUF_NAME +#error Must define RING_BUF_NAME +#endif + +#ifndef RING_BUF_SIZE +#error Must define RING_BUF_SIZE +#endif + +#ifndef RING_BUF_TYPE +#define RING_BUF_TYPE uint8_t +#endif + +#ifndef RING_BUF_INDEX_TYPE +#define RING_BUF_INDEX_TYPE volatile uint8_t +#endif + +#ifndef RING_BUF_FUNC +#define RING_BUF_FUNC static inline +#endif + +#define RING_BUF_MASK (RING_BUF_SIZE - 1) +#if (RING_BUF_SIZE & RING_BUF_MASK) +#error RING_BUF_SIZE is not a power of 2 +#endif + +#ifndef CONCAT +#define _CONCAT(prefix, name) prefix##name +#define CONCAT(prefix, name) _CONCAT(prefix, name) +#endif + +#define RING_BUF_STRUCT CONCAT(RING_BUF_NAME, _ring_buf_t) +#define RING_BUF RING_BUF_NAME + +typedef struct { + RING_BUF_TYPE buf[RING_BUF_SIZE]; + RING_BUF_INDEX_TYPE head; + RING_BUF_INDEX_TYPE tail; +} RING_BUF_STRUCT; + +static RING_BUF_STRUCT RING_BUF; + + +RING_BUF_FUNC void CONCAT(RING_BUF_NAME, _init)() { + RING_BUF.head = RING_BUF.tail = 0; +} + + +#define RING_BUF_INC(x) (((x) + 1) & RING_BUF_MASK) +#define RING_BUF_DEC(x) ((x) ? x - 1 : (RING_BUF_SIZE - 1)) + + +#ifdef RING_BUF_ATOMIC_COPY + +#undef RING_BUF_ATOMIC_COPY +#define RING_BUF_ATOMIC_COPY(TO, FROM) \ + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) TO = FROM + + +#define RING_BUF_ATOMIC_READ_INDEX(INDEX) \ + RING_BUF_FUNC RING_BUF_INDEX_TYPE CONCAT(RING_BUF_NAME, _read_##INDEX)() { \ + RING_BUF_INDEX_TYPE index; \ + RING_BUF_ATOMIC_COPY(index, RING_BUF.INDEX); \ + return index; \ + } + + +#define RING_BUF_ATOMIC_WRITE_INDEX(INDEX) \ + RING_BUF_FUNC void CONCAT(RING_BUF_NAME, _write_##INDEX) \ + (RING_BUF_INDEX_TYPE value) { \ + RING_BUF_ATOMIC_COPY(RING_BUF.INDEX, value); \ + } + + +RING_BUF_ATOMIC_READ_INDEX(head); +RING_BUF_ATOMIC_READ_INDEX(tail); +RING_BUF_ATOMIC_WRITE_INDEX(head); +RING_BUF_ATOMIC_WRITE_INDEX(tail); + +#define RING_BUF_READ_INDEX(INDEX) CONCAT(RING_BUF_NAME, _read_##INDEX)() +#define RING_BUF_WRITE_INDEX(INDEX, VALUE) \ + CONCAT(RING_BUF_NAME, _write_##INDEX)(VALUE) + + +#else // RING_BUF_ATOMIC_COPY +#define RING_BUF_READ_INDEX(INDEX) RING_BUF.INDEX +#define RING_BUF_WRITE_INDEX(INDEX, VALUE) RING_BUF.INDEX = VALUE +#endif // RING_BUF_ATOMIC_COPY + + +RING_BUF_FUNC bool CONCAT(RING_BUF_NAME, _empty)() { + return RING_BUF_READ_INDEX(head) == RING_BUF_READ_INDEX(tail); +} + + +RING_BUF_FUNC bool CONCAT(RING_BUF_NAME, _full)() { + return RING_BUF_READ_INDEX(head) == RING_BUF_INC(RING_BUF_READ_INDEX(tail)); +} + + +RING_BUF_FUNC RING_BUF_INDEX_TYPE CONCAT(RING_BUF_NAME, _fill)() { + return + (RING_BUF_READ_INDEX(tail) - RING_BUF_READ_INDEX(head)) & RING_BUF_MASK; +} + + +RING_BUF_FUNC RING_BUF_INDEX_TYPE CONCAT(RING_BUF_NAME, _space)() { + return (RING_BUF_SIZE - 1) - CONCAT(RING_BUF_NAME, _fill)(); +} + + +RING_BUF_FUNC RING_BUF_TYPE CONCAT(RING_BUF_NAME, _peek)() { + return RING_BUF.buf[RING_BUF_READ_INDEX(head)]; +} + + +RING_BUF_FUNC RING_BUF_TYPE *CONCAT(RING_BUF_NAME, _front)() { + return &RING_BUF.buf[RING_BUF_READ_INDEX(head)]; +} + + +RING_BUF_FUNC RING_BUF_TYPE *CONCAT(RING_BUF_NAME, _back)() { + return &RING_BUF.buf[RING_BUF_DEC(RING_BUF_READ_INDEX(tail))]; +} + + +RING_BUF_FUNC RING_BUF_TYPE CONCAT(RING_BUF_NAME, _get)(int offset) { + return RING_BUF.buf[(RING_BUF_READ_INDEX(head) + offset) & RING_BUF_MASK]; +} + + +RING_BUF_FUNC void CONCAT(RING_BUF_NAME, _pop)() { + RING_BUF_WRITE_INDEX(head, RING_BUF_INC(RING_BUF_READ_INDEX(head))); +} + + +RING_BUF_FUNC RING_BUF_TYPE CONCAT(RING_BUF_NAME, _next)() { + RING_BUF_TYPE x = CONCAT(RING_BUF_NAME, _peek)(); + CONCAT(RING_BUF_NAME, _pop)(); + return x; +} + + +RING_BUF_FUNC void CONCAT(RING_BUF_NAME, _push)(RING_BUF_TYPE data) { + RING_BUF.buf[RING_BUF_READ_INDEX(tail)] = data; + RING_BUF_WRITE_INDEX(tail, RING_BUF_INC(RING_BUF_READ_INDEX(tail))); +} + + +#undef RING_BUF +#undef RING_BUF_STRUCT +#undef RING_BUF_INC +#undef RING_BUF_MASK + +#undef RING_BUF_NAME +#undef RING_BUF_SIZE +#undef RING_BUF_TYPE +#undef RING_BUF_INDEX_TYPE +#undef RING_BUF_FUNC + +#undef RING_BUF_READ_INDEX +#undef RING_BUF_WRITE_INDEX + +#ifdef RING_BUF_ATOMIC_COPY +#undef RING_BUF_ATOMIC_COPY +#undef RING_BUF_ATOMIC_READ_INDEX +#undef RING_BUF_ATOMIC_WRITE_INDEX +#endif // RING_BUF_ATOMIC_COPY diff --git a/src/avr/src/rtc.c b/src/avr/src/rtc.c new file mode 100644 index 0000000..8020895 --- /dev/null +++ b/src/avr/src/rtc.c @@ -0,0 +1,78 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "rtc.h" + +#include "switch.h" +#include "analog.h" +#include "motor.h" +#include "lcd.h" +#include "vfd_spindle.h" + +#include +#include +#include + +#include + + +static uint32_t ticks; + + +ISR(RTC_OVF_vect) { + ticks++; + + lcd_rtc_callback(); + switch_rtc_callback(); + analog_rtc_callback(); + vfd_spindle_rtc_callback(); + if (!(ticks & 255)) motor_rtc_callback(); + wdt_reset(); +} + + +/// Initialize and start the clock +/// This routine follows the code in app note AVR1314. +void rtc_init() { + ticks = 0; + + OSC.CTRL |= OSC_RC32KEN_bm; // enable internal 32kHz. + while (!(OSC.STATUS & OSC_RC32KRDY_bm)); // 32kHz osc stabilize + while (RTC.STATUS & RTC_SYNCBUSY_bm); // wait RTC not busy + + CLK.RTCCTRL = CLK_RTCSRC_RCOSC32_gc | CLK_RTCEN_bm; // 32kHz clock as RTC src + while (RTC.STATUS & RTC_SYNCBUSY_bm); // wait RTC not busy + + // the following must be in this order or it doesn't work + RTC.PER = 33; // overflow period ~1ms + RTC.INTCTRL = RTC_OVFINTLVL_LO_gc; // overflow LO interrupt + RTC.CTRL = RTC_PRESCALER_DIV1_gc; // no prescale +} + + +uint32_t rtc_get_time() {return ticks;} +bool rtc_expired(uint32_t t) {return 0 <= (int32_t)(ticks - t);} diff --git a/src/avr/src/rtc.h b/src/avr/src/rtc.h new file mode 100644 index 0000000..6313ae9 --- /dev/null +++ b/src/avr/src/rtc.h @@ -0,0 +1,37 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +#include +#include + +void rtc_init(); +uint32_t rtc_get_time(); +int32_t rtc_diff(uint32_t t); +bool rtc_expired(uint32_t t); diff --git a/src/avr/src/seek.c b/src/avr/src/seek.c new file mode 100644 index 0000000..e3faf45 --- /dev/null +++ b/src/avr/src/seek.c @@ -0,0 +1,139 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "seek.h" + +#include "command.h" +#include "switch.h" +#include "estop.h" +#include "util.h" +#include "state.h" + +#include + +#include "motor.h" + +enum { + SEEK_ACTIVE = 1 << 0, + SEEK_ERROR = 1 << 1, + SEEK_FOUND = 1 << 2, +}; + + +typedef struct { + bool active; + switch_id_t sw; + uint8_t flags; +} seek_t; + + +static seek_t seek = {false, SW_INVALID, 0}; + + +switch_id_t seek_get_switch() {return seek.active ? seek.sw : SW_INVALID;} + +bool seek_active() { + return seek.active; +} + +bool seek_switch_found() { + if (!seek.active) return false; + + bool inactive = !(seek.flags & SEEK_ACTIVE); + + if (switch_is_active(seek.sw) ^ inactive) { + seek.flags |= SEEK_FOUND; + return true; + } + + return false; +} + + +void seek_end() { + switch_id_t seekMode; + + seekMode = seek_get_switch(); + + if (!seek.active) return; + + if (!(SEEK_FOUND & seek.flags) && (SEEK_ERROR & seek.flags)) + estop_trigger(STAT_SEEK_NOT_FOUND); + + if((seekMode == SW_STALL_0) || (seekMode == SW_STALL_1) || (seekMode ==SW_STALL_2) || (seekMode == SW_STALL_3)) + disable_stall_microstep(); + + seek.active = false; +} + + +void seek_cancel() { + switch_id_t seekMode; + + seekMode = seek_get_switch(); + + + if((seekMode == SW_STALL_0) || (seekMode == SW_STALL_1) || (seekMode ==SW_STALL_2) || (seekMode == SW_STALL_3)) + disable_stall_microstep(); + + seek.active = false; +} + + +// Command callbacks +stat_t command_seek(char *cmd) { + switch_id_t sw = (switch_id_t)decode_hex_nibble(cmd[1]); + if (sw <= 0) return STAT_INVALID_ARGUMENTS; // Don't allow seek to ESTOP + if (!switch_is_enabled(sw)) return STAT_SEEK_NOT_ENABLED; + + uint8_t flags = decode_hex_nibble(cmd[2]); + if (flags & 0xfc) return STAT_INVALID_ARGUMENTS; + + ////////////// + if(sw == SW_STALL_0) enable_stall_microstep(0); + if(sw == SW_STALL_1) enable_stall_microstep(1); + if(sw == SW_STALL_2) enable_stall_microstep(2); + if(sw == SW_STALL_3) enable_stall_microstep(3); + + seek_t seek = {true, sw, flags}; + command_push(*cmd, &seek); + + return STAT_OK; +} + + +unsigned command_seek_size() {return sizeof(seek_t);} +void command_seek_exec(void *data) { + //switch_id_t seekMode; + + //seekMode = seek_get_switch(); + + //if((seekMode == SW_STALL_0) || (seekMode == SW_STALL_1) || (seekMode ==SW_STALL_2) || (seekMode == SW_STALL_3)) + // enable_stall_microstep(); + + seek = *(seek_t *)data; +} diff --git a/src/avr/src/seek.h b/src/avr/src/seek.h new file mode 100644 index 0000000..aa1d7fa --- /dev/null +++ b/src/avr/src/seek.h @@ -0,0 +1,39 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "switch.h" + +#include + + +switch_id_t seek_get_switch(); +bool seek_switch_found(); +void seek_end(); +void seek_cancel(); +bool seek_active(); diff --git a/src/avr/src/spindle.c b/src/avr/src/spindle.c new file mode 100644 index 0000000..cd39a88 --- /dev/null +++ b/src/avr/src/spindle.c @@ -0,0 +1,334 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "spindle.h" +#include "pwm.h" +#include "huanyang.h" +#include "vfd_spindle.h" +#include "stepper.h" +#include "config.h" +#include "command.h" +#include "exec.h" +#include "util.h" + +#include + + +typedef struct { + float dist; + float speed; +} sync_speed_t; + + +static struct { + spindle_type_t type; + float override; + sync_speed_t sync_speed; + float speed; + bool reversed; + float min_rpm; + float max_rpm; + float inv_max_rpm; + + bool dynamic_power; + float inv_feed; + + spindle_type_t next_type; + +} spindle = { + .type = SPINDLE_TYPE_DISABLED, + .override = 1, + .sync_speed = {-1, 0} +}; + + +static float _get_power() { + switch (spindle.type) { + case SPINDLE_TYPE_DISABLED: return 0; + case SPINDLE_TYPE_PWM: return pwm_get(); + case SPINDLE_TYPE_HUANYANG: return huanyang_get(); + default: return vfd_spindle_get(); + } +} + + +static float _speed_to_power(float speed) { + bool negative = speed < 0; + float power = fabs(speed * spindle.override); + + if (power < spindle.min_rpm) power = 0; + else if (spindle.max_rpm <= power) power = 1; + else power *= spindle.inv_max_rpm; + + return (negative ^ spindle.reversed) ? -power : power; +} + + +static void _set_speed(float speed) { + spindle.speed = speed; + + float power = _speed_to_power(speed); + + switch (spindle.type) { + case SPINDLE_TYPE_DISABLED: break; + + case SPINDLE_TYPE_PWM: { + // PWM speed updates must be synchronized with stepper movement + spindle.sync_speed.dist = 0; + spindle.sync_speed.speed = speed; + break; + } + + case SPINDLE_TYPE_HUANYANG: huanyang_set(power); break; + default: vfd_spindle_set(power); break; + } +} + + +static void _deinit_cb() { + spindle.type = spindle.next_type; + spindle.next_type = SPINDLE_TYPE_DISABLED; + + switch (spindle.type) { + case SPINDLE_TYPE_DISABLED: break; + case SPINDLE_TYPE_PWM: pwm_init(); break; + case SPINDLE_TYPE_HUANYANG: huanyang_init(); break; + default: vfd_spindle_init(); break; + } + + spindle_update_speed(); +} + + +static void _set_type(spindle_type_t type) { + if (type == spindle.type) return; + + spindle_type_t old_type = spindle.type; + spindle.next_type = type; + spindle.type = SPINDLE_TYPE_DISABLED; + + switch (old_type) { + case SPINDLE_TYPE_DISABLED: _deinit_cb(); break; + case SPINDLE_TYPE_PWM: pwm_deinit(_deinit_cb); break; + case SPINDLE_TYPE_HUANYANG: huanyang_deinit(_deinit_cb); break; + default: vfd_spindle_deinit(_deinit_cb); break; + } +} + + +spindle_type_t spindle_get_type() {return spindle.type;} + + +static power_update_t _get_power_update() { + float power = _speed_to_power(spindle.speed); + + // Handle dynamic power + if (spindle.dynamic_power && spindle.inv_feed) { + float scale = spindle.inv_feed * exec_get_velocity(); + if (scale < 1) power *= scale; + } + + return pwm_get_update(power); +} + + +void spindle_load_power_updates(power_update_t updates[], float minD, + float maxD) { + float stepD = (maxD - minD) * (1.0 / POWER_MAX_UPDATES); + float d = minD + 1e-3; // Starting distance + + for (unsigned i = 0; i < POWER_MAX_UPDATES; i++) { + bool changed = false; + d += stepD; // Ending distance for this power step + + while (true) { + // Load new sync speed if needed and available + if (spindle.sync_speed.dist < 0 && command_peek() == COMMAND_sync_speed) + spindle.sync_speed = *(sync_speed_t *)(command_next() + 1); + + // Exit if we don't have a speed or it's not ready to be set + if (spindle.sync_speed.dist == -1 || d < spindle.sync_speed.dist) break; + + // Load sync speed + spindle.sync_speed.dist = -1; // Mark done + spindle.speed = spindle.sync_speed.speed; + changed = true; + } + + if (spindle.type == SPINDLE_TYPE_PWM) updates[i] = _get_power_update(); + else { + updates[i].state = POWER_IGNORE; + if (changed) spindle_update_speed(); + } + } +} + + +// Called from hi-priority stepper interrupt +void spindle_update(const power_update_t &update) {pwm_update(update);} +void spindle_update_speed() {_set_speed(spindle.speed);} + + +// Called from lo-priority stepper interrupt +void spindle_idle() { + if (spindle.sync_speed.dist != -1) { + spindle.sync_speed.dist = -1; // Mark done + spindle.speed = spindle.sync_speed.speed; + + if (spindle.type == SPINDLE_TYPE_PWM) spindle_update(_get_power_update()); + else spindle_update_speed(); + } +} + + +void spindle_stop() {_set_speed(0);} // Only called when steppers have halted +void spindle_estop() {_set_type(SPINDLE_TYPE_DISABLED);} + + +// Var callbacks +uint8_t get_tool_type() {return spindle.type;} +void set_tool_type(uint8_t value) {_set_type((spindle_type_t)value);} +bool get_tool_reversed() {return spindle.reversed;} + + +void set_tool_reversed(bool reversed) { + if (spindle.reversed == reversed) return; + spindle.reversed = reversed; + spindle_update_speed(); +} + + +float get_speed() {return _get_power() * spindle.max_rpm;} +float get_max_spin() {return spindle.max_rpm;} + + +void set_max_spin(float value) { + if (spindle.max_rpm != value) { + spindle.max_rpm = value; + spindle.inv_max_rpm = 1 / value; + spindle_update_speed(); + } +} + + +float get_min_spin() {return spindle.min_rpm;} + + +void set_min_spin(float value) { + if (spindle.min_rpm != value) { + spindle.min_rpm = value; + spindle_update_speed(); + } +} + + +uint16_t get_spindle_status() { + switch (spindle.type) { + case SPINDLE_TYPE_DISABLED: return 0; + case SPINDLE_TYPE_PWM: return 0; + case SPINDLE_TYPE_HUANYANG: return huanyang_get_status(); + default: return vfd_get_status(); + } +} + + +uint16_t get_speed_override() {return spindle.override * 1000;} + + +void set_speed_override(uint16_t value) { + value *= 0.001; + + if (spindle.override != value) { + spindle.override = value; + spindle_update_speed(); + } +} + + +bool get_dynamic_power() {return spindle.dynamic_power;} + + +void set_dynamic_power(bool enable) { + if (spindle.dynamic_power != enable) { + spindle.dynamic_power = enable; + spindle_update_speed(); + } +} + + +float get_inverse_feed() {return spindle.inv_feed;} + + +void set_inverse_feed(float iF) { + if (spindle.inv_feed != iF) { + spindle.inv_feed = iF; + spindle_update_speed(); + } +} + + +// Command callbacks +stat_t command_sync_speed(char *cmd) { + sync_speed_t s; + + cmd++; // Skip command code + + // Get distance and speed + if (!decode_float(&cmd, &s.dist) || s.dist < 0) return STAT_BAD_FLOAT; + if (!decode_float(&cmd, &s.speed)) return STAT_BAD_FLOAT; + + // Queue + command_push(COMMAND_sync_speed, &s); + + return STAT_OK; +} + + +unsigned command_sync_speed_size() {return sizeof(sync_speed_t);} + + +void command_sync_speed_exec(void *data) { + _set_speed(((sync_speed_t *)data)->speed); +} + + +stat_t command_speed(char *cmd) { + cmd++; // Skip command code + + // Get speed + float speed; + if (!decode_float(&cmd, &speed)) return STAT_BAD_FLOAT; + + // Queue + command_push(COMMAND_speed, &speed); + + return STAT_OK; +} + + +unsigned command_speed_size() {return sizeof(float);} +void command_speed_exec(void *data) {_set_speed(*(float *)data);} diff --git a/src/avr/src/spindle.h b/src/avr/src/spindle.h new file mode 100644 index 0000000..4728631 --- /dev/null +++ b/src/avr/src/spindle.h @@ -0,0 +1,74 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include +#include + + +typedef enum { + POWER_IGNORE, + POWER_FORWARD, + POWER_REVERSE +} power_state_t; + + +typedef struct { + power_state_t state; + float power; + uint16_t period; // Used by PWM +} power_update_t; + + +typedef enum { + SPINDLE_TYPE_DISABLED, + SPINDLE_TYPE_PWM, + SPINDLE_TYPE_HUANYANG, + SPINDLE_TYPE_CUSTOM, + SPINDLE_TYPE_AC_TECH, + SPINDLE_TYPE_NOWFOREVER, + SPINDLE_TYPE_DELTA_VFD015M21A, + SPINDLE_TYPE_YL600, + SPINDLE_TYPE_FR_D700, + SPINDLE_TYPE_SUNFAR_E300, + SPINDLE_TYPE_OMRON_MX2, + SPINDLE_TYPE_V70, +} spindle_type_t; + + +typedef void (*deinit_cb_t)(); + + +spindle_type_t spindle_get_type(); +void spindle_stop(); +void spindle_estop(); +void spindle_load_power_updates(power_update_t updates[], float minD, + float maxD); +void spindle_update(const power_update_t &update); +void spindle_update_speed(); +void spindle_idle(); diff --git a/src/avr/src/state.c b/src/avr/src/state.c new file mode 100644 index 0000000..e94069c --- /dev/null +++ b/src/avr/src/state.c @@ -0,0 +1,259 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "state.h" + +#include "exec.h" +#include "command.h" +#include "stepper.h" +#include "spindle.h" +#include "outputs.h" +#include "jog.h" +#include "estop.h" +#include "seek.h" + +#include + + +static struct { + bool flushing; + bool resuming; + bool stop_requested; + bool pause_requested; + bool unpause_requested; + + state_t state; + uint16_t state_count; + hold_reason_t hold_reason; +} s = { + .flushing = true, // Start out flushing +}; + + +PGM_P state_get_pgmstr(state_t state) { + switch (state) { + case STATE_READY: return PSTR("READY"); + case STATE_ESTOPPED: return PSTR("ESTOPPED"); + case STATE_RUNNING: return PSTR("RUNNING"); + case STATE_JOGGING: return PSTR("JOGGING"); + case STATE_STOPPING: return PSTR("STOPPING"); + case STATE_HOLDING: return PSTR("HOLDING"); + } + + return PSTR("INVALID"); +} + + +PGM_P state_get_hold_reason_pgmstr(hold_reason_t reason) { + switch (reason) { + case HOLD_REASON_USER_PAUSE: return PSTR("User pause"); + case HOLD_REASON_USER_STOP: return PSTR("User stop"); + case HOLD_REASON_PROGRAM_PAUSE: return PSTR("Program pause"); + case HOLD_REASON_OPTIONAL_PAUSE: return PSTR("Optional pause"); + case HOLD_REASON_SWITCH_FOUND: return PSTR("Switch found"); + } + + return PSTR("INVALID"); +} + + +state_t state_get() {return s.state;} + + +static void _set_state(state_t state) { + if (s.state == state) return; // No change + if (s.state == STATE_ESTOPPED) return; // Can't leave EStop state + s.state = state; + s.state_count++; +} + + +static void _set_hold_reason(hold_reason_t reason) {s.hold_reason = reason;} +bool state_is_flushing() {return s.flushing && !s.resuming;} +bool state_is_resuming() {return s.resuming;} + + +static bool _is_idle() { + return (state_get() == STATE_READY || state_get() == STATE_HOLDING) && + !st_is_busy(); +} + + +void state_seek_hold() { + if (state_get() == STATE_RUNNING) { + _set_hold_reason(HOLD_REASON_SWITCH_FOUND); + _set_state(STATE_STOPPING); + } +} + + +static void _stop() { + _set_hold_reason(HOLD_REASON_USER_STOP); + + switch (state_get()) { + case STATE_RUNNING: + _set_state(STATE_STOPPING); + break; + + case STATE_JOGGING: + case STATE_READY: + case STATE_HOLDING: + jog_stop(); + spindle_stop(); + outputs_stop(); + seek_cancel(); + break; + + case STATE_STOPPING: + case STATE_ESTOPPED: + break; // Ignore + } +} + + +void state_holding() { + _set_state(STATE_HOLDING); + if (s.hold_reason == HOLD_REASON_USER_STOP) _stop(); +} + + +void state_running() { + if (state_get() == STATE_READY) _set_state(STATE_RUNNING); +} + + +void state_jogging() { + if (state_get() == STATE_READY || state_get() == STATE_HOLDING) + _set_state(STATE_JOGGING); +} + + +void state_idle() { + if (state_get() == STATE_RUNNING || state_get() == STATE_JOGGING) + _set_state(STATE_READY); +} + + +void state_estop() {_set_state(STATE_ESTOPPED);} + + +void state_callback() { + if (estop_triggered()) return; + + // Pause + if (s.pause_requested) { + if (state_get() == STATE_RUNNING) { + if (s.pause_requested) _set_hold_reason(HOLD_REASON_USER_PAUSE); + _set_state(STATE_STOPPING); + } + + s.pause_requested = false; + } + + // Stop + if (s.stop_requested) { + _stop(); + s.stop_requested = false; + } + + // Only flush queue when idle (READY or HOLDING) + if (s.flushing && _is_idle()) { + command_flush_queue(); + + // Resume + if (s.resuming) s.flushing = s.resuming = false; + } + + // Unpause + if (s.unpause_requested && !s.flushing && state_get() != STATE_STOPPING) { + s.unpause_requested = false; + + if (state_get() == STATE_HOLDING) { + // Check if any moves are buffered + if (command_get_count()) _set_state(STATE_RUNNING); + else _set_state(STATE_READY); + } + } +} + + +// Var callbacks +PGM_P get_state() {return state_get_pgmstr(state_get());} +uint16_t get_state_count() {return s.state_count;} +PGM_P get_hold_reason() {return state_get_hold_reason_pgmstr(s.hold_reason);} + + +// Command callbacks +stat_t command_pause(char *cmd) { + pause_t type = (pause_t)(cmd[1] - '0'); + + if (type == PAUSE_USER) s.pause_requested = true; + else command_push(cmd[0], &type); + + return STAT_OK; +} + + +unsigned command_pause_size() {return sizeof(pause_t);} + + +void command_pause_exec(void *data) { + switch (*(pause_t *)data) { + case PAUSE_PROGRAM_OPTIONAL: + _set_hold_reason(HOLD_REASON_OPTIONAL_PAUSE); + break; + + case PAUSE_PROGRAM: _set_hold_reason(HOLD_REASON_PROGRAM_PAUSE); break; + default: return; + } + + state_holding(); +} + + +stat_t command_stop(char *cmd) { + s.stop_requested = true; + return STAT_OK; +} + + +stat_t command_unpause(char *cmd) { + s.unpause_requested = true; + return STAT_OK; +} + + +stat_t command_resume(char *cmd) { + if (s.flushing) s.resuming = true; + return STAT_OK; +} + + +stat_t command_flush(char *cmd) { + s.flushing = true; + return STAT_OK; +} diff --git a/src/avr/src/state.h b/src/avr/src/state.h new file mode 100644 index 0000000..d3488ab --- /dev/null +++ b/src/avr/src/state.h @@ -0,0 +1,76 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "pgmspace.h" + +#include + + +typedef enum { + STATE_READY, + STATE_ESTOPPED, + STATE_RUNNING, + STATE_JOGGING, + STATE_STOPPING, + STATE_HOLDING, +} state_t; + + +typedef enum { + HOLD_REASON_USER_PAUSE, + HOLD_REASON_USER_STOP, + HOLD_REASON_PROGRAM_PAUSE, + HOLD_REASON_OPTIONAL_PAUSE, + HOLD_REASON_SWITCH_FOUND, +} hold_reason_t; + + +typedef enum { + PAUSE_USER, + PAUSE_PROGRAM, + PAUSE_PROGRAM_OPTIONAL, +} pause_t; + + +PGM_P state_get_pgmstr(state_t state); +PGM_P state_get_hold_reason_pgmstr(hold_reason_t reason); + +state_t state_get(); + +bool state_is_flushing(); +bool state_is_resuming(); + +void state_seek_hold(); +void state_holding(); +void state_running(); +void state_jogging(); +void state_idle(); +void state_estop(); + +void state_callback(); diff --git a/src/avr/src/status.c b/src/avr/src/status.c new file mode 100644 index 0000000..4279637 --- /dev/null +++ b/src/avr/src/status.c @@ -0,0 +1,96 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "status.h" +#include "estop.h" +#include "usart.h" + +#include +#include + + +#define STAT_MSG(NAME, TEXT) static const char stat_##NAME[] PROGMEM = TEXT; +#include "messages.def" +#undef STAT_MSG + + +static const char *const stat_msg[] PROGMEM = { +#define STAT_MSG(NAME, TEXT) stat_##NAME, +#include "messages.def" +#undef STAT_MSG +}; + + +const char *status_to_pgmstr(stat_t code) { + return (const char *)pgm_read_ptr(&stat_msg[code]); +} + + +const char *status_level_pgmstr(status_level_t level) { + switch (level) { + case STAT_LEVEL_INFO: return PSTR("info"); + case STAT_LEVEL_DEBUG: return PSTR("debug"); + case STAT_LEVEL_WARNING: return PSTR("warning"); + default: return PSTR("error"); + } +} + + +stat_t status_message_P(const char *location, status_level_t level, + stat_t code, const char *msg, ...) { + va_list args; + + // Type + printf_P(PSTR("\n{\"level\":\"%" PRPSTR "\",\"msg\":\""), + status_level_pgmstr(level)); + + // Message + printf_P(PSTR("%" PRPSTR), status_to_pgmstr(code)); + + if (msg && pgm_read_byte(msg)) { + putchar(':'); + putchar(' '); + + // TODO escape invalid chars + va_start(args, msg); + vfprintf_P(stdout, msg, args); + va_end(args); + } + + putchar('"'); + + // Code + if (code) printf_P(PSTR(",\"code\":%d"), code); + + // Location + if (location) printf_P(PSTR(",\"where\":\"%" PRPSTR "\""), location); + + putchar('}'); + putchar('\n'); + + return code; +} diff --git a/src/avr/src/status.h b/src/avr/src/status.h new file mode 100644 index 0000000..d363a1d --- /dev/null +++ b/src/avr/src/status.h @@ -0,0 +1,84 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "pgmspace.h" + + +typedef enum { +#define STAT_MSG(NAME, TEXT) STAT_##NAME, +#include "messages.def" +#undef STAT_MSG + + STAT_MAX, + STAT_DO_NOT_EXCEED = 255 // Do not exceed 255 +} stat_t; + + +typedef enum { + STAT_LEVEL_INFO, + STAT_LEVEL_DEBUG, + STAT_LEVEL_WARNING, + STAT_LEVEL_ERROR, +} status_level_t; + + +extern stat_t status_code; + +const char *status_to_pgmstr(stat_t code); +const char *status_level_pgmstr(status_level_t level); +stat_t status_message_P(const char *location, status_level_t level, + stat_t code, const char *msg, ...); + +#define TO_STRING(x) _TO_STRING(x) +#define _TO_STRING(x) #x + +#define STATUS_LOCATION PSTR(__FILE__ ":" TO_STRING(__LINE__)) + +#define STATUS_MESSAGE(LEVEL, CODE, MSG, ...) \ + status_message_P(STATUS_LOCATION, LEVEL, CODE, PSTR(MSG), ##__VA_ARGS__) + +#define STATUS_INFO(MSG, ...) \ + STATUS_MESSAGE(STAT_LEVEL_INFO, STAT_OK, MSG, ##__VA_ARGS__) + +#define STATUS_DEBUG(MSG, ...) \ + STATUS_MESSAGE(STAT_LEVEL_DEBUG, STAT_OK, MSG, ##__VA_ARGS__) + +#define STATUS_WARNING(CODE, MSG, ...) \ + STATUS_MESSAGE(STAT_LEVEL_WARNING, CODE, MSG, ##__VA_ARGS__) + +#define STATUS_ERROR(CODE, MSG, ...) \ + STATUS_MESSAGE(STAT_LEVEL_ERROR, CODE, MSG, ##__VA_ARGS__) + + +#ifdef DEBUG +#define DEBUG_CALL(FMT, ...) \ + printf_P(PSTR("%s(" FMT ")\n"), __FUNCTION__, ##__VA_ARGS__) +#else // DEBUG +#define DEBUG_CALL(...) +#endif // DEBUG diff --git a/src/avr/src/stepper.c b/src/avr/src/stepper.c new file mode 100644 index 0000000..68453f7 --- /dev/null +++ b/src/avr/src/stepper.c @@ -0,0 +1,247 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "stepper.h" + +#include "config.h" +#include "motor.h" +#include "hardware.h" +#include "estop.h" +#include "util.h" +#include "cpp_magic.h" +#include "exec.h" +#include "drv8711.h" + +#include + +#include +#include + + +typedef struct { + // Runtime + bool busy; + bool requesting; + float dwell; + uint8_t power_buf; + uint8_t power_index; + + // Move prep + bool move_ready; // Prepped move ready for loader + bool move_queued; // Prepped move queued + float prep_dwell; + int8_t power_next; + + power_update_t powers[2][POWER_MAX_UPDATES]; + + uint32_t underrun; +} stepper_t; + + +static stepper_t st = {0}; + + +void stepper_init() { + // Setup step timer + TIMER_STEP.CTRLB = TC_WGMODE_NORMAL_gc; // Count to TOP & rollover + TIMER_STEP.INTCTRLA = TC_OVFINTLVL_HI_gc; // Interrupt level + TIMER_STEP.PER = STEP_TIMER_POLL; // Timer rate + TIMER_STEP.CTRLA = TC_CLKSEL_DIV8_gc; // Start step timer +} + + +static void _end_move() { + for (int motor = 0; motor < MOTORS; motor++) + motor_end_move(motor); +} + + +static void _load_move() { + for (int motor = 0; motor < MOTORS; motor++) + motor_load_move(motor); +} + + +void st_shutdown() { + TIMER_STEP.CTRLA = 0; // Stop stepper clock + _end_move(); // Stop motor clocks + ADCB_CH0_INTCTRL = 0; // Disable next move interrupt +} + + +/// Return true if motors or dwell are running +bool st_is_busy() {return st.busy;} + + +/// Interrupt handler for calling move exec function. +/// ADC channel 0 triggered by load ISR as a "software" interrupt. +ISR(STEP_LOW_LEVEL_ISR) { + while (true) { + stat_t status = exec_next(); + + switch (status) { + case STAT_NOP: // No move executed, idle + if (!st.busy) { + if (MIN_VELOCITY < exec_get_velocity()) st.underrun++; + exec_set_velocity(0); // Velocity is zero if there are no moves + + spindle_idle(); + } + break; + + case STAT_AGAIN: continue; // No command executed, try again + + case STAT_OK: // Move executed + if (!st.move_queued) + estop_trigger(STAT_EXPECTED_MOVE); // No move was queued + st.move_queued = false; + st.move_ready = true; + break; + + default: estop_trigger(status); break; + } + + break; + } + + ADCB_CH0_INTCTRL = 0; + st.requesting = false; +} + + +static void _request_exec_move() { + if (st.requesting) return; + st.requesting = true; + + // Use ADC as "software" interrupt to trigger next move exec as low interrupt. + ADCB_CH0_INTCTRL = ADC_CH_INTLVL_LO_gc; + ADCB_CTRLA = ADC_ENABLE_bm | ADC_CH0START_bm; +} + + +static void _update_power() { + if (st.power_index < POWER_MAX_UPDATES) + spindle_update(st.powers[st.power_buf][st.power_index++]); +} + + +/// Step timer interrupt routine. +/// Dwell or dequeue and load next move. +ISR(STEP_TIMER_ISR) { + static uint8_t tick = 0; + + // Update spindle power on every tick + _update_power(); + + // Dwell + if (0 < st.dwell) { + st.dwell -= 0.001; // 1ms + return; + } + st.dwell = 0; + + if (tick++ & 3) return; // Proceed every 4 ticks + + // If the next move is not ready try to load it + if (!st.move_ready) { + _request_exec_move(); + _end_move(); + tick = 0; // Try again in 1ms + st.busy = false; + return; + } + + if (st.prep_dwell) { + // End last move, if any + _end_move(); + + // Start dwell + st.dwell = st.prep_dwell; + st.prep_dwell = 0; + + } else { + // Start move + _load_move(); + + // Request next move when not in a dwell. Requesting the next move may + // power up motors which should not be powered up during a dwell. + _request_exec_move(); + } + + // Handle power updates + if (st.power_next != -1) { + st.power_index = 0; + st.power_buf = st.power_next; + st.power_next = -1; + _update_power(); + } + + st.busy = true; // Executing move so mark busy + st.move_ready = false; // We are done with this move, flip the flag back +} + + +void st_prep_power(const power_update_t powers[]) { + ESTOP_ASSERT(!st.move_ready, STAT_STEPPER_NOT_READY); + st.power_next = !st.power_buf; + memcpy(st.powers[st.power_next], powers, + sizeof(power_update_t) * POWER_MAX_UPDATES); +} + + +void st_prep_line(const float target[]) { + // Trap conditions that would prevent queuing the line + ESTOP_ASSERT(!st.move_ready, STAT_STEPPER_NOT_READY); + + // Prepare motor moves + for (int motor = 0; motor < MOTORS; motor++) + motor_prep_move(motor, target[motor_get_axis(motor)]); + + st.move_queued = true; // signal prep buffer ready (do this last) +} + + +/// Add a dwell to the move buffer +void st_prep_dwell(float seconds) { + ESTOP_ASSERT(!st.move_ready, STAT_STEPPER_NOT_READY); + if (seconds <= 1e-4) seconds = 1e-4; // Min dwell + st.power_next = !st.power_buf; + spindle_load_power_updates(st.powers[st.power_next], 0, 0); + st.prep_dwell = seconds; + st.move_queued = true; // signal prep buffer ready +} + + +// Var callbacks +uint32_t get_underrun() {return st.underrun;} + + +float get_dwell_time() { + float dwell; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) dwell = st.dwell; + return dwell; +} diff --git a/src/avr/src/stepper.h b/src/avr/src/stepper.h new file mode 100644 index 0000000..d365e34 --- /dev/null +++ b/src/avr/src/stepper.h @@ -0,0 +1,42 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "spindle.h" + +#include +#include + + +void stepper_init(); +void st_shutdown(); +bool st_is_busy(); +void st_set_power_scale(float scale); +void st_prep_power(const power_update_t powers[]); +void st_prep_line(const float target[]); +void st_prep_dwell(float seconds); diff --git a/src/avr/src/switch.c b/src/avr/src/switch.c new file mode 100644 index 0000000..6994b15 --- /dev/null +++ b/src/avr/src/switch.c @@ -0,0 +1,209 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "switch.h" +#include "config.h" + +#include +#include + + +static struct { + uint16_t debounce; + uint16_t lockout; +} sw = { + .debounce = SWITCH_DEBOUNCE, + .lockout = SWITCH_LOCKOUT, +}; + +typedef struct { + uint8_t pin; + switch_type_t type; + + switch_callback_t cb; + bool state; + uint16_t debounce; + uint16_t lockout; + bool initialized; +} switch_t; + + +// Order must match indices in var functions below +static switch_t switches[] = { + {.pin = ESTOP_PIN, .type = SW_DISABLED}, + {.pin = PROBE_PIN, .type = SW_DISABLED}, + {.pin = MIN_0_PIN, .type = SW_DISABLED}, + {.pin = MAX_0_PIN, .type = SW_DISABLED}, + {.pin = MIN_1_PIN, .type = SW_DISABLED}, + {.pin = MAX_1_PIN, .type = SW_DISABLED}, + {.pin = MIN_2_PIN, .type = SW_DISABLED}, + {.pin = MAX_2_PIN, .type = SW_DISABLED}, + {.pin = MIN_3_PIN, .type = SW_DISABLED}, + {.pin = MAX_3_PIN, .type = SW_DISABLED}, + {.pin = STALL_0_PIN, .type = SW_DISABLED}, + {.pin = STALL_1_PIN, .type = SW_DISABLED}, + {.pin = STALL_2_PIN, .type = SW_DISABLED}, + {.pin = STALL_3_PIN, .type = SW_DISABLED}, + {.pin = MOTOR_FAULT_PIN, .type = SW_DISABLED}, +}; + + +static const int num_switches = sizeof(switches) / sizeof (switch_t); + + +void switch_init() { + for (int i = 0; i < num_switches; i++) { + switch_t *s = &switches[i]; + PINCTRL_PIN(s->pin) = PORT_OPC_PULLUP_gc; // Pull up + DIRCLR_PIN(s->pin); // Input + } +} + + +/// Called from RTC on each tick +void switch_rtc_callback() { + for (int i = 0; i < num_switches; i++) { + switch_t *s = &switches[i]; + + if (s->type == SW_DISABLED) continue; + if (s->lockout && --s->lockout) continue; + + // Debounce switch + bool state = IN_PIN(s->pin); + if (state == s->state && s->initialized) s->debounce = 0; + else if (++s->debounce == sw.debounce) { + s->state = state; + s->debounce = 0; + s->initialized = true; + s->lockout = sw.lockout; + if (s->cb) s->cb((switch_id_t)i, switch_is_active((switch_id_t)i)); + } + } +} + + +bool switch_is_active(switch_id_t sw) { + if (sw < 0 || num_switches <= sw || !switches[sw].initialized) return false; + + // NOTE, switch inputs are active lo + switch (switches[sw].type) { + case SW_DISABLED: break; // A disabled switch cannot be active + case SW_NORMALLY_OPEN: return !switches[sw].state; + case SW_NORMALLY_CLOSED: return switches[sw].state; + } + + return false; +} + + +bool switch_is_enabled(switch_id_t sw) { + return switch_get_type(sw) != SW_DISABLED; +} + + +switch_type_t switch_get_type(switch_id_t sw) { + return (sw < 0 || num_switches <= sw) ? SW_DISABLED : switches[sw].type; +} + + +void switch_set_type(switch_id_t sw, switch_type_t type) { + if (sw < 0 || num_switches <= sw) return; + switch_t *s = &switches[sw]; + + if (s->type != type) { + bool wasActive = switch_is_active(sw); + s->type = type; + bool isActive = switch_is_active(sw); + if (wasActive != isActive && s->cb) s->cb(sw, isActive); + } +} + + +void switch_set_callback(switch_id_t sw, switch_callback_t cb) { + switches[sw].cb = cb; +} + + +// Var callbacks +uint8_t get_min_sw_mode(int index) {return switch_get_type(MIN_SWITCH(index));} + + +void set_min_sw_mode(int index, uint8_t value) { + switch_set_type(MIN_SWITCH(index), (switch_type_t)value); +} + + +uint8_t get_max_sw_mode(int index) {return switch_get_type(MAX_SWITCH(index));} + + +void set_max_sw_mode(int index, uint8_t value) { + switch_set_type(MAX_SWITCH(index), (switch_type_t)value); +} + + +uint8_t get_estop_mode() {return switch_get_type(SW_ESTOP);} + + +void set_estop_mode(uint8_t value) { + switch_set_type(SW_ESTOP, (switch_type_t)value); +} + + +uint8_t get_probe_mode() {return switch_get_type(SW_PROBE);} + + +void set_probe_mode(uint8_t value) { + switch_set_type(SW_PROBE, (switch_type_t)value); +} + + +static uint8_t _get_state(int index) { + if (!switch_is_enabled((switch_id_t)index)) return 2; // Disabled + return switches[index].state; +} + + +uint8_t get_min_switch(int index) {return _get_state(MIN_SWITCH(index));} +uint8_t get_max_switch(int index) {return _get_state(MAX_SWITCH(index));} +uint8_t get_estop_switch() {return _get_state(SW_ESTOP);} +uint8_t get_probe_switch() {return _get_state(SW_PROBE);} + + +void set_switch_debounce(uint16_t debounce) { + sw.debounce = SWITCH_MAX_DEBOUNCE < debounce ? SWITCH_DEBOUNCE : debounce; +} + + +uint16_t get_switch_debounce() {return sw.debounce;} + + +void set_switch_lockout(uint16_t lockout) { + sw.lockout = SWITCH_MAX_LOCKOUT < lockout ? SWITCH_LOCKOUT : lockout; +} + + +uint16_t get_switch_lockout() {return sw.lockout;} diff --git a/src/avr/src/switch.h b/src/avr/src/switch.h new file mode 100644 index 0000000..fb3a9d7 --- /dev/null +++ b/src/avr/src/switch.h @@ -0,0 +1,72 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +#include "config.h" + +#include +#include + + +// macros for finding the index into the switch table give the axis number +#define MIN_SWITCH(axis) ((switch_id_t)(2 + axis * 2)) +#define MAX_SWITCH(axis) ((switch_id_t)(2 + axis * 2 + 1)) + + +typedef enum { + SW_DISABLED, + SW_NORMALLY_OPEN, + SW_NORMALLY_CLOSED, +} switch_type_t; + + +/// Switch IDs +typedef enum { + SW_INVALID = -1, + SW_ESTOP, SW_PROBE, + SW_MIN_0, SW_MAX_0, + SW_MIN_1, SW_MAX_1, + SW_MIN_2, SW_MAX_2, + SW_MIN_3, SW_MAX_3, + SW_STALL_0, SW_STALL_1, + SW_STALL_2, SW_STALL_3, + SW_MOTOR_FAULT, +} switch_id_t; + + +typedef void (*switch_callback_t)(switch_id_t sw, bool active); + + +void switch_init(); +void switch_rtc_callback(); +bool switch_is_active(switch_id_t sw); +bool switch_is_enabled(switch_id_t sw); +switch_type_t switch_get_type(switch_id_t sw); +void switch_set_type(switch_id_t sw, switch_type_t type); +void switch_set_callback(switch_id_t sw, switch_callback_t cb); diff --git a/src/avr/src/type.c b/src/avr/src/type.c new file mode 100644 index 0000000..272e1f6 --- /dev/null +++ b/src/avr/src/type.c @@ -0,0 +1,216 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "type.h" +#include "base64.h" + +#include +#include +#include +#include +#include +#include + + +#define TYPEDEF(TYPE, DEF) \ + static const char TYPE##_name [] PROGMEM = "<" #TYPE ">"; \ + pstr type_get_##TYPE##_name_pgm() {return TYPE##_name;} + +#include "type.def" +#undef TYPEDEF + + +// String +bool type_eq_str(str a, str b) {return a == b;} +void type_print_str(str s) {printf_P(PSTR("\"%s\""), s);} +str type_parse_str(const char *s, stat_t *) {return s;} + +// Program string +bool type_eq_pstr(pstr a, pstr b) {return a == b;} +void type_print_pstr(pstr s) {printf_P(PSTR("\"%" PRPSTR "\""), s);} +const char *type_parse_pstr(const char *value, stat_t *) {return value;} + + +// Float +bool type_eq_f32(float a, float b) {return a == b || (isnan(a) && isnan(b));} + + +void type_print_f32(float x) { + if (isnan(x)) printf_P(PSTR("\"nan\"")); + else if (isinf(x)) printf_P(PSTR("\"%cinf\""), x < 0 ? '-' : '+'); + + else { + char buf[20]; + + int len = sprintf_P(buf, PSTR("%.3f"), x); + + // Remove trailing zeros + for (int i = len; 0 < i; i--) { + if (buf[i - 1] == '.') buf[i - 1] = 0; + else if (buf[i - 1] == '0') { + buf[i - 1] = 0; + continue; + } + + break; + } + + printf(buf); + } +} + + +float type_parse_f32(const char *value, stat_t *status) { + while (*value && isspace(*value)) value++; + + if (*value == ':') { + value++; + if (strnlen(value, 6) != 6) { + if (status) *status = STAT_INVALID_VALUE; + return NAN; + } + + float f; + if (b64_decode_float(value, &f)) return f; + else { + if (status) *status = STAT_BAD_FLOAT; + return NAN; + } + } + + char *endptr = 0; + float x = strtod(value, &endptr); + + if (endptr == value) { + if (status) *status = STAT_BAD_FLOAT; + return NAN; + } + + return x; +} + + +// bool +bool type_eq_b8(bool a, bool b) {return a == b;} +void type_print_b8(bool x) {printf_P(x ? PSTR("true") : PSTR("false"));} + + +bool type_parse_b8(const char *value, stat_t *status) { + return !strcasecmp(value, "true") || type_parse_f32(value, status); +} + + +// s8 +bool type_eq_s8(s8 a, s8 b) {return a == b;} +void type_print_s8(s8 x) {printf_P(PSTR("%" PRIi8), x);} + + +s8 type_parse_s8(const char *value, stat_t *status) { + char *endptr = 0; + s8 x = strtol(value, &endptr, 0); + if (endptr == value && status) *status = STAT_BAD_INT; + return x; +} + + +// u8 +bool type_eq_u8(u8 a, u8 b) {return a == b;} +void type_print_u8(u8 x) {printf_P(PSTR("%" PRIu8), x);} + + +u8 type_parse_u8(const char *value, stat_t *status) { + char *endptr = 0; + u8 x = strtoul(value, &endptr, 0); + if (endptr == value && status) *status = STAT_BAD_INT; + return x; +} + + +// u16 +bool type_eq_u16(u16 a, u16 b) {return a == b;} +void type_print_u16(u16 x) {printf_P(PSTR("%" PRIu16), x);} + + +u16 type_parse_u16(const char *value, stat_t *status) { + char *endptr = 0; + u16 x = strtoul(value, &endptr, 0); + if (endptr == value && status) *status = STAT_BAD_INT; + return x; +} + + +// s32 +bool type_eq_s32(s32 a, s32 b) {return a == b;} +void type_print_s32(s32 x) {printf_P(PSTR("%" PRIi32), x);} + + +s32 type_parse_s32(const char *value, stat_t *status) { + char *endptr = 0; + s32 x = strtol(value, &endptr, 0); + if (endptr == value && status) *status = STAT_BAD_INT; + return x; +} + + +// u32 +bool type_eq_u32(u32 a, u32 b) {return a == b;} +void type_print_u32(u32 x) {printf_P(PSTR("%" PRIu32), x);} + + +u32 type_parse_u32(const char *value, stat_t *status) { + char *endptr = 0; + u32 x = strtoul(value, &endptr, 0); + if (endptr == value && status) *status = STAT_BAD_INT; + return x; +} + + +type_u type_parse(type_t type, const char *s, stat_t *status) { + type_u value; + + if (status) *status = STAT_OK; + + switch (type) { +#define TYPEDEF(TYPE, ...) \ + case TYPE_##TYPE: value._##TYPE = type_parse_##TYPE(s, status); break; +#include "type.def" +#undef TYPEDEF + default: if (status) *status = STAT_INVALID_TYPE; + } + + return value; +} + + +void type_print(type_t type, type_u value) { + switch (type) { +#define TYPEDEF(TYPE, ...) \ + case TYPE_##TYPE: type_print_##TYPE(value._##TYPE); break; +#include "type.def" +#undef TYPEDEF + } +} diff --git a/src/avr/src/type.def b/src/avr/src/type.def new file mode 100644 index 0000000..5f350fa --- /dev/null +++ b/src/avr/src/type.def @@ -0,0 +1,37 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +// TYPE DEF +TYPEDEF(str, const char *) +TYPEDEF(pstr, PGM_P) +TYPEDEF(f32, float) +TYPEDEF(u8, uint8_t) +TYPEDEF(s8, int8_t) +TYPEDEF(u16, uint16_t) +TYPEDEF(s32, int32_t) +TYPEDEF(u32, uint32_t) +TYPEDEF(b8, bool) diff --git a/src/avr/src/type.h b/src/avr/src/type.h new file mode 100644 index 0000000..322ebd6 --- /dev/null +++ b/src/avr/src/type.h @@ -0,0 +1,67 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "pgmspace.h" +#include "status.h" + +#include +#include + + +// Define types +#define TYPEDEF(TYPE, DEF) typedef DEF TYPE; +#include "type.def" +#undef TYPEDEF + +typedef enum { +#define TYPEDEF(TYPE, ...) TYPE_##TYPE, +#include "type.def" +#undef TYPEDEF +} type_t; + + +typedef union { +#define TYPEDEF(TYPE, ...) TYPE _##TYPE; +#include "type.def" +#undef TYPEDEF +} type_u; + + +// Define functions +#define TYPEDEF(TYPE, DEF) \ + pstr type_get_##TYPE##_name_pgm(); \ + bool type_eq_##TYPE(TYPE a, TYPE b); \ + TYPE type_parse_##TYPE(const char *s, stat_t *status); \ + void type_print_##TYPE(TYPE x); +#include "type.def" +#undef TYPEDEF + + +type_u type_parse(type_t type, const char *s, stat_t *status); +void type_print(type_t type, type_u value); diff --git a/src/avr/src/usart.c b/src/avr/src/usart.c new file mode 100644 index 0000000..d035c0e --- /dev/null +++ b/src/avr/src/usart.c @@ -0,0 +1,285 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "usart.h" +#include "cpp_magic.h" +#include "config.h" + +#include +#include + +#include +#include +#include + + +// Ring buffers +#define RING_BUF_INDEX_TYPE volatile uint16_t +#define RING_BUF_NAME tx_buf +#define RING_BUF_SIZE USART_TX_RING_BUF_SIZE +#define RING_BUF_ATOMIC_COPY 1 +#include "ringbuf.def" + +#define RING_BUF_INDEX_TYPE volatile uint16_t +#define RING_BUF_NAME rx_buf +#define RING_BUF_SIZE USART_RX_RING_BUF_SIZE +#define RING_BUF_ATOMIC_COPY 1 +#include "ringbuf.def" + +static bool _flush = false; + + +static void _set_dre_interrupt(bool enable) { + if (enable) SERIAL_PORT.CTRLA |= USART_DREINTLVL_MED_gc; + else SERIAL_PORT.CTRLA &= ~USART_DREINTLVL_MED_gc; +} + + +static void _set_rxc_interrupt(bool enable) { + if (enable) { + if (SERIAL_CTS_THRESH <= rx_buf_space()) + OUTCLR_PIN(SERIAL_CTS_PIN); // CTS Lo (enable) + + SERIAL_PORT.CTRLA |= USART_RXCINTLVL_HI_gc; + + } else SERIAL_PORT.CTRLA &= ~USART_RXCINTLVL_HI_gc; +} + + +// Data register empty interrupt vector +ISR(SERIAL_DRE_vect) { + if (tx_buf_empty()) _set_dre_interrupt(false); // Disable interrupt + + else { + SERIAL_PORT.DATA = tx_buf_peek(); + tx_buf_pop(); + } +} + + +// Data received interrupt vector +ISR(SERIAL_RXC_vect) { + if (rx_buf_full()) _set_rxc_interrupt(false); // Disable interrupt + else rx_buf_push(SERIAL_PORT.DATA); + + if (rx_buf_space() < SERIAL_CTS_THRESH) + OUTSET_PIN(SERIAL_CTS_PIN); // CTS Hi (disable) +} + + +#ifdef __AVR__ +static int _usart_putchar(char c, FILE *f) { + usart_putc(c); + return 0; +} +#endif // __AVR__ + + +static void _set_baud(USART_t *port, uint16_t bsel, uint8_t bscale) { + port->BAUDCTRLB = (uint8_t)((bscale << 4) | (bsel >> 8)); + port->BAUDCTRLA = bsel; + port->CTRLB |= USART_CLK2X_bm; +} + + +void usart_set_baud(USART_t *port, baud_t baud) { + // The BSEL / BSCALE values provided below assume a 32 Mhz clock + // With CTRLB CLK2X is set + // See http://www.avrcalc.elektronik-projekt.de/xmega/baud_rate_calculator + + switch (baud) { + case USART_BAUD_9600: _set_baud(port, 3325, 0b1101); break; + case USART_BAUD_19200: _set_baud(port, 3317, 0b1100); break; + case USART_BAUD_38400: _set_baud(port, 3301, 0b1011); break; + case USART_BAUD_57600: _set_baud(port, 1095, 0b1100); break; + case USART_BAUD_115200: _set_baud(port, 1079, 0b1011); break; + case USART_BAUD_230400: _set_baud(port, 1047, 0b1010); break; + case USART_BAUD_460800: _set_baud(port, 983, 0b1001); break; + case USART_BAUD_921600: _set_baud(port, 107, 0b1011); break; + case USART_BAUD_500000: _set_baud(port, 1, 0b0010); break; + case USART_BAUD_1000000: _set_baud(port, 1, 0b0001); break; + } +} + + +void usart_set_parity(USART_t *port, parity_t parity) { + uint8_t reg = port->CTRLC & ~USART_PMODE_gm; + + switch (parity) { + case USART_NONE: reg |= USART_PMODE_DISABLED_gc; break; + case USART_EVEN: reg |= USART_PMODE_EVEN_gc; break; + case USART_ODD: reg |= USART_PMODE_ODD_gc; break; + } + + port->CTRLC = reg; +} + + +void usart_set_stop(USART_t *port, stop_t stop) { + switch (stop) { + case USART_1STOP: port->CTRLC &= ~USART_SBMODE_bm; break; + case USART_2STOP: port->CTRLC |= USART_SBMODE_bm; break; + } +} + + +void usart_set_bits(USART_t *port, bits_t bits) { + uint8_t reg = port->CTRLC & ~USART_CHSIZE_gm; + + switch (bits) { + case USART_5BITS: reg |= USART_CHSIZE_5BIT_gc; break; + case USART_6BITS: reg |= USART_CHSIZE_6BIT_gc; break; + case USART_7BITS: reg |= USART_CHSIZE_7BIT_gc; break; + case USART_8BITS: reg |= USART_CHSIZE_8BIT_gc; break; + case USART_9BITS: reg |= USART_CHSIZE_9BIT_gc; break; + } + + port->CTRLC = reg; +} + + +void usart_init_port(USART_t *port, baud_t baud, parity_t parity, bits_t bits, + stop_t stop) { + // Set baud rate + usart_set_baud(port, baud); + + // Async, no parity, 8 data bits, 1 stop bit + port->CTRLC = USART_CMODE_ASYNCHRONOUS_gc; + usart_set_parity(port, parity); + usart_set_bits(port, bits); + usart_set_stop(port, stop); + + // Configure receiver and transmitter + port->CTRLB |= USART_RXEN_bm | USART_TXEN_bm; +} + + +void usart_init() { + // Setup ring buffer + tx_buf_init(); + rx_buf_init(); + + PR.PRPC &= ~PR_USART0_bm; // Disable power reduction + + // Setup pins + OUTSET_PIN(SERIAL_CTS_PIN); // CTS Hi (disable) + DIRSET_PIN(SERIAL_CTS_PIN); // CTS Output + OUTSET_PIN(SERIAL_TX_PIN); // Tx High + DIRSET_PIN(SERIAL_TX_PIN); // Tx Output + DIRCLR_PIN(SERIAL_RX_PIN); // Rx Input + + // Configure port + usart_init_port(&SERIAL_PORT, SERIAL_BAUD, USART_NONE, USART_8BITS, + USART_1STOP); + + PMIC.CTRL |= PMIC_HILVLEN_bm; // Interrupt level on + +#ifdef __AVR__ + // Connect IO + static FILE _stdout; + memset(&_stdout, 0, sizeof(FILE)); + _stdout.put = _usart_putchar; + _stdout.flags = _FDEV_SETUP_WRITE; + + stdout = &_stdout; + stderr = &_stdout; +#endif // __AVR__ + + // Enable Rx + _set_rxc_interrupt(true); +} + + + +void usart_putc(char c) { + while (tx_buf_full() || _flush) continue; + tx_buf_push(c); + _set_dre_interrupt(true); // Enable interrupt +} + + +void usart_puts(const char *s) {while (*s) usart_putc(*s++);} + + +int8_t usart_getc() { + while (rx_buf_empty()) continue; + uint8_t data = rx_buf_next(); + _set_rxc_interrupt(true); // Enable interrupt + return data; +} + + +/*** Line editing features: + * + * ENTER Submit current command line. + * BS Backspace, delete last character. + * CTRL-X Cancel current line entry. + */ +char *usart_readline() { + static char line[INPUT_BUFFER_LEN]; + static int i = 0; + bool eol = false; + + while (!rx_buf_empty()) { + char data = usart_getc(); + + switch (data) { + case '\r': case '\n': eol = true; break; + case '\b': if (i) i--; break; // BS - backspace + case 0x18: i = 0; break; // CAN - Cancel or CTRL-X + + default: + line[i++] = data; + if (i == INPUT_BUFFER_LEN - 1) eol = true; // Line buffer full + break; + } + + if (eol) { + line[i] = 0; + i = 0; + return line; + } + } + + return 0; +} + + +void usart_flush() { + _flush = true; + + while (!tx_buf_empty() || !(SERIAL_PORT.STATUS & USART_DREIF_bm) || + !(SERIAL_PORT.STATUS & USART_TXCIF_bm)) + continue; +} + + +void usart_rx_flush() {rx_buf_init();} +int16_t usart_rx_space() {return rx_buf_space();} +int16_t usart_rx_fill() {return rx_buf_fill();} +int16_t usart_tx_space() {return tx_buf_space();} +int16_t usart_tx_fill() {return tx_buf_fill();} diff --git a/src/avr/src/usart.h b/src/avr/src/usart.h new file mode 100644 index 0000000..b59f4ed --- /dev/null +++ b/src/avr/src/usart.h @@ -0,0 +1,100 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include + +#include +#include + + +// NOTE, RING_BUF_INDEX_TYPE must be be large enough to cover the buffer +#define USART_TX_RING_BUF_SIZE 1024 +#define USART_RX_RING_BUF_SIZE 1024 + + +typedef enum { + USART_BAUD_9600, + USART_BAUD_19200, + USART_BAUD_38400, + USART_BAUD_57600, + USART_BAUD_115200, + USART_BAUD_230400, + USART_BAUD_460800, + USART_BAUD_921600, + USART_BAUD_500000, + USART_BAUD_1000000 +} baud_t; + + +typedef enum { + USART_NONE, + USART_EVEN, + USART_ODD, +} parity_t; + + +typedef enum { + USART_1STOP, + USART_2STOP, +} stop_t; + + +typedef enum { + USART_5BITS, + USART_6BITS, + USART_7BITS, + USART_8BITS, + USART_9BITS, +} bits_t; + + +void usart_set_baud(USART_t *port, baud_t baud); +void usart_set_parity(USART_t *port, parity_t parity); +void usart_set_stop(USART_t *port, stop_t stop); +void usart_set_bits(USART_t *port, bits_t bits); +void usart_init_port(USART_t *port, baud_t baud, parity_t parity, bits_t bits, + stop_t stop); + +void usart_init(); +void usart_putc(char c); +void usart_puts(const char *s); +int8_t usart_getc(); +char *usart_readline(); +void usart_flush(); + +void usart_rx_flush(); +int16_t usart_rx_fill(); +int16_t usart_rx_space(); +inline bool usart_rx_empty() {return !usart_rx_fill();} +inline bool usart_rx_full() {return !usart_rx_space();} + +int16_t usart_tx_fill(); +int16_t usart_tx_space(); +inline bool usart_tx_empty() {return !usart_tx_fill();} +inline bool usart_tx_full() {return !usart_tx_space();} diff --git a/src/avr/src/util.c b/src/avr/src/util.c new file mode 100644 index 0000000..61137fe --- /dev/null +++ b/src/avr/src/util.c @@ -0,0 +1,95 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "util.h" + +#include "base64.h" + +#include +#include +#include +#include + + +/// Fast inverse square root originally from Quake III Arena code. Original +/// comments left intact. +/// See: https://en.wikipedia.org/wiki/Fast_inverse_square_root +float invsqrt(float x) { + // evil floating point bit level hacking + union { + float f; + int32_t i; + } u; + + const float xhalf = x * 0.5f; + u.f = x; + u.i = 0x5f3759df - (u.i >> 1); // what the fuck? + u.f = u.f * (1.5f - xhalf * u.f * u.f); // 1st iteration + u.f = u.f * (1.5f - xhalf * u.f * u.f); // 2nd iteration, can be removed + + return u.f; +} + + +int8_t decode_hex_nibble(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return c - 'a' + 10; + if ('A' <= c && c <= 'F') return c - 'A' + 10; + return -1; +} + + +bool decode_float(char **s, float *f) { + bool ok = b64_decode_float(*s, f) && isfinite(*f); + *s += 6; + return ok; +} + + +stat_t decode_axes(char **cmd, float axes[AXES]) { + while (**cmd) { + const char *names = "xyzabc"; + const char *match = strchr(names, **cmd); + if (!match) break; + char *s = *cmd + 1; + if (!decode_float(&s, &axes[match - names])) return STAT_BAD_FLOAT; + *cmd = s; + } + + return STAT_OK; +} + + +// Assumes the caller provide format buffer length is @param len * 2 + 1. +void format_hex_buf(char *buf, const uint8_t *data, unsigned len) { + uint8_t i; + + for (i = 0; i < len; i++) + sprintf(buf + i * 2, "%02x", data[i]); + + buf[i * 2] = 0; +} diff --git a/src/avr/src/util.h b/src/avr/src/util.h new file mode 100644 index 0000000..300fb1a --- /dev/null +++ b/src/avr/src/util.h @@ -0,0 +1,85 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +#include "config.h" +#include "status.h" + +#include +#include +#include +#include + + +// Vector utilities +#define clear_vector(a) memset(a, 0, sizeof(a)) +#define copy_vector(d, s) memcpy(d, s, sizeof(d)) + +// Math utilities +inline static float min(float a, float b) {return a < b ? a : b;} +inline static float max(float a, float b) {return a < b ? b : a;} +inline static float min3(float a, float b, float c) {return min(min(a, b), c);} +inline static float max3(float a, float b, float c) {return max(max(a, b), c);} +inline static float min4(float a, float b, float c, float d) +{return min(min(a, b), min(c, d));} +inline static float max4(float a, float b, float c, float d) +{return max(max(a, b), max(c, d));} + +float invsqrt(float number); + +#ifndef __AVR__ +inline static float square(float x) {return x * x;} +#endif + +// Floating-point utilities +#define EPSILON 0.00001 // allowable rounding error for floats +inline static bool fp_EQ(float a, float b) {return fabs(a - b) < EPSILON;} +inline static bool fp_NE(float a, float b) {return fabs(a - b) > EPSILON;} +inline static bool fp_ZERO(float a) {return fabs(a) < EPSILON;} +inline static bool fp_FALSE(float a) {return fp_ZERO(a);} +inline static bool fp_TRUE(float a) {return !fp_ZERO(a);} + +int8_t decode_hex_nibble(char c); +bool decode_float(char **s, float *f); +stat_t decode_axes(char **cmd, float axes[AXES]); +void format_hex_buf(char *buf, const uint8_t *data, unsigned len); + +// Constants +#define MM_PER_INCH 25.4 +#define INCHES_PER_MM (1 / 25.4) +#define MICROSECONDS_PER_MINUTE 60000000.0 + +// 24bit integers +#ifdef __AVR__ +typedef __int24 int24_t; +typedef __uint24 uint24_t; +#else +typedef int32_t int24_t; +typedef uint32_t uint24_t; +#endif diff --git a/src/avr/src/vars.c b/src/avr/src/vars.c new file mode 100644 index 0000000..244ae08 --- /dev/null +++ b/src/avr/src/vars.c @@ -0,0 +1,411 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "vars.h" + +#include "type.h" +#include "status.h" +#include "hardware.h" +#include "config.h" +#include "axis.h" +#include "cpp_magic.h" +#include "report.h" +#include "command.h" + +#include +#include + +// Format strings +static const char code_fmt[] PROGMEM = "\"%s\":"; +static const char indexed_code_fmt[] PROGMEM = "\"%c%s\":"; + + +// Ensure no var code is used more than once +enum { +#define VAR(NAME, CODE, ...) var_code_##CODE, +#include "vars.def" +#undef VAR + var_code_count +}; + + +// Var forward declarations +#define VAR(NAME, CODE, TYPE, INDEX, SET, ...) \ + TYPE get_##NAME(IF(INDEX)(int index)); \ + IF(SET) \ + (void set_##NAME(IF(INDEX)(int index,) TYPE value);) + +#include "vars.def" +#undef VAR + + +// Set callback union +typedef union { + void *ptr; +#define TYPEDEF(TYPE, ...) void (*set_##TYPE)(TYPE); +#include "type.def" +#undef TYPEDEF + +#define TYPEDEF(TYPE, ...) void (*set_##TYPE##_index)(int i, TYPE); +#include "type.def" +#undef TYPEDEF +} set_cb_u; + + +// Get callback union +typedef union { + void *ptr; +#define TYPEDEF(TYPE, ...) TYPE (*get_##TYPE)(); +#include "type.def" +#undef TYPEDEF + +#define TYPEDEF(TYPE, ...) TYPE (*get_##TYPE##_index)(int i); +#include "type.def" +#undef TYPEDEF +} get_cb_u; + + +typedef struct { + type_t type; + char name[5]; + int8_t index; + get_cb_u get; + set_cb_u set; +} var_info_t; + + +// Var names +#define VAR(NAME, CODE, TYPE, INDEX, SET, REPORT) \ + static const char NAME##_name[] PROGMEM = #NAME; + +#include "vars.def" +#undef VAR + + +// Last value +#define VAR(NAME, CODE, TYPE, INDEX, ...) \ + static TYPE NAME##_state IF(INDEX)([INDEX]); + +#include "vars.def" +#undef VAR + + +// Report +static uint8_t _report_var[(var_code_count >> 3) + 1] = {0,}; + + +static bool _get_report_var(int index) { + return _report_var[index >> 3] & (1 << (index & 7)); +} + + +static void _set_report_var(int index, bool enable) { + if (enable) _report_var[index >> 3] |= 1 << (index & 7); + else _report_var[index >> 3] &= ~(1 << (index & 7)); +} + + +static int _find_code(const char *code) { +#define VAR(NAME, CODE, TYPE, INDEX, ...) \ + if (!strcmp(code, #CODE)) return var_code_##CODE; \ + +#include "vars.def" +#undef VAR + return -1; +} + + +void vars_init() { + // Initialize var state +#define VAR(NAME, CODE, TYPE, INDEX, ...) \ + IF(INDEX)(for (int i = 0; i < INDEX; i++)) \ + (NAME##_state)IF(INDEX)([i]) = get_##NAME(IF(INDEX)(i)); + +#include "vars.def" +#undef VAR + +// Report +#define VAR(NAME, CODE, TYPE, INDEX, SET, REPORT, ...) \ + _set_report_var(var_code_##CODE, REPORT); + +#include "vars.def" +#undef VAR +} + + +void vars_report(bool full) { + bool reported = false; + +#define VAR(NAME, CODE, TYPE, INDEX, ...) \ + if (_get_report_var(var_code_##CODE)) { \ + IF(INDEX)(for (int i = 0; i < (INDEX ? INDEX : 1); i++)) { \ + TYPE value = get_##NAME(IF(INDEX)(i)); \ + TYPE last = (NAME##_state)IF(INDEX)([i]); \ + \ + if (full || (!type_eq_##TYPE(value, last))) { \ + (NAME##_state)IF(INDEX)([i]) = value; \ + \ + if (!reported) { \ + reported = true; \ + putchar('{'); \ + } else putchar(','); \ + \ + printf_P \ + (IF_ELSE(INDEX)(indexed_code_fmt, code_fmt), \ + IF(INDEX)(INDEX##_LABEL[i],) #CODE); \ + \ + type_print_##TYPE(value); \ + } \ + } \ + } + +#include "vars.def" +#undef VAR + + if (reported) printf("}\n"); +} + +void vars_report_all(bool enable) { +#define VAR(NAME, CODE, TYPE, INDEX, SET, REPORT, ...) \ + _set_report_var(var_code_##CODE, enable); + +#include "vars.def" +#undef VAR +} + + +void vars_report_var(const char *code, bool enable) { + int index = _find_code(code); + if (index != -1) _set_report_var(index, enable); +} + + +static char *_resolve_name(const char *_name) { + unsigned len = strlen(_name); + + if (!len || 4 < len) return 0; + + static char name[5]; + strncpy(name, _name, 4); + name[4] = 0; + + // Handle axis to motor mapping + if (2 < len && name[1] == '.') { + int axis = axis_get_id(name[0]); + if (axis < 0) return 0; + int motor = axis_get_motor(axis); + if (motor < 0) return 0; + + name[0] = MOTORS_LABEL[motor]; + for (int i = 1; _name[i]; i++) + name[i] = _name[i + 1]; + } + + return name; +} + + +static int _index(char c, const char *s) { + const char *index = strchr(s, c); + return index ? index - s : -1; +} + + +static bool _find_var(const char *_name, var_info_t *info) { + char *name = _resolve_name(_name); + if (!name) return false; + + int i = -1; + memset(info, 0, sizeof(var_info_t)); + strcpy(info->name, name); + +#define VAR(NAME, CODE, TYPE, INDEX, SET, ...) \ + if (!strcmp(IF_ELSE(INDEX)(name + 1, name), #CODE) \ + IF(INDEX)(&& (i = _index(name[0], INDEX##_LABEL)) != -1) \ + ) { \ + \ + info->type = TYPE_##TYPE; \ + info->index = i; \ + info->get.IF_ELSE(INDEX)(get_##TYPE##_index, get_##TYPE) = \ + get_##NAME; \ + \ + IF(SET)(info->set.IF_ELSE(INDEX) \ + (set_##TYPE##_index, set_##TYPE) = set_##NAME;) \ + \ + return true; \ + } + +#include "vars.def" +#undef VAR + + return false; +} + + +static type_u _get(type_t type, int8_t index, get_cb_u cb) { + type_u value; + + switch (type) { +#define TYPEDEF(TYPE, ...) \ + case TYPE_##TYPE: \ + if (index == -1) value._##TYPE = cb.get_##TYPE(); \ + value._##TYPE = cb.get_##TYPE##_index(index); \ + break; +#include "type.def" +#undef TYPEDEF + } + + return value; +} + + +static void _set(type_t type, int8_t index, set_cb_u cb, type_u value) { + switch (type) { +#define TYPEDEF(TYPE, ...) \ + case TYPE_##TYPE: \ + if (index == -1) cb.set_##TYPE(value._##TYPE); \ + else cb.set_##TYPE##_index(index, value._##TYPE); \ + break; +#include "type.def" +#undef TYPEDEF + } +} + + +stat_t vars_print(const char *name) { + var_info_t info; + if (!_find_var(name, &info)) return STAT_UNRECOGNIZED_NAME; + + printf_P(PSTR("{\"%s\":"), info.name); + type_print(info.type, _get(info.type, info.index, info.get)); + putchar('}'); + putchar('\n'); + + return STAT_OK; +} + + +stat_t vars_set(const char *name, const char *value) { + var_info_t info; + if (!_find_var(name, &info)) return STAT_UNRECOGNIZED_NAME; + if (!info.set.ptr) return STAT_READ_ONLY; + + stat_t status; + type_u x = type_parse(info.type, value, &status); + if (status == STAT_OK) _set(info.type, info.index, info.set, x); + + return status; +} + + +void vars_print_json() { + bool first = true; + static const char fmt[] PROGMEM = + "\"%s\":{\"name\":\"%" PRPSTR "\",\"type\":\"%" PRPSTR "\""; + static const char index_fmt[] PROGMEM = ",\"index\":\"%s\""; + +#define VAR(NAME, CODE, TYPE, INDEX, ...) \ + if (first) first = false; else putchar(','); \ + printf_P(fmt, #CODE, NAME##_name, type_get_##TYPE##_name_pgm()); \ + IF(INDEX)(printf_P(index_fmt, INDEX##_LABEL)); \ + putchar('}'); +#include "vars.def" +#undef VAR +} + + +// Command callbacks +stat_t command_var(char *cmd) { + cmd++; // Skip command code + + if (*cmd == '$' && !cmd[1]) { + report_request_full(); + return STAT_OK; + } + + // Get or set variable + char *value = strchr(cmd, '='); + if (value) { + *value++ = 0; + stat_t status = vars_set(cmd, value); + value[-1] = '='; // For error reporting + return status; + } + + return vars_print(cmd); +} + + +typedef struct { + type_t type; + int8_t index; + set_cb_u set; + type_u value; +} var_cmd_t; + + +stat_t command_sync_var(char *cmd) { + // Get value + char *value = strchr(cmd + 1, '='); + if (!value) return STAT_INVALID_COMMAND; + *value++ = 0; + + var_info_t info; + if (!_find_var(cmd + 1, &info)) return STAT_UNRECOGNIZED_NAME; + if (!info.set.ptr) return STAT_READ_ONLY; + + stat_t status; + var_cmd_t buffer; + + buffer.type = info.type; + buffer.index = info.index; + buffer.set = info.set; + buffer.value = type_parse(info.type, value, &status); + + if (status == STAT_OK) command_push(*cmd, &buffer); + + return status; +} + + +unsigned command_sync_var_size() {return sizeof(var_cmd_t);} + + +void command_sync_var_exec(void *data) { + var_cmd_t *cmd = (var_cmd_t *)data; + _set(cmd->type, cmd->index, cmd->set, cmd->value); +} + + +stat_t command_report(char *cmd) { + bool enable = cmd[1] != '0'; + + if (cmd[2]) vars_report_var(cmd + 2, enable); + else vars_report_all(enable); + + return STAT_OK; +} diff --git a/src/avr/src/vars.def b/src/avr/src/vars.def new file mode 100644 index 0000000..ecb48a0 --- /dev/null +++ b/src/avr/src/vars.def @@ -0,0 +1,150 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#define AXES_LABEL "xyzabc" +#define MOTORS_LABEL "0123" +#define OUTS_LABEL "ed12ft" +#define ANALOG_LABEL "12" +#define VFDREG_LABEL "0123456789abcdefghijklmnopqrstuv" + +// VAR(name, code, type, index, settable, report) + +// Motor +VAR(motor_axis, an, u8, MOTORS, 1, 1) // Maps motor to axis + +VAR(motor_enabled, me, b8, MOTORS, 1, 1) // Motor enabled +VAR(drive_current, dc, f32, MOTORS, 1, 1) // Max motor drive current +VAR(idle_current, ic, f32, MOTORS, 1, 1) // Motor idle current + +VAR(reverse, rv, b8, MOTORS, 1, 1) // Reverse motor polarity +VAR(microstep, mi, u16, MOTORS, 1, 1) // Microsteps per full step +VAR(velocity_max, vm, f32, MOTORS, 1, 1) // Maxium vel in mm/min +VAR(accel_max, am, f32, MOTORS, 1, 1) // Maxium accel in mm/min^2 +VAR(jerk_max, jm, f32, MOTORS, 1, 1) // Maxium jerk in mm/min^3 +VAR(step_angle, sa, f32, MOTORS, 1, 1) // In degrees per full step +VAR(travel, tr, f32, MOTORS, 1, 1) // Travel in mm/rev + +VAR(min_soft_limit, tn, f32, MOTORS, 1, 1) // Min soft limit +VAR(max_soft_limit, tm, f32, MOTORS, 1, 1) // Max soft limit +VAR(homed, h, b8, MOTORS, 1, 1) // Motor homed status + +VAR(active_current, ac, f32, MOTORS, 0, 0) // Motor current now +VAR(driver_flags, df, u16, MOTORS, 1, 1) // Motor driver flags +VAR(driver_stalled, sl, b8, MOTORS, 0, 0) // Motor driver status +VAR(stall_volts, tv, f32, MOTORS, 1, 1) // Motor BEMF threshold voltage +VAR(encoder, en, s32, MOTORS, 0, 0) // Motor encoder +VAR(error, ee, s32, MOTORS, 0, 0) // Motor position error + +VAR(stall_microstep, lm, u16, MOTORS, 1, 1) // Motor stall microsteps +VAR(stall_sample_time,sp,u16, MOTORS, 1, 1) // Motor stall sample time +VAR(stall_current, tc, f32, MOTORS, 1, 1) // Motor stall current + +VAR(motor_fault, fa, b8, 0, 0, 1) // Motor fault status + +// Switches +VAR(min_sw_mode, ls, u8, MOTORS, 1, 1) // Minimum switch mode +VAR(max_sw_mode, xs, u8, MOTORS, 1, 1) // Maximum switch mode +VAR(estop_mode, et, u8, 0, 1, 1) // Estop switch mode +VAR(probe_mode, pt, u8, 0, 1, 1) // Probe switch mode +VAR(min_switch, lw, u8, MOTORS, 0, 1) // Minimum switch state +VAR(max_switch, xw, u8, MOTORS, 0, 1) // Maximum switch state +VAR(estop_switch, ew, u8, 0, 0, 1) // Estop switch state +VAR(probe_switch, pw, u8, 0, 0, 1) // Probe switch state +VAR(switch_debounce, sd, u16, 0, 1, 1) // Switch debounce time in ms +VAR(switch_lockout, sc, u16, 0, 1, 1) // Switch lockout time in ms + +// Axis +VAR(axis_position, p, f32, AXES, 0, 1) // Axis position + +// Outputs +VAR(output_active, oa, b8, OUTS, 1, 1) // Output pin active +VAR(output_state, os, u8, OUTS, 0, 1) // Output pin state +VAR(output_mode, om, u8, OUTS, 1, 1) // Output pin mode + +// Analog +VAR(analog_input, ai, f32, ANALOG, 0, 0) // Analog input pins + +// Spindle +VAR(tool_type, st, u8, 0, 1, 1) // See spindle.c +VAR(speed, s, f32, 0, 0, 1) // Current spindle speed +VAR(tool_reversed, sr, b8, 0, 1, 1) // Reverse tool +VAR(max_spin, sx, f32, 0, 1, 1) // Maximum spindle speed +VAR(min_spin, sm, f32, 0, 1, 1) // Minimum spindle speed +VAR(spindle_status, ss, u16, 0, 0, 1) // Spindle status code + +// PWM spindle +VAR(pwm_invert, pi, b8, 0, 1, 1) // Inverted spindle PWM +VAR(pwm_min_duty, nd, f32, 0, 1, 1) // Minimum PWM duty cycle +VAR(pwm_max_duty, md, f32, 0, 1, 1) // Maximum PWM duty cycle +VAR(pwm_duty, pd, f32, 0, 0, 0) // Current PWM duty cycle +VAR(pwm_freq, sf, f32, 0, 1, 0) // Spindle PWM frequency in Hz + +// Modbus spindle +VAR(mb_debug, hb, b8, 0, 1, 1) // Modbus debugging +VAR(mb_id, hi, u8, 0, 1, 1) // Modbus ID +VAR(mb_baud, mb, u8, 0, 1, 1) // Modbus BAUD rate +VAR(mb_parity, ma, u8, 0, 1, 1) // Modbus parity +VAR(mb_status, mx, u8, 0, 0, 1) // Modbus status +VAR(mb_crc_errs, cr, u16, 0, 0, 1) // Modbus CRC error counter + +// VFD spindle +VAR(vfd_max_freq, vf, u16, 0, 1, 1) // VFD maximum frequency +VAR(vfd_multi_write, mw, b8, 0, 1, 1) // Use Modbus multi write mode +VAR(vfd_reg_type, vt, u8, VFDREG, 1, 1) // VFD register type +VAR(vfd_reg_addr, va, u16, VFDREG, 1, 1) // VFD register address +VAR(vfd_reg_val, vv, u16, VFDREG, 1, 1) // VFD register value +VAR(vfd_reg_fails, vr, u8, VFDREG, 1, 1) // VFD register fail count + +// Huanyang spindle +VAR(hy_freq, hz, f32, 0, 0, 0) // Huanyang actual freq +VAR(hy_current, hc, f32, 0, 0, 0) // Huanyang actual current +VAR(hy_temp, ht, u16, 0, 0, 0) // Huanyang temperature +VAR(hy_max_freq, hx, f32, 0, 0, 1) // Huanyang max freq +VAR(hy_min_freq, hm, f32, 0, 0, 1) // Huanyang min freq +VAR(hy_rated_rpm, hq, u16, 0, 0, 1) // Huanyang rated RPM + +// Machine state +VAR(id, id, u16, 0, 1, 1) // Last executed command ID +VAR(feed_override, fo, u16, 0, 1, 1) // Feed rate override +VAR(speed_override, so, u16, 0, 1, 1) // Spindle speed override + +// System +VAR(velocity, v, f32, 0, 0, 1) // Current velocity +VAR(acceleration, ax, f32, 0, 0, 0) // Current acceleration +VAR(jerk, j, f32, 0, 0, 0) // Current jerk +VAR(peak_vel, pv, f32, 0, 1, 1) // Peak velocity, set to clear +VAR(peak_accel, pa, f32, 0, 1, 1) // Peak accel, set to clear +VAR(dynamic_power, dp, b8, 0, 1, 1) // Dynamic power +VAR(inverse_feed, if, f32, 0, 1, 1) // Inverse feed rate +VAR(hw_id, hid, str, 0, 0, 1) // Hardware ID +VAR(estop, es, b8, 0, 1, 1) // Emergency stop +VAR(estop_reason, er, pstr, 0, 0, 1) // Emergency stop reason +VAR(state, xx, pstr, 0, 0, 1) // Machine state +VAR(state_count, xc, u16, 0, 0, 1) // Machine state change count +VAR(hold_reason, pr, pstr, 0, 0, 1) // Machine pause reason +VAR(underrun, un, u32, 0, 0, 1) // Stepper buffer underrun count +VAR(dwell_time, dt, f32, 0, 0, 1) // Dwell timer diff --git a/src/avr/src/vars.h b/src/avr/src/vars.h new file mode 100644 index 0000000..949f5aa --- /dev/null +++ b/src/avr/src/vars.h @@ -0,0 +1,45 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "status.h" + +#include + + +float var_decode_float(const char *value); +bool var_parse_bool(const char *value); + +void vars_init(); + +void vars_report(bool full); +void vars_report_all(bool enable); +void vars_report_var(const char *code, bool enable); +stat_t vars_print(const char *name); +stat_t vars_set(const char *name, const char *value); +void vars_print_json(); diff --git a/src/avr/src/vars.json.in b/src/avr/src/vars.json.in new file mode 100644 index 0000000..267e916 --- /dev/null +++ b/src/avr/src/vars.json.in @@ -0,0 +1,14 @@ +#include "cpp_magic.h" +{ +#define VAR(NAME, CODE, TYPE, INDEX, SET, REPORT) \ +#CODE: { \ + "name": #NAME, \ + "type": #TYPE, \ + "index": IF_ELSE(INDEX)(true, false), \ + "setable": IF_ELSE(SET)(true, false), \ + "report": IF_ELSE(REPORT)(true, false) \ + }, +#include "vars.def" +#undef VAR + "_": {} +} diff --git a/src/avr/src/vfd_spindle.c b/src/avr/src/vfd_spindle.c new file mode 100644 index 0000000..fb247fe --- /dev/null +++ b/src/avr/src/vfd_spindle.c @@ -0,0 +1,478 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "vfd_spindle.h" +#include "modbus.h" +#include "rtc.h" +#include "config.h" +#include "estop.h" +#include "pgmspace.h" + +#include + +#include +#include +#include + + +typedef enum { + REG_DISABLED, + + REG_CONNECT_WRITE, + + REG_MAX_FREQ_READ, + REG_MAX_FREQ_FIXED, + + REG_FREQ_SET, + REG_FREQ_SIGN_SET, + + REG_STOP_WRITE, + REG_FWD_WRITE, + REG_REV_WRITE, + + REG_FREQ_READ, + REG_FREQ_SIGN_READ, + REG_FREQ_ACTECH_READ, + + REG_STATUS_READ, + + REG_DISCONNECT_WRITE, +} vfd_reg_type_t; + + +typedef struct { + vfd_reg_type_t type; + uint16_t addr; + uint16_t value; + uint8_t fails; +} vfd_reg_t; + + +#define P(H, L) ((H) << 8 | (L)) + + +// NOTE, Modbus reg = AC Tech reg + 1 +const vfd_reg_t ac_tech_regs[] PROGMEM = { + {REG_CONNECT_WRITE, 48, 19}, // Password unlock + {REG_CONNECT_WRITE, 1, 512}, // Manual mode + {REG_MAX_FREQ_READ, 62, 0}, // Max frequency + {REG_FREQ_SET, 40, 0}, // Frequency + {REG_STOP_WRITE, 1, 4}, // Stop drive + {REG_FWD_WRITE, 1, 128}, // Forward + {REG_FWD_WRITE, 1, 8}, // Start drive + {REG_REV_WRITE, 1, 64}, // Reverse + {REG_REV_WRITE, 1, 8}, // Start drive + {REG_FREQ_ACTECH_READ, 24, 0}, // Actual freq + {REG_DISCONNECT_WRITE, 1, 2}, // Lock controls and parameters + {REG_DISABLED}, +}; + + +const vfd_reg_t nowforever_regs[] PROGMEM = { + {REG_MAX_FREQ_READ, 7, 0}, // Max frequency + {REG_FREQ_SET, 2305, 0}, // Frequency + {REG_STOP_WRITE, 2304, 0}, // Stop drive + {REG_FWD_WRITE, 2304, 1}, // Forward + {REG_REV_WRITE, 2304, 3}, // Reverse + {REG_FREQ_READ, 1282, 0}, // Output freq + {REG_STATUS_READ, 768, 0}, // Status + {REG_DISABLED}, +}; + + +const vfd_reg_t delta_vfd015m21a_regs[] PROGMEM = { + {REG_CONNECT_WRITE, 0x2002, 2}, // Reset fault + {REG_MAX_FREQ_READ, 3, 0}, // Max frequency + {REG_FREQ_SET, 0x2001, 0}, // Frequency + {REG_STOP_WRITE, 0x2000, 1}, // Stop drive + {REG_FWD_WRITE, 0x2000, 18}, // Forward + {REG_REV_WRITE, 0x2000, 34}, // Reverse + {REG_FREQ_READ, 0x2103, 0}, // Output freq + {REG_STATUS_READ, 0x2100, 0}, // Status + {REG_DISABLED}, +}; + + +const vfd_reg_t yl600_regs[] PROGMEM = { + {REG_CONNECT_WRITE, 0x2000, 128}, // Reset all errors + {REG_MAX_FREQ_READ, 0x0004, 0}, // Max frequency + {REG_FREQ_SET, 0x2001, 0}, // Frequency + {REG_STOP_WRITE, 0x2000, 1}, // Stop drive + {REG_FWD_WRITE, 0x2000, 18}, // Forward + {REG_REV_WRITE, 0x2000, 34}, // Reverse + {REG_FREQ_READ, 0x200b, 0}, // Output freq + {REG_STATUS_READ, 0x2008, 0}, // Status + {REG_DISABLED}, +}; + + +const vfd_reg_t fr_d700_regs[] PROGMEM = { + {REG_MAX_FREQ_READ, 1000, 0}, // Max frequency + {REG_FREQ_SET, 13, 0}, // Frequency + {REG_STOP_WRITE, 8, 1}, // Stop drive + {REG_FWD_WRITE, 8, 2}, // Forward + {REG_REV_WRITE, 8, 4}, // Reverse + {REG_FREQ_READ, 200, 0}, // Output freq + {REG_DISABLED}, +}; + + +const vfd_reg_t sunfar_e300_regs[] PROGMEM = { + {REG_CONNECT_WRITE, 0x1001, 32}, // Reset all errors + {REG_MAX_FREQ_READ, 0xf004, 0}, // Max frequency F0.4 + {REG_FREQ_SET, 0x1002, 0}, // Frequency + {REG_STOP_WRITE, 0x1001, 3}, // Stop drive + {REG_FWD_WRITE, 0x1001, 1}, // Forward + {REG_REV_WRITE, 0x1001, 2}, // Reverse + {REG_FREQ_READ, 0xd000, 0}, // Output freq d.0 + {REG_STATUS_READ, 0x2000, 0}, // Status + {REG_DISABLED}, +}; + + +const vfd_reg_t omron_mx2_regs[] PROGMEM = { + {REG_CONNECT_WRITE, 0x1200, 3}, // A001 Frequency reference modbus + {REG_CONNECT_WRITE, 0x1201, 3}, // A002 Run command modbus + {REG_MAX_FREQ_FIXED, 0, 40000}, // TODO Want to use A004 max frequency + {REG_FREQ_SET, 0x0001, 0}, // F001 Frequency + {REG_STOP_WRITE, 0x1f00, 0}, // Stop drive + {REG_FWD_WRITE, 0x1f00, 2}, // Forward + {REG_REV_WRITE, 0x1f00, 6}, // Reverse + {REG_FREQ_READ, 0x1001, 0}, // D001 Output freq + {REG_STATUS_READ, 0x0004, 0}, // Status A + {REG_DISABLED}, +}; + + +const vfd_reg_t v70_regs[] PROGMEM = { + {REG_MAX_FREQ_READ, 0x0005, 0}, // Maximum operating frequency + {REG_FREQ_SET, 0x0201, 0}, // Set frequency in 0.1Hz + {REG_STOP_WRITE, 0x0200, 0}, // Stop + {REG_FWD_WRITE, 0x0200, 1}, // Run forward + {REG_REV_WRITE, 0x0200, 5}, // Run reverse + {REG_FREQ_READ, 0x0220, 0}, // Read operating frequency + {REG_STATUS_READ, 0x0210, 0}, // Read status + {REG_DISABLED}, +}; + + +static vfd_reg_t regs[VFDREG]; +static vfd_reg_t custom_regs[VFDREG]; + +static struct { + vfd_reg_type_t state; + int8_t reg; + uint8_t read_count; + bool changed; + bool shutdown; + + float power; + uint16_t max_freq; + bool user_multi_write; + float actual_power; + uint16_t status; + + uint32_t wait; + deinit_cb_t deinit_cb; +} vfd; + + +static void _disconnected() { + modbus_deinit(); + if (vfd.deinit_cb) vfd.deinit_cb(); + vfd.deinit_cb = 0; +} + + +static bool _next_state() { + switch (vfd.state) { + case REG_MAX_FREQ_FIXED: + if (!vfd.power) vfd.state = REG_STOP_WRITE; + else vfd.state = REG_FREQ_SET; + break; + + case REG_FREQ_SIGN_SET: + if (vfd.power < 0) vfd.state = REG_REV_WRITE; + else if (0 < vfd.power) vfd.state = REG_FWD_WRITE; + else vfd.state = REG_STOP_WRITE; + break; + + case REG_STOP_WRITE: case REG_FWD_WRITE: case REG_REV_WRITE: + vfd.state = REG_FREQ_READ; + break; + + case REG_STATUS_READ: + if (vfd.shutdown || estop_triggered()) vfd.state = REG_DISCONNECT_WRITE; + + else if (vfd.changed) { + // Update frequency and state + vfd.changed = false; + vfd.state = REG_MAX_FREQ_READ; + + } else { + // Continue querying after delay + vfd.state = REG_FREQ_READ; + vfd.wait = rtc_get_time() + VFD_QUERY_DELAY; + return false; + } + break; + + case REG_DISCONNECT_WRITE: + _disconnected(); + return false; + + default: + vfd.state = (vfd_reg_type_t)(vfd.state + 1); + } + + return true; +} + + +static bool _exec_command(); + + +static void _next_reg() { + while (true) { + vfd.reg++; + + if (vfd.reg == VFDREG) { + vfd.reg = -1; + vfd.read_count = 0; + if (!_next_state()) break; + + } else if (regs[vfd.reg].type == vfd.state && _exec_command()) break; + } +} + + +static void _connect() { + vfd.state = REG_CONNECT_WRITE; + vfd.reg = -1; + _next_reg(); +} + + +static void _modbus_cb(bool ok, uint16_t addr, uint16_t value) { + // Handle error + if (!ok) { + if (regs[vfd.reg].fails < 255) regs[vfd.reg].fails++; + if (vfd.shutdown || estop_triggered()) _disconnected(); + else _connect(); + return; + } + + // Handle read result + vfd.read_count++; + + switch (regs[vfd.reg].type) { + case REG_MAX_FREQ_READ: vfd.max_freq = value; break; + case REG_FREQ_READ: vfd.actual_power = value / (float)vfd.max_freq; break; + + case REG_FREQ_SIGN_READ: + vfd.actual_power = (int16_t)value / (float)vfd.max_freq; + break; + + case REG_FREQ_ACTECH_READ: + if (vfd.read_count == 2) vfd.actual_power = value / (float)vfd.max_freq; + if (vfd.read_count < 6) return; + break; + + case REG_STATUS_READ: vfd.status = value; break; + + default: break; + } + + // Next + _next_reg(); +} + + +static bool _use_multi_write() { + switch (spindle_get_type()) { + case SPINDLE_TYPE_CUSTOM: return vfd.user_multi_write; + case SPINDLE_TYPE_NOWFOREVER: return true; + default: return false; + } +} + + +static bool _exec_command() { + if (vfd.wait) return true; + + vfd_reg_t reg = regs[vfd.reg]; + uint16_t words = 1; + bool read = false; + bool write = false; + + switch (reg.type) { + case REG_DISABLED: break; + + case REG_MAX_FREQ_FIXED: vfd.max_freq = reg.value; break; + + case REG_FREQ_SET: + write = true; + reg.value = fabs(vfd.power) * vfd.max_freq; + break; + + case REG_FREQ_SIGN_SET: + write = true; + reg.value = vfd.power * vfd.max_freq; + break; + + case REG_CONNECT_WRITE: + case REG_STOP_WRITE: + case REG_FWD_WRITE: + case REG_REV_WRITE: + case REG_DISCONNECT_WRITE: + write = true; + break; + + case REG_FREQ_ACTECH_READ: + words = 6; + + case REG_FREQ_READ: + case REG_FREQ_SIGN_READ: + case REG_MAX_FREQ_READ: + case REG_STATUS_READ: + read = true; + break; + } + + if (read) modbus_read(reg.addr, words, _modbus_cb); + else if (write) (_use_multi_write() ? modbus_multi_write : modbus_write) + (reg.addr, reg.value, _modbus_cb); + else return false; + + return true; +} + + +static void _load(const vfd_reg_t *_regs) { + memset(®s, 0, sizeof(regs)); + + for (int i = 0; i < VFDREG; i++) { + regs[i].type = (vfd_reg_type_t)pgm_read_byte(&_regs[i].type); + if (!regs[i].type) break; + regs[i].addr = pgm_read_word(&_regs[i].addr); + regs[i].value = pgm_read_word(&_regs[i].value); + } +} + + +void vfd_spindle_init() { + memset(&vfd, 0, sizeof(vfd)); + for (int i = 0; i < VFDREG; i++) regs[i].fails = 0; + modbus_init(); + + switch (spindle_get_type()) { + case SPINDLE_TYPE_CUSTOM: memcpy(regs, custom_regs, sizeof(regs)); break; + case SPINDLE_TYPE_AC_TECH: _load(ac_tech_regs); break; + case SPINDLE_TYPE_NOWFOREVER: _load(nowforever_regs); break; + case SPINDLE_TYPE_DELTA_VFD015M21A: _load(delta_vfd015m21a_regs); break; + case SPINDLE_TYPE_YL600: _load(yl600_regs); break; + case SPINDLE_TYPE_FR_D700: _load(fr_d700_regs); break; + case SPINDLE_TYPE_SUNFAR_E300: _load(sunfar_e300_regs); break; + case SPINDLE_TYPE_OMRON_MX2: _load(omron_mx2_regs); break; + case SPINDLE_TYPE_V70: _load(v70_regs); break; + default: break; + } + + _connect(); +} + + +void vfd_spindle_deinit(deinit_cb_t cb) { + vfd.shutdown = true; + vfd.deinit_cb = cb; +} + + +void vfd_spindle_set(float power) { + if (vfd.power != power) + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + vfd.power = power; + vfd.changed = true; + } +} + + +float vfd_spindle_get() {return vfd.actual_power;} +uint16_t vfd_get_status() {return vfd.status;} + + +void vfd_spindle_rtc_callback() { + if (!vfd.wait || !rtc_expired(vfd.wait)) return; + vfd.wait = 0; + _next_reg(); +} + + +// Variable callbacks +uint16_t get_vfd_max_freq() {return vfd.max_freq;} +void set_vfd_max_freq(uint16_t max_freq) {vfd.max_freq = max_freq;} +bool get_vfd_multi_write() {return vfd.user_multi_write;} +void set_vfd_multi_write(bool value) {vfd.user_multi_write = value;} +uint8_t get_vfd_reg_type(int reg) {return regs[reg].type;} + + +void set_vfd_reg_type(int reg, uint8_t type) { + custom_regs[reg].type = (vfd_reg_type_t)type; + if (spindle_get_type() == SPINDLE_TYPE_CUSTOM) + regs[reg].type = custom_regs[reg].type; + vfd.changed = true; +} + + +uint16_t get_vfd_reg_addr(int reg) {return regs[reg].addr;} + + +void set_vfd_reg_addr(int reg, uint16_t addr) { + custom_regs[reg].addr = addr; + if (spindle_get_type() == SPINDLE_TYPE_CUSTOM) + regs[reg].addr = custom_regs[reg].addr; + vfd.changed = true; +} + + +uint16_t get_vfd_reg_val(int reg) {return regs[reg].value;} + + +void set_vfd_reg_val(int reg, uint16_t value) { + custom_regs[reg].value = value; + if (spindle_get_type() == SPINDLE_TYPE_CUSTOM) + regs[reg].value = custom_regs[reg].value; + vfd.changed = true; +} + + +uint8_t get_vfd_reg_fails(int reg) {return regs[reg].fails;} + + +void set_vfd_reg_fails(int reg, uint8_t value) { + regs[reg].fails = value; +} diff --git a/src/avr/src/vfd_spindle.h b/src/avr/src/vfd_spindle.h new file mode 100644 index 0000000..1bfc3d9 --- /dev/null +++ b/src/avr/src/vfd_spindle.h @@ -0,0 +1,38 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "spindle.h" + + +void vfd_spindle_init(); +void vfd_spindle_deinit(deinit_cb_t cb); +void vfd_spindle_set(float power); +float vfd_spindle_get(); +uint16_t vfd_get_status(); +void vfd_spindle_rtc_callback(); diff --git a/src/avr/step-test/Makefile b/src/avr/step-test/Makefile new file mode 100644 index 0000000..52f894a --- /dev/null +++ b/src/avr/step-test/Makefile @@ -0,0 +1,20 @@ +# Makefile for Bulidbotics step-test +PROJECT = step-test +MCU = atxmega192a3u +CLOCK = 32000000 + +# SRC +SRC = usart.c lcd.c pins.c hardware.c +SRC := $(wildcard *.c) $(patsubst %,../src/%,$(SRC)) + +include ../Makefile.common + +CFLAGS += -I../src -I. + +# Build +all: $(PROJECT).hex size + +$(PROJECT).elf: $(SRC) + $(CC) $(SRC) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ + +.PHONY: all diff --git a/src/avr/step-test/plot_velocity.py b/src/avr/step-test/plot_velocity.py new file mode 100644 index 0000000..2006faf --- /dev/null +++ b/src/avr/step-test/plot_velocity.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 + +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import sys, serial, argparse +import numpy as np +import math +import json +from time import sleep +from collections import deque +from datetime import datetime + +import matplotlib.pyplot as plt +import matplotlib.animation as animation + + +MM_PER_STEP = 5 * 1.8 / 360 / 32 +SAMPLES_PER_MIN = 6000 + + +class Plot: + def __init__(self, port, baud, max_len): + # Open serial port + self.sp = serial.Serial(port, baud) + + # Create data series + self.series = [] + for i in range(5): + self.series.append(deque([0.0] * max_len)) + + # Init vars + self.max_len = max_len + self.incoming = '' + self.last = [None] * 4 + + # Open velocity log + ts = datetime.now().strftime('%Y-%m-%d-%H:%M:%S') + self.log = open('velocity-%s.log' % ts, 'w') + + + # Add new series data + def add(self, i, value): + self.series[i].pop() + self.series[i].appendleft(value) + + + def update_text(self, text, vel, data): + text[4].set_text('V {0:8,.2f}'.format(vel)) + + for i in range(4): + text[i].set_text('{} {:11,}'.format('XYZA'[i], int(data[i]))) + + + def update(self, frame, axes, text): + # Read new data + try: + data = self.sp.read(self.sp.in_waiting) + self.incoming += data.decode('utf-8') + + except Exception as e: + print(e) + return + + while True: + # Parse lines + i = self.incoming.find('\n') + if i == -1: break + line = self.incoming[0:i] + self.incoming = self.incoming[i + 1:] + + # Handle reset + if line.find('RESET') != -1: + self.update_text(text, 0, [0] * 4) + self.log.write(line + '\n') + self.last = [None] * 4 + continue + + # Parse data + try: + data = [float(value) for value in line.split(',')] + except ValueError: continue + + if len(data) != 4: continue + + # Compute axis velocities + v = [] # Axis velocity + totalV = 0 # Tool velocity + + for i in range(4): + if self.last[i] is not None: + delta = self.last[i] - data[i] + v.append(delta * MM_PER_STEP * SAMPLES_PER_MIN) # mm/min + totalV += math.pow(v[i], 2) + + self.last[i] = data[i] + + # Compute tool velocity + totalV = math.sqrt(totalV) + + # Update position and velocity text + self.update_text(text, totalV, data) + + # Don't update plots when not moving + if totalV == 0: continue + + # Add new data + for i in range(4): self.add(i, v[i]) + self.add(4, totalV) + + # Update plots + for i in range(5): + axes[i].set_data(range(self.max_len), self.series[i]) + + self.log.write(line + '\n') + + + def close(self): + self.sp.flush() + self.sp.close() + + +if __name__ == '__main__': + # Parse command line arguments + description = "Plot velocity data in real-time" + parser = argparse.ArgumentParser(description = description) + parser.add_argument('-p', '--port', default = '/dev/ttyUSB0') + parser.add_argument('-b', '--baud', default = 115200, type = int) + parser.add_argument('-m', '--max-width', default = 2000, type = int) + args = parser.parse_args() + + # Create plot + plot = Plot(args.port, args.baud, args.max_width) + fig = plt.figure() + ax = plt.axes(xlim = (0, args.max_width), ylim = (-10000, 10000)) + axes = [] + axes_text = [] + + # Setup position and velocity text fields + font = dict(fontsize = 14, family = 'monospace') + + axes_text.append(plt.text(0, 11700, '', **font)) + axes_text.append(plt.text(800, 11700, '', **font)) + axes_text.append(plt.text(0, 10500, '', **font)) + axes_text.append(plt.text(800, 10500, '', **font)) + axes_text.append(plt.text(1500, 11700, '', **font)) + + # Create axes + for i in range(5): + axes.append(ax.plot([], [])[0]) + # Set text color to match axis color + axes_text[i].set_color(axes[i].get_color()) + + # Initial text views + plot.update_text(axes_text, 0, [0] * 4) + + # Set up animation + anim = animation.FuncAnimation(fig, plot.update, fargs = [axes, axes_text], + interval = 100) + + # Run + plt.show() + plot.close() diff --git a/src/avr/step-test/step-test.c b/src/avr/step-test/step-test.c new file mode 100644 index 0000000..db267bf --- /dev/null +++ b/src/avr/step-test/step-test.c @@ -0,0 +1,230 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "config.h" +#include "hardware.h" +#include "usart.h" +#include "lcd.h" + +#include + +#include +#include + + +#define RESET_PIN SPI_MOSI_PIN + + +void rtc_init() {} + + +static struct { + uint8_t step_pin; + uint8_t dir_pin; + TC0_t *timer; + volatile int16_t high; + volatile bool reading; + +} channel[4] = { + {STEP_X_PIN, DIR_X_PIN, &TCC0, 0}, + {STEP_Y_PIN, DIR_Y_PIN, &TCD0, 0}, + {STEP_Z_PIN, DIR_Z_PIN, &TCE0, 0}, + {STEP_A_PIN, DIR_A_PIN, &TCF0, 0}, +}; + + +static int reset = 0; + + +void channel_reset(int i) {channel[i].timer->CNT = channel[i].high = 0;} + + +#define EVSYS_CHMUX(CH) (&EVSYS_CH0MUX)[CH] +#define EVSYS_CHCTRL(CH) (&EVSYS_CH0CTRL)[CH] + + +void channel_overflow(int i) { + if (IN_PIN(channel[i].dir_pin)) channel[i].high--; + else channel[i].high++; + channel[i].reading = false; +} + + +ISR(TCC0_OVF_vect) {channel_overflow(0);} +ISR(TCD0_OVF_vect) {channel_overflow(1);} +ISR(TCE0_OVF_vect) {channel_overflow(2);} +ISR(TCF0_OVF_vect) {channel_overflow(3);} + + +void channel_update_dir(int i) { + if (IN_PIN(channel[i].dir_pin)) channel[i].timer->CTRLFSET = TC0_DIR_bm; + else channel[i].timer->CTRLFCLR = TC0_DIR_bm; +} + + +ISR(PORTE_INT0_vect) {for (int i = 0; i < 4; i++) channel_update_dir(i);} + + +int32_t __attribute__ ((noinline)) _channel_read(int i) { + return (int32_t)channel[i].high << 16 | channel[i].timer->CNT; +} + + +int32_t channel_read(int i) { + while (true) { + channel[i].reading = true; + + int32_t x = _channel_read(i); + int32_t y = _channel_read(i); + + if (x != y || !channel[i].reading) continue; + + channel[i].reading = false; + return x; + } +} + + +void channel_update_enable(int i) { + if (IN_PIN(MOTOR_ENABLE_PIN)) + channel[i].timer->CTRLA = TC_CLKSEL_EVCH0_gc + i; + else channel[i].timer->CTRLA = 0; +} + + +ISR(PORTF_INT0_vect) { + for (int i = 0; i < 4; i++) channel_update_enable(i); + if (!IN_PIN(MOTOR_ENABLE_PIN)) reset = 2; +} + + +ISR(PORTC_INT0_vect) {reset = 32;} + + +ISR(TCC1_OVF_vect) { + if (reset) reset--; + + // Report measured steps + static int32_t counts[4] = {0, 0, 0, 0}; + bool moving = false; + bool zero = true; + for (int i = 0; i < 4; i++) { + int32_t count = channel_read(i); + if (count != counts[i]) moving = true; + if (count) zero = false; + counts[i] = count; + } + + if (reset && !zero) { + for (int i = 0; i < 4; i++) channel_reset(i); + printf("RESET\n"); + return; + } + + if (moving) + printf("%ld,%ld,%ld,%ld\n", counts[0], counts[1], counts[2], counts[3]); +} + + +static void _splash(uint8_t addr) { + lcd_init(addr); + lcd_goto(addr, 5, 1); + lcd_pgmstr(addr, PSTR("Step Test")); +} + + +void channel_init(int i) { + uint8_t step_pin = channel[i].step_pin; + uint8_t dir_pin = channel[i].dir_pin; + + // Configure I/O + DIRCLR_PIN(step_pin); + DIRCLR_PIN(dir_pin); + PINCTRL_PIN(step_pin) = PORT_SRLEN_bm | PORT_ISC_RISING_gc; + PINCTRL_PIN(dir_pin) = PORT_SRLEN_bm | PORT_ISC_BOTHEDGES_gc; + + // Dir change interrupt + PIN_PORT(dir_pin)->INTCTRL |= PORT_INT0LVL_HI_gc; + PIN_PORT(dir_pin)->INT0MASK |= PIN_BM(dir_pin); + + // Events + EVSYS_CHMUX(i) = PIN_EVSYS_CHMUX(step_pin); + EVSYS_CHCTRL(i) = EVSYS_DIGFILT_8SAMPLES_gc; + + // Clock + channel_update_enable(i); + channel[i].timer->INTCTRLA = TC_OVFINTLVL_HI_gc; + + // Set initial clock direction + channel_update_dir(i); +} + + +static void init() { + cli(); + + hw_init(); + usart_init(); + + // Motor channels + for (int i = 0; i < 4; i++) channel_init(i); + + // Motor enable + DIRCLR_PIN(MOTOR_ENABLE_PIN); + PINCTRL_PIN(MOTOR_ENABLE_PIN) = + PORT_SRLEN_bm | PORT_ISC_BOTHEDGES_gc | PORT_OPC_PULLUP_gc; + PIN_PORT(MOTOR_ENABLE_PIN)->INTCTRL |= PORT_INT0LVL_HI_gc; + PIN_PORT(MOTOR_ENABLE_PIN)->INT0MASK |= PIN_BM(MOTOR_ENABLE_PIN); + + // Configure report clock + TCC1.INTCTRLA = TC_OVFINTLVL_LO_gc; + TCC1.PER = F_CPU / 256 * 0.01; // 10ms + TCC1.CTRLA = TC_CLKSEL_DIV256_gc; + + // Reset switch + DIRCLR_PIN(RESET_PIN); + PINCTRL_PIN(RESET_PIN) = + PORT_SRLEN_bm | PORT_ISC_RISING_gc | PORT_OPC_PULLUP_gc; + PIN_PORT(RESET_PIN)->INTCTRL |= PORT_INT0LVL_LO_gc; + PIN_PORT(RESET_PIN)->INT0MASK |= PIN_BM(RESET_PIN); + + printf("RESET\n"); + + sei(); +} + + +int main() { + init(); + + _splash(0x27); + _splash(0x3f); + + while (true) continue; + + return 0; +} diff --git a/src/bbserial/.gitignore b/src/bbserial/.gitignore new file mode 100644 index 0000000..a90b6ca --- /dev/null +++ b/src/bbserial/.gitignore @@ -0,0 +1,8 @@ +/.*.cmd +/.tmp_versions +/Module.symvers +/*.ko +/*.mod.c +/*.o +/kernel +/modules.order diff --git a/src/bbserial/Makefile b/src/bbserial/Makefile new file mode 100644 index 0000000..0b7bbc6 --- /dev/null +++ b/src/bbserial/Makefile @@ -0,0 +1,26 @@ +CROSS:=arm-linux-gnueabihf- +DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +obj-m:=bbserial.o +ccflags-y:=-std=gnu99 -Wno-declaration-after-statement + +KPKG=raspberrypi-kernel_1.20171029-1.tar.gz +KURL=https://github.com/dbrgn/linux-rpi/archive/$(KPKG) +KDIR=linux-rpi-raspberrypi-kernel_1.20171029-1 +export KERNEL=kernel7 + +KOPTS=ARCH=arm CROSS_COMPILE=$(CROSS) -C $(KDIR) + +all: $(KDIR) + $(MAKE) $(KOPTS) M=$(DIR) modules + +$(KDIR): $(KPKG) + tar xf $(KPKG) + $(MAKE) $(KOPTS) bcm2709_defconfig + $(MAKE) $(KOPTS) modules_prepare + +$(KPKG): + wget $(KURL) + +clean: + $(MAKE) $(KOPTS) M=$(DIR) clean + rm -rf $(KDIR) $(KPKG) diff --git a/src/bbserial/bbserial.c b/src/bbserial/bbserial.c new file mode 100644 index 0000000..0c00d5d --- /dev/null +++ b/src/bbserial/bbserial.c @@ -0,0 +1,690 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2019, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joseph Coffland"); +MODULE_DESCRIPTION("Buildbotics controller serial port driver"); +MODULE_VERSION("0.3"); + + +#define DEVICE_NAME "ttyAMA0" +#define BUF_SIZE (1 << 16) + + +#define UART01x_LCRH_WLEN_bm 0x60 + + +static int debug = 0; +module_param(debug, int, 0660); + + +struct ring_buf { + unsigned char *buf; + unsigned head; + unsigned tail; +}; + +static struct { + struct clk *clk; + struct ring_buf tx_buf; + struct ring_buf rx_buf; + spinlock_t lock; + unsigned open; + unsigned char __iomem *base; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + unsigned irq; + unsigned im; // interrupt mask + + unsigned brk_errs; + unsigned parity_errs; + unsigned frame_errs; + unsigned overruns; + + int major; + struct class *class; + struct device *dev; + struct ktermios term; +} _port; + + +#define RING_BUF_INC(BUF, INDEX) \ + do {(BUF).INDEX = ((BUF).INDEX + 1) & (BUF_SIZE - 1);} while (0) + +#define RING_BUF_POP(BUF) RING_BUF_INC(BUF, tail) + +#define RING_BUF_PUSH(BUF, C) \ + do { \ + (BUF).buf[(BUF).head] = C; \ + mb(); \ + RING_BUF_INC(BUF, head); \ + } while (0) + +#define RING_BUF_POKE(BUF) (BUF).buf[(BUF).head] +#define RING_BUF_PEEK(BUF) (BUF).buf[(BUF).tail] +#define RING_BUF_SPACE(BUF) ((((BUF).tail) - ((BUF).head + 1)) & (BUF_SIZE - 1)) +#define RING_BUF_FILL(BUF) ((((BUF).head) - ((BUF).tail)) & (BUF_SIZE - 1)) +#define RING_BUF_CLEAR(BUF) do {(BUF).head = (BUF).tail = 0;} while (0) + + +static unsigned _read(unsigned reg) {return readw_relaxed(_port.base + reg);} + + +static void _write(unsigned val, unsigned reg) { + writew_relaxed(val, _port.base + reg); +} + + +static void _tx_chars(void) { + unsigned fill = RING_BUF_FILL(_port.tx_buf); + + while (fill--) { + // Check if UART FIFO full + if (_read(UART01x_FR) & UART01x_FR_TXFF) break; + + _write(RING_BUF_PEEK(_port.tx_buf), UART01x_DR); + mb(); + RING_BUF_POP(_port.tx_buf); + } + + // Stop TX when buffer is empty + if (!RING_BUF_FILL(_port.tx_buf)) { + _port.im &= ~UART011_TXIM; + _write(_port.im, UART011_IMSC); + } +} + + +static void _rx_chars(void) { + unsigned space = RING_BUF_SPACE(_port.rx_buf); + + while (space--) { + // Check if UART FIFO empty + unsigned status = _read(UART01x_FR); + if (status & UART01x_FR_RXFE) break; + + // Read char from FIFO and update status + unsigned ch = _read(UART01x_DR); + + // Record errors + if (ch & UART011_DR_BE) _port.brk_errs++; + if (ch & UART011_DR_PE) _port.parity_errs++; + if (ch & UART011_DR_FE) _port.frame_errs++; + if (ch & UART011_DR_OE) _port.overruns++; + + // Queue char + RING_BUF_PUSH(_port.rx_buf, ch); + } + + // Stop RX interrupts when buffer is full + if (!RING_BUF_SPACE(_port.rx_buf)) { + _port.im &= ~(UART011_RXIM | UART011_RTIM); + _write(_port.im, UART011_IMSC); + } +} + + +static int _read_status(void) { + int status = 0; + + unsigned fr = _read(UART01x_FR); + unsigned cr = _read(UART011_CR); + + if (fr & UART01x_FR_DSR) status |= TIOCM_LE; // DSR (data set ready) + if (cr & UART011_CR_DTR) status |= TIOCM_DTR; // DTR (data terminal ready) + if (cr & UART011_CR_RTS) status |= TIOCM_RTS; // RTS (request to send) + // TODO What is TIOCM_ST - Secondary TXD (transmit)? + // TODO What is TIOCM_SR - Secondary RXD (receive)? + if (fr & UART01x_FR_CTS) status |= TIOCM_CTS; // CTS (clear to send) + if (fr & UART01x_FR_DCD) status |= TIOCM_CD; // DCD (data carrier detect) + if (fr & UART011_FR_RI) status |= TIOCM_RI; // RI (ring) + if (fr & UART01x_FR_DSR) status |= TIOCM_DSR; // DSR (data set ready) + + if (debug) printk(KERN_INFO "bbserial: _read_status() = %d\n", status); + + return status; +} + + +static void _write_status(int status) { + if (debug) printk(KERN_INFO "bbserial: _write_status() = %d\n", status); + + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + unsigned cr = _read(UART011_CR); + + // DTR (data terminal ready) + if (status & TIOCM_DTR) cr |= UART011_CR_DTR; + else cr &= ~UART011_CR_DTR; + + // RTS (request to send) + if (status & TIOCM_RTS) cr |= UART011_CR_RTS; + else cr &= ~UART011_CR_RTS; + + _write(cr, UART011_CR); + + spin_unlock_irqrestore(&_port.lock, flags); +} + + +static struct ktermios *_get_term(void) { + unsigned lcrh = _read(UART011_LCRH); + unsigned cr = _read(UART011_CR); + + // Baud rate + unsigned brd = _read(UART011_IBRD) << 6 | _read(UART011_FBRD); + speed_t baud = clk_get_rate(_port.clk) * 4 / brd; + tty_termios_encode_baud_rate(&_port.term, baud, baud); + + // Data bits + unsigned cflag; + switch (lcrh & UART01x_LCRH_WLEN_bm) { + case UART01x_LCRH_WLEN_5: cflag = CS5; break; + case UART01x_LCRH_WLEN_6: cflag = CS6; break; + case UART01x_LCRH_WLEN_7: cflag = CS7; break; + default: cflag = CS8; break; + } + + // Stop bits + if (lcrh & UART01x_LCRH_STP2) cflag |= CSTOPB; + + // Parity + if (lcrh & UART01x_LCRH_PEN) { + cflag |= PARENB; + + if (!(UART01x_LCRH_EPS & lcrh)) cflag |= PARODD; + if (UART011_LCRH_SPS & lcrh) cflag |= CMSPAR; + } + + // Hardware flow control + if (cr & UART011_CR_CTSEN) cflag |= CRTSCTS; + + _port.term.c_cflag = cflag; + + return &_port.term; +} + + +static void _set_baud(speed_t baud) { + if (debug) printk(KERN_INFO "bbserial: baud=%d\n", baud); + + unsigned brd = clk_get_rate(_port.clk) * 16 / baud; + + if ((brd & 3) == 3) brd = (brd >> 2) + 1; // Round up + else brd >>= 2; + + _write(brd & 0x3f, UART011_FBRD); + _write(brd >> 6, UART011_IBRD); +} + + +static int _set_term(struct ktermios *term) { + unsigned lcrh = UART01x_LCRH_FEN; // Enable FIFOs + unsigned cflag = term->c_cflag; + + // Data bits + switch (cflag & CSIZE) { + case CS5: lcrh |= UART01x_LCRH_WLEN_5; break; + case CS6: lcrh |= UART01x_LCRH_WLEN_6; break; + case CS7: lcrh |= UART01x_LCRH_WLEN_7; break; + default: lcrh |= UART01x_LCRH_WLEN_8; break; + } + + // Stop bits + if (cflag & CSTOPB) lcrh |= UART01x_LCRH_STP2; + + // Parity + if (cflag & PARENB) { + lcrh |= UART01x_LCRH_PEN; + + if (!(cflag & PARODD)) lcrh |= UART01x_LCRH_EPS; + if (cflag & CMSPAR) lcrh |= UART011_LCRH_SPS; + } + + // Get baud rate + speed_t baud = tty_termios_baud_rate(term); + + // Set + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + // Hardware flow control + unsigned cr = _read(UART011_CR); + if (cflag & CRTSCTS) cr |= UART011_CR_CTSEN; + + _write(0, UART011_CR); // Disable + _set_baud(baud); // Baud + _write(lcrh, UART011_LCRH); // Must be after baud + _write(cr, UART011_CR); // Enable + + spin_unlock_irqrestore(&_port.lock, flags); + + return 0; +} + + +static void _flush_input(void) { + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + RING_BUF_CLEAR(_port.rx_buf); + + spin_unlock_irqrestore(&_port.lock, flags); +} + + +static void _flush_output(void) { + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + RING_BUF_CLEAR(_port.tx_buf); + + spin_unlock_irqrestore(&_port.lock, flags); +} + + +static irqreturn_t _interrupt(int irq, void *id) { + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + // Read and/or write + unsigned status = _read(UART011_MIS); + if (status & (UART011_RTIS | UART011_RXIS)) _rx_chars(); + if (status & UART011_TXIS) _tx_chars(); + + unsigned txSpace = RING_BUF_SPACE(_port.tx_buf); + unsigned rxFill = RING_BUF_FILL(_port.rx_buf); + + spin_unlock_irqrestore(&_port.lock, flags); + + // Notify pollers + if (rxFill) wake_up_interruptible_poll(&_port.read_wait, POLLIN); + if (txSpace) wake_up_interruptible_poll(&_port.write_wait, POLLOUT); + + return IRQ_HANDLED; +} + + +static void _enable_tx(void) { + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + _port.im |= UART011_TXIM; + _write(_port.im, UART011_IMSC); + _tx_chars(); // Must prime the pump + + spin_unlock_irqrestore(&_port.lock, flags); +} + + +static int _tx_enabled(void) {return _port.im & UART011_TXIM;} + + +static void _enable_rx(void) { + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + _port.im |= UART011_RTIM | UART011_RXIM; + _write(_port.im, UART011_IMSC); + + spin_unlock_irqrestore(&_port.lock, flags); +} + + +static int _rx_enabled(void) {return _port.im & (UART011_RTIM | UART011_RXIM);} + + +static int _dev_open(struct inode *inodep, struct file *filep) { + if (debug) printk(KERN_INFO "bbserial: open()\n"); + if (_port.open) return -EBUSY; + _port.open = 1; + return 0; +} + + +static ssize_t _dev_read(struct file *filep, char *buffer, size_t len, + loff_t *offset) { + if (debug) printk(KERN_INFO "bbserial: read() len=%d overruns=%d\n", len, + _port.overruns); + + ssize_t bytes = 0; + + while (bytes < len && RING_BUF_FILL(_port.rx_buf)) { + put_user(RING_BUF_PEEK(_port.rx_buf), buffer++); + RING_BUF_POP(_port.rx_buf); + bytes++; + if (!_rx_enabled()) _enable_rx(); + } + + return bytes ? bytes : -EAGAIN; +} + + +static ssize_t _dev_write(struct file *filep, const char *buffer, size_t len, + loff_t *offset) { + if (debug) + printk(KERN_INFO "bbserial: write() len=%d tx=%d rx=%d\n", + len, RING_BUF_FILL(_port.tx_buf), RING_BUF_FILL(_port.rx_buf)); + + ssize_t bytes = 0; + + while (bytes < len && RING_BUF_SPACE(_port.tx_buf)) { + get_user(RING_BUF_POKE(_port.tx_buf), buffer++); + RING_BUF_INC(_port.tx_buf, head); + bytes++; + if (!_tx_enabled()) _enable_tx(); + } + + return bytes ? bytes : -EAGAIN; +} + + +static int _dev_release(struct inode *inodep, struct file *filep) { + printk(KERN_INFO "bbserial: release()\n"); + _port.open = 0; + return 0; +} + + +static unsigned _dev_poll(struct file *file, poll_table *wait) { + if (debug) { + unsigned events = poll_requested_events(wait); + printk(KERN_INFO "bbserial: poll(in=%s, out=%s)\n", + (events & POLLIN) ? "true" : "false", + (events & POLLOUT) ? "true" : "false"); + } + + poll_wait(file, &_port.read_wait, wait); + poll_wait(file, &_port.write_wait, wait); + + unsigned ret = 0; + if (RING_BUF_FILL(_port.rx_buf)) ret |= POLLIN | POLLRDNORM; + if (RING_BUF_SPACE(_port.tx_buf)) ret |= POLLOUT | POLLWRNORM; + + if (debug) printk(KERN_INFO "bbserial: tx=%d rx=%d\n", + RING_BUF_FILL(_port.tx_buf), RING_BUF_FILL(_port.rx_buf)); + + return ret; +} + + +static long _dev_ioctl(struct file *file, unsigned cmd, unsigned long arg) { + if (debug) + printk(KERN_INFO "bbserial: ioctl() cmd=0x%04x arg=%lu\n", cmd, arg); + + int __user *ptr = (int __user *)arg; + int status; + + switch (cmd) { + case TCGETS: { // Get serial port settings + struct ktermios *term = _get_term(); + if (copy_to_user((void __user *)arg, &term, sizeof(struct termios))) + return -EFAULT; + return 0; + } + + case TCSETS: { // Set serial port settings + struct ktermios term; + if (copy_from_user(&term, (void __user *)arg, sizeof(struct termios))) + return -EFAULT; + return _set_term(&term); + } + + case TIOCMGET: // Get status of modem bits + put_user(_read_status(), ptr); + return 0; + + case TIOCMSET: // Set status of modem bits + get_user(status, ptr); + _write_status(status); + return 0; + + case TIOCMBIC: // Clear indicated modem bits + get_user(status, ptr); + _write_status(~status & _read_status()); + return 0; + + case TIOCMBIS: // Set indicated modem bits + get_user(status, ptr); + _write_status(status | _read_status()); + return 0; + + case TCFLSH: // Flush + if (arg == TCIFLUSH || arg == TCIOFLUSH) _flush_input(); + if (arg == TCOFLUSH || arg == TCIOFLUSH) _flush_output(); + return 0; + + case TIOCINQ: return put_user(RING_BUF_FILL(_port.rx_buf), ptr); + case TIOCOUTQ: return put_user(RING_BUF_FILL(_port.tx_buf), ptr); + + default: return -ENOIOCTLCMD; + } + + return 0; +} + + +static struct file_operations _ops = { + .owner = THIS_MODULE, + .open = _dev_open, + .read = _dev_read, + .write = _dev_write, + .release = _dev_release, + .poll = _dev_poll, + .unlocked_ioctl = _dev_ioctl, +}; + + +static int _probe(struct amba_device *dev, const struct amba_id *id) { + if (debug) printk(KERN_INFO "bbserial: probing\n"); + + // Allocate buffers + _port.tx_buf.buf = devm_kzalloc(&dev->dev, BUF_SIZE, GFP_KERNEL); + _port.rx_buf.buf = devm_kzalloc(&dev->dev, BUF_SIZE, GFP_KERNEL); + if (!_port.tx_buf.buf || !_port.rx_buf.buf) return -ENOMEM; + + // Map IO memory + _port.base = devm_ioremap_resource(&dev->dev, &dev->res); + if (IS_ERR(_port.base)) { + dev_err(&dev->dev, "bbserial: failed to map IO memory\n"); + return PTR_ERR(_port.base); + } + + // Get and enable clock + _port.clk = devm_clk_get(&dev->dev, 0); + if (IS_ERR(_port.clk)) { + dev_err(&dev->dev, "bbserial: failed to get clock\n"); + return PTR_ERR(_port.clk); + } + + int ret = clk_prepare_enable(_port.clk); + if (ret) { + dev_err(&dev->dev, "bbserial: clock prepare failed\n"); + return ret; + } + + // Disable UART and mask interrupts + _write(0, UART011_CR); + _write(0, UART011_IMSC); + + // Set default baud rate + _set_baud(38400); + + // N81 & enable FIFOs, must be after baud + _write(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, UART011_LCRH); + + // Enable, TX, RX, RTS, DTR & CTS + unsigned cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE | + UART011_CR_RTS | UART011_CR_DTR | UART011_CR_CTSEN; + _write(cr, UART011_CR); + + // Set interrupt FIFO trigger levels + _write(UART011_IFLS_RX2_8 | UART011_IFLS_TX6_8, UART011_IFLS); + + // Clear pending interrupts + _write(0x7ff, UART011_ICR); + + // Enable read interrupts + _port.im = 0; + _enable_rx(); + + // Allocate IRQ + _port.irq = dev->irq[0]; + ret = request_irq(_port.irq, _interrupt, 0, "bbserial", &_port); + + if (ret) { + dev_err(&dev->dev, "bbserial: request for IRQ failed\n"); + clk_disable_unprepare(_port.clk); + return ret; + } + + // Dynamically allocate device major number + _port.major = register_chrdev(0, DEVICE_NAME, &_ops); + if (_port.major < 0) { + clk_disable_unprepare(_port.clk); + dev_err(&dev->dev, "bbserial: failed to register a major number\n"); + return _port.major; + } + + // Register device class + _port.class = class_create(THIS_MODULE, "bbs"); + if (IS_ERR(_port.class)) { + unregister_chrdev(_port.major, DEVICE_NAME); + clk_disable_unprepare(_port.clk); + dev_err(&dev->dev, "bbserial: failed to register device class\n"); + return PTR_ERR(_port.class); + } + + // Register device driver + _port.dev = + device_create(_port.class, 0, MKDEV(_port.major, 0), 0, DEVICE_NAME); + if (IS_ERR(_port.dev)) { + class_destroy(_port.class); + unregister_chrdev(_port.major, DEVICE_NAME); + dev_err(&dev->dev, "bbserial: failed to create the device\n"); + return PTR_ERR(_port.dev); + } + + return 0; +} + + +static int _remove(struct amba_device *dev) { + if (debug) printk(KERN_INFO "bbserial: removing\n"); + + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + // Mask and clear interrupts + _write(0, UART011_IMSC); + _write(0x7ff, UART011_ICR); + + // Disable UART + _write(0, UART011_CR); + + spin_unlock_irqrestore(&_port.lock, flags); + + // Free IRQ + free_irq(_port.irq, &_port); + synchronize_irq(_port.irq); + + // Shut down the clock producer + clk_disable_unprepare(_port.clk); + + // Unload char dev + device_destroy(_port.class, MKDEV(_port.major, 0)); + class_unregister(_port.class); + class_destroy(_port.class); + unregister_chrdev(_port.major, DEVICE_NAME); + + return 0; +} + + +static struct amba_id _ids[] = { + { + .id = 0x00041011, + .mask = 0x000fffff, + .data = 0, + }, + {0, 0}, +}; + + +MODULE_DEVICE_TABLE(amba, _ids); + + +static struct amba_driver _driver = { + .drv = {.name = "bbserial"}, + .id_table = _ids, + .probe = _probe, + .remove = _remove, +}; + + +static int __init bbserial_init(void) { + printk(KERN_INFO "bbserial: loaded\n"); + + // Clear memory + memset(&_port, 0, sizeof(_port)); + + // Init lock + spin_lock_init(&_port.lock); + + // Init wait queues + init_waitqueue_head(&_port.read_wait); + init_waitqueue_head(&_port.write_wait); + + return amba_driver_register(&_driver); +} + + +static void __exit bbserial_exit(void) { + amba_driver_unregister(&_driver); + printk(KERN_INFO "bbserial: unloaded\n"); +} + + +module_init(bbserial_init); +module_exit(bbserial_exit); diff --git a/src/boot/.gitignore b/src/boot/.gitignore new file mode 100644 index 0000000..acbe7a5 --- /dev/null +++ b/src/boot/.gitignore @@ -0,0 +1,3 @@ +/build +/*.elf +/*.hex diff --git a/src/boot/Makefile b/src/boot/Makefile new file mode 100644 index 0000000..c09a60e --- /dev/null +++ b/src/boot/Makefile @@ -0,0 +1,116 @@ +# Makefile for the project Bulidbotics bootloader +PROJECT = bbctrl-avr-boot +MCU = atxmega192a3u +CLOCK = 32000000 + +TARGET = $(PROJECT).elf + +# Compile flags +CC = avr-gcc +CPP = avr-g++ + +COMMON = -mmcu=$(MCU) -flto -fwhole-program + +CFLAGS += $(COMMON) -Wall -Werror +CFLAGS += -std=gnu99 -DF_CPU=$(CLOCK)UL -O3 +CFLAGS += -funsigned-bitfields -fpack-struct -fshort-enums -funsigned-char +CFLAGS += -MD -MP -MT $@ -MF build/dep/$(@F).d +CFLAGS += -Isrc + +# Linker flags +LDFLAGS += $(COMMON) -Wl,-u,vfprintf -lprintf_flt -lm +LDFLAGS += -Wl,--section-start=.text=0x030000 +LIBS += -lm + +# Programming flags +PROGRAMMER = avrispmkII +PDEV = usb +AVRDUDE_OPTS = -c $(PROGRAMMER) -p $(MCU) -P $(PDEV) + +FUSE0=0xff +FUSE1=0x00 +FUSE2=0xbe +FUSE4=0xff +FUSE5=0xeb + +# SRC +SRC = $(wildcard src/*.S) +SRC += $(wildcard src/*.c) +OBJ = $(patsubst src/%.c,build/%.o,$(SRC)) +OBJ := $(patsubst src/%.S,build/%.o,$(OBJ)) + + +# Build +all: $(TARGET) size + +# Compile +build/%.o: src/%.c + @mkdir -p $(shell dirname $@) + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +build/%.o: src/%.S + @mkdir -p $(shell dirname $@) + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +# Link +$(TARGET): $(OBJ) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.hex: %.elf + avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $< $@ + +size: $(TARGET) + @for X in A B C; do\ + echo '****************************************************************' ;\ + avr-size -$$X --mcu=$(MCU) $(TARGET) ;\ + done + +# Program +init: + $(MAKE) erase + -$(MAKE) fuses + $(MAKE) fuses + $(MAKE) program + +reset: + avrdude $(AVRDUDE_OPTS) + +erase: + avrdude $(AVRDUDE_OPTS) -e + +program: $(PROJECT).hex + avrdude $(AVRDUDE_OPTS) -U flash:w:$(PROJECT).hex:i + +verify: $(PROJECT).hex + avrdude $(AVRDUDE_OPTS) -U flash:v:$(PROJECT).hex:i + +fuses: + avrdude $(AVRDUDE_OPTS) -U fuse0:w:$(FUSE0):m -U fuse1:w:$(FUSE1):m \ + -U fuse2:w:$(FUSE2):m -U fuse4:w:$(FUSE4):m -U fuse5:w:$(FUSE5):m + +read_fuses: + avrdude $(AVRDUDE_OPTS) -q -q -U fuse0:r:-:h -U fuse1:r:-:h -U fuse2:r:-:h \ + -U fuse4:r:-:h -U fuse5:r:-:h + +signature: + avrdude $(AVRDUDE_OPTS) -q -q -U signature:r:-:h + +prodsig: + avrdude $(AVRDUDE_OPTS) -q -q -U prodsig:r:-:h + +usersig: + avrdude $(AVRDUDE_OPTS) -q -q -U usersig:r:-:h + +# Clean +tidy: + rm -f $(shell find -name \*~ -o -name \#\*) + +clean: tidy + rm -rf $(PROJECT).elf $(PROJECT).hex $(PROJECT).eep $(PROJECT).lss \ + $(PROJECT).map build + +.PHONY: tidy clean size all reset erase program fuses read_fuses prodsig +.PHONY: signature usersig + +# Dependencies +-include $(shell mkdir -p build/dep) $(wildcard build/dep/*) diff --git a/src/boot/src/boot.c b/src/boot/src/boot.c new file mode 100644 index 0000000..5137617 --- /dev/null +++ b/src/boot/src/boot.c @@ -0,0 +1,452 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "boot.h" +#include "sp_driver.h" + +#include +#include + +#include +#include + +#include + + +uint8_t buffer[SPM_PAGESIZE]; +uint16_t block_crc = 0; + + +void clock_init() { + // External 16Mhz Xtal w/ 2x PLL = 32 Mhz + // 12-16 MHz crystal; 0.4-16 MHz XTAL w/ 16K CLK startup + OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc; + OSC.CTRL = OSC_XOSCEN_bm; // enable external crystal oscillator + while (!(OSC.STATUS & OSC_XOSCRDY_bm)); // wait for oscillator ready + + OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | 2; // PLL source, 2x (32 MHz sys clock) + OSC.CTRL = OSC_PLLEN_bm | OSC_XOSCEN_bm; // Enable PLL & External Oscillator + while (!(OSC.STATUS & OSC_PLLRDY_bm)); // wait for PLL ready + + CCP = CCP_IOREG_gc; + CLK.CTRL = CLK_SCLKSEL_PLL_gc; // switch to PLL clock + OSC.CTRL &= ~OSC_RC2MEN_bm; // disable internal 2 MHz clock +} + + +bool uart_has_char() {return UART_DEVICE.STATUS & USART_RXCIF_bm;} +uint8_t uart_recv_char() {return UART_DEVICE.DATA;} + + +void uart_send_char_blocking(uint8_t c) { + UART_DEVICE.DATA = c; + while (!(UART_DEVICE.STATUS & USART_TXCIF_bm)) continue; + UART_DEVICE.STATUS |= USART_TXCIF_bm; +} + + +void uart_init() { + UART_PORT.DIRSET = 1 << UART_TX_PIN; + UART_DEVICE.BAUDCTRLA = UART_BSEL_VALUE & USART_BSEL_gm; + UART_DEVICE.BAUDCTRLB = + ((UART_BSCALE_VALUE << USART_BSCALE_gp) & USART_BSCALE_gm) | + ((UART_BSEL_VALUE >> 8) & ~USART_BSCALE_gm); + UART_DEVICE.CTRLB = USART_RXEN_bm | USART_TXEN_bm; + + PORTC.OUTCLR = 1 << 4; // CTS Lo (enable) + PORTC.DIRSET = 1 << 4; // CTS Output +} + + +void uart_deinit() { + UART_DEVICE.CTRLB = 0; + UART_DEVICE.BAUDCTRLA = 0; + UART_DEVICE.BAUDCTRLB = 0; + UART_PORT.DIRCLR = 1 << UART_TX_PIN; +} + + +void watchdog_init() { + uint8_t temp = WDT_ENABLE_bm | WDT_CEN_bm | WDT_PER_4KCLK_gc; + CCP = CCP_IOREG_gc; + WDT.CTRL = temp; + while (WDT.STATUS & WDT_SYNCBUSY_bm) continue; +} + + +void watchdog_reset() {asm("wdr");} + + +void watchdog_disable() { + uint8_t temp = (WDT.CTRL & ~WDT_ENABLE_bm) | WDT_CEN_bm; + CCP = CCP_IOREG_gc; + WDT.CTRL = temp; +} + + +void nvm_wait() {while (NVM_STATUS & NVM_NVMBUSY_bp) watchdog_reset();} + + +void nvm_exec() { + void *z = (void *)&NVM_CTRLA; + + __asm__ volatile + ("out %[ccp], %[ioreg]\n" + "st z, %[cmdex]" :: + [ccp] "I" (_SFR_IO_ADDR(CCP)), + [ioreg] "d" (CCP_IOREG_gc), + [cmdex] "r" (NVM_CMDEX_bm), + [z] "z" (z)); +} + + +uint8_t get_char() { + while (!uart_has_char()) continue; + return uart_recv_char(); +} + + +void send_char(uint8_t c) {uart_send_char_blocking(c);} + + +uint16_t get_word() { + uint8_t hi = get_char(); + uint8_t lo = get_char(); + return ((uint16_t)hi << 8) | lo; +} + + +uint8_t BlockLoad(unsigned size, uint8_t mem, uint32_t *address) { + watchdog_reset(); + + // fill up buffer + block_crc = 0xffff; + for (int i = 0; i < SPM_PAGESIZE; i++) + if (i < size) { + buffer[i] = get_char(); + block_crc = _crc16_update(block_crc, buffer[i]); + + } else buffer[i] = 0xff; + + switch (mem) { + case MEM_EEPROM: + eeprom_write_block(buffer, (uint8_t *)(uint16_t)*address, size); + *address += size; + break; + + case MEM_FLASH: + // NOTE: Flash programming, address is given in words. + SP_LoadFlashPage(buffer); + SP_EraseWriteApplicationPage(*address << 1); + *address += size >> 1; + nvm_wait(); + break; + + case MEM_USERSIG: + SP_LoadFlashPage(buffer); + SP_EraseUserSignatureRow(); + nvm_wait(); + SP_WriteUserSignatureRow(); + nvm_wait(); + break; + + default: return REPLY_ERROR; + } + + return REPLY_ACK; +} + + + +void BlockRead(unsigned size, uint8_t mem, uint32_t *address) { + switch (mem) { + case MEM_EEPROM: + eeprom_read_block(buffer, (uint8_t *)(uint16_t)*address, size); + *address += size; + + // send bytes + for (int i = 0; i < size; i++) + send_char(buffer[i]); + break; + + case MEM_FLASH: case MEM_USERSIG: case MEM_PRODSIG: { + *address <<= 1; // Convert address to bytes temporarily + + do { + switch (mem) { + case MEM_FLASH: send_char(SP_ReadByte(*address)); break; + case MEM_USERSIG: send_char(SP_ReadUserSignatureByte(*address)); break; + case MEM_PRODSIG: send_char(SP_ReadCalibrationByte(*address)); break; + } + + nvm_wait(); + + (*address)++; // Select next word in memory. + size--; // Subtract two bytes from number of bytes to read + } while (size); // Repeat until all block has been read + + *address >>= 1; // Convert address back to Flash words again. + break; + } + + default: break; + } +} + + +void bootloader() { + uint32_t address = 0; + uint16_t i = 0; + + while (true) { + uint8_t val = get_char(); + watchdog_reset(); + + // Main bootloader parser + switch (val) { + case CMD_CHECK_AUTOINCREMENT: send_char(REPLY_YES); break; + + case CMD_SET_ADDRESS: + address = get_word(); + send_char(REPLY_ACK); + break; + + case CMD_SET_EXT_ADDRESS: { + uint8_t hi = get_char(); + address = ((uint32_t)hi << 16) | get_word(); + send_char(REPLY_ACK); + break; + } + + case CMD_FLASH_ERASE: + SP_EraseApplicationSection(); + nvm_wait(); + send_char(REPLY_ACK); + break; + + case CMD_EEPROM_ERASE: + NVM.CMD = NVM_CMD_ERASE_EEPROM_gc; + nvm_exec(); + send_char(REPLY_ACK); + break; + + case CMD_CHECK_BLOCK_SUPPORT: + send_char(REPLY_YES); + // Send block size (page size) + send_char(SPM_PAGESIZE >> 8); + send_char((uint8_t)SPM_PAGESIZE); + break; + + case CMD_BLOCK_LOAD: + i = get_word(); // Block size + val = get_char(); // Memory type + send_char(BlockLoad(i, val, &address)); // Load it + break; + + case CMD_BLOCK_READ: + i = get_word(); // Block size + val = get_char(); // Memory type + BlockRead(i, val, &address); // Read it + break; + + case CMD_READ_BYTE: { + unsigned w = SP_ReadWord(address << 1); + + send_char(w >> 8); + send_char(w); + + address++; + break; + } + + case CMD_WRITE_LOW_BYTE: + i = get_char(); // get low byte + send_char(REPLY_ACK); + break; + + case CMD_WRITE_HIGH_BYTE: + i |= (get_char() << 8); // get high byte; combine + SP_LoadFlashWord(address << 1, i); + address++; + send_char(REPLY_ACK); + break; + + case CMD_WRITE_PAGE: + if (address >= (APP_SECTION_SIZE >> 1)) + send_char(REPLY_ERROR); // don't allow bootloader overwrite + + else { + SP_WriteApplicationPage(address << 1); + send_char(REPLY_ACK); + } + break; + + case CMD_WRITE_EEPROM_BYTE: + eeprom_write_byte((uint8_t *)(uint16_t)address, get_char()); + address++; + send_char(REPLY_ACK); + break; + + case CMD_READ_EEPROM_BYTE: + send_char(eeprom_read_byte((uint8_t *)(uint16_t)address)); + address++; + break; + + case CMD_READ_LOW_FUSE_BITS: send_char(SP_ReadFuseByte(0)); break; + case CMD_READ_HIGH_FUSE_BITS: send_char(SP_ReadFuseByte(1)); break; + case CMD_READ_EXT_FUSE_BITS: send_char(SP_ReadFuseByte(2)); break; + + case CMD_ENTER_PROG_MODE: case CMD_LEAVE_PROG_MODE: + send_char(REPLY_ACK); + break; + + case CMD_EXIT_BOOTLOADER: + send_char(REPLY_ACK); + return; + break; + + case CMD_PROGRAMMER_TYPE: send_char('S'); break; // serial + + case CMD_DEVICE_CODE: + send_char(123); // send only this device + send_char(0); // terminator + break; + + case CMD_SET_LED: case CMD_CLEAR_LED: case CMD_SET_TYPE: + get_char(); // discard parameter + send_char(REPLY_ACK); + break; + + case CMD_PROGRAM_ID: + send_char('b'); + send_char('b'); + send_char('c'); + send_char('t'); + send_char('r'); + send_char('l'); + send_char(' '); + break; + + case CMD_VERSION: + send_char('0'); + send_char('2'); + break; + + case CMD_READ_SIGNATURE: + send_char(SIGNATURE_2); + send_char(SIGNATURE_1); + send_char(SIGNATURE_0); + break; + + case CMD_READ_CHECKSUM: + // Setup + nvm_wait(); + + // Reset CRC + CRC_CTRL |= CRC_RESET_RESET1_gc; + CRC.CHECKSUM0 = CRC.CHECKSUM1 = CRC.CHECKSUM2 = CRC.CHECKSUM3 = 0xff; + + // 32-bit mode, flash source + CRC_CTRL = CRC_CRC32_bm | CRC_SOURCE_FLASH_gc; + + // Start address + NVM.ADDR0 = (uint8_t)(APP_SECTION_START >> 0); + NVM.ADDR1 = (uint8_t)(APP_SECTION_START >> 8); + NVM.ADDR2 = 0; + + // End address + NVM.DATA0 = (uint8_t)(APP_SECTION_END >> 0); + NVM.DATA1 = (uint8_t)(APP_SECTION_END >> 8); + NVM.DATA2 = (uint8_t)(APP_SECTION_END >> 16); + + NVM.CMD = NVM_CMD_FLASH_RANGE_CRC_gc; + CCP = CCP_IOREG_gc; + NVM.CTRLA = NVM_CMDEX_bm; + + // Compute + nvm_wait(); + while (CRC.STATUS & CRC_BUSY_bm) continue; + + // Send 32-bit checksum + send_char(CRC.CHECKSUM3); + send_char(CRC.CHECKSUM2); + send_char(CRC.CHECKSUM1); + send_char(CRC.CHECKSUM0); + break; + + case CMD_FLASH_LENGTH: + send_char((uint8_t)(APP_SECTION_SIZE >> 16)); + send_char((uint8_t)(APP_SECTION_SIZE >> 8)); + send_char((uint8_t)(APP_SECTION_SIZE >> 0)); + break; + + case CMD_BLOCK_CRC: + send_char(block_crc >> 8); + send_char((uint8_t)block_crc); + break; + + case CMD_SYNC: break; // ESC (0x1b) to sync + + default: // otherwise, error + send_char(REPLY_ERROR); + break; + } + + // Wait for any lingering SPM instructions to finish + nvm_wait(); + } +} + + +int main() { + // Init + clock_init(); + uart_init(); + watchdog_init(); + + // Check for trigger + uint16_t j = INITIAL_WAIT; + while (0 < j--) { + if (uart_has_char() && uart_recv_char() == CMD_SYNC) { + bootloader(); + break; + } + + watchdog_reset(); + _delay_ms(1); + } + + // Deinit + uart_deinit(); + watchdog_disable(); + + // Disable further self programming until next reset + SP_LockSPM(); + + // Jump to application code + asm("jmp 0"); +} diff --git a/src/boot/src/boot.h b/src/boot/src/boot.h new file mode 100644 index 0000000..def2d06 --- /dev/null +++ b/src/boot/src/boot.h @@ -0,0 +1,110 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#define INITIAL_WAIT 1000 // In ms + +#define UART_RX_PIN 2 +#define UART_TX_PIN 3 +#define UART_PORT PORTC +#define UART_DEVICE USARTC0 + +// Baud rate 921600 @ 32Mhz +#define UART_BSEL_VALUE 150 +#define UART_BSCALE_VALUE -7 + + +// Protocol +enum { + CMD_SYNC = '\x1b', + + // Informational + CMD_CHECK_AUTOINCREMENT = 'a', + CMD_CHECK_BLOCK_SUPPORT = 'b', + CMD_PROGRAMMER_TYPE = 'p', + CMD_DEVICE_CODE = 't', + CMD_PROGRAM_ID = 'S', + CMD_VERSION = 'V', + CMD_HW_VERSION = 'v', // Unsupported extension + CMD_READ_SIGNATURE = 's', + CMD_READ_CHECKSUM = 'X', + CMD_FLASH_LENGTH = 'n', + + // Addressing + CMD_SET_ADDRESS = 'A', + CMD_SET_EXT_ADDRESS = 'H', + + // Erase + CMD_FLASH_ERASE = 'e', + CMD_EEPROM_ERASE = '_', + + // Block Access + CMD_BLOCK_LOAD = 'B', + CMD_BLOCK_READ = 'g', + CMD_BLOCK_CRC = 'i', + + // Byte Access + CMD_READ_BYTE = 'R', + CMD_WRITE_LOW_BYTE = 'c', + CMD_WRITE_HIGH_BYTE = 'C', + CMD_WRITE_PAGE = 'm', + CMD_WRITE_EEPROM_BYTE = 'D', + CMD_READ_EEPROM_BYTE = 'd', + + // Lock and Fuse Bits + CMD_WRITE_LOCK_BITS = 'l', + CMD_READ_LOCK_BITS = 'r', + CMD_READ_LOW_FUSE_BITS = 'F', + CMD_READ_HIGH_FUSE_BITS = 'N', + CMD_READ_EXT_FUSE_BITS = 'Q', + + // Control + CMD_ENTER_PROG_MODE = 'P', + CMD_LEAVE_PROG_MODE = 'L', + CMD_EXIT_BOOTLOADER = 'E', + CMD_SET_LED = 'x', + CMD_CLEAR_LED = 'y', + CMD_SET_TYPE = 'T', +}; + + +// Memory types for block access +enum { + MEM_EEPROM = 'E', + MEM_FLASH = 'F', + MEM_USERSIG = 'U', + MEM_PRODSIG = 'P', +}; + + +// Command Responses +enum { + REPLY_ACK = '\r', + REPLY_YES = 'Y', + REPLY_ERROR = '?', +}; diff --git a/src/boot/src/sp_driver.S b/src/boot/src/sp_driver.S new file mode 100644 index 0000000..bbb1ee7 --- /dev/null +++ b/src/boot/src/sp_driver.S @@ -0,0 +1,403 @@ +;****************************************************************************** +;* $Revision: 1153 $ +;* $Date: 2007-12-18 09:48:23 +0100 (ti, 18 des 2007) $ +;* +;* Copyright (c) 2007, Atmel Corporation All rights reserved. +;* +;* Redistribution and use in source and binary forms, with or without +;* modification, are permitted provided that the following conditions are met: +;* +;* 1. Redistributions of source code must retain the above copyright notice, +;* this list of conditions and the following disclaimer. +;* +;* 2. Redistributions in binary form must reproduce the above copyright notice, +;* this list of conditions and the following disclaimer in the documentation +;* and/or other materials provided with the distribution. +;* +;* 3. The name of ATMEL may not be used to endorse or promote products derived +;* from this software without specific prior written permission. +;* +;* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED +;* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +;* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND +;* SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, +;* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +;* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +;* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +;* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +;* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;****************************************************************************** +;* +;* XMEGA Self-programming driver assembly source file. +;* +;* This file contains the low-level implementations for the +;* XMEGA Self-programming driver. It is written for the GCC Assembler. +;* +;* If any SPM instructions are used, the linker file must define a segment +;* named bootloader which must be located in the device Boot section. +;* This can be done by passing "-Wl,--section-start=.BOOT=0x020000" to the +;* linker with the correct address for the boot section. +;* +;* None of these routines clean up the NVM Command Register after use. It +;* is therefore important to write NVM_CMD_NO_OPERATION_gc (0x00) to this +;* register when finished using any of the functions in this driver. +;* +;* For all routines, it is important that any interrupt handlers do not +;* perform NVM operations. The user must implement a scheme for mutually +;* exclusive access to NVM. However, the 4-cycle timeout will work fine, +;* since writing to the Configuration Change Protection register (CCP) +;* automatically disables interrupts for 4 instruction cycles. +;* +;* Note on IAR calling convention: +;* Scratch registers: R18-R27, R30-R31 +;* Preserved registers: R2-R17, R28-R29 +;* Parameter registers: R8-R25 (2-,4-, or 8- byte alignment) +;* Return registers: R18-R25 (up to 64-bit) +;* +;* Application note: +;* AVR1316: XMEGA Self-programming +;* +;* Documentation +;* For comprehensive code documentation, supported compilers, compiler +;* settings and supported devices see readme.html +;* +;* Atmel Corporation: http:;www.atmel.com \n +;* Support email: avr@atmel.com + +#include + +; Defines not yet included in header file. +#define NVM_CMD_NO_OPERATION_gc 0x00 +#define NVM_CMD_READ_USER_SIG_ROW_gc 0x01 +#define NVM_CMD_READ_CALIB_ROW_gc 0x02 +#define NVM_CMD_READ_EEPROM_gc 0x06 +#define NVM_CMD_READ_FUSES_gc 0x07 +#define NVM_CMD_WRITE_LOCK_BITS_gc 0x08 +#define NVM_CMD_ERASE_USER_SIG_ROW_gc 0x18 +#define NVM_CMD_WRITE_USER_SIG_ROW_gc 0x1a +#define NVM_CMD_ERASE_APP_gc 0x20 +#define NVM_CMD_ERASE_APP_PAGE_gc 0x22 +#define NVM_CMD_LOAD_FLASH_BUFFER_gc 0x23 +#define NVM_CMD_WRITE_APP_PAGE_gc 0x24 +#define NVM_CMD_ERASE_WRITE_APP_PAGE_gc 0x25 +#define NVM_CMD_ERASE_FLASH_BUFFER_gc 0x26 +#define NVM_CMD_ERASE_BOOT_PAGE_gc 0x2a +#define NVM_CMD_WRITE_BOOT_PAGE_gc 0x2c +#define NVM_CMD_ERASE_WRITE_BOOT_PAGE_gc 0x2d +#define NVM_CMD_ERASE_EEPROM_gc 0x30 +#define NVM_CMD_ERASE_EEPROM_PAGE_gc 0x32 +#define NVM_CMD_LOAD_EEPROM_BUFFER_gc 0x33 +#define NVM_CMD_WRITE_EEPROM_PAGE_gc 0x34 +#define NVM_CMD_ERASE_WRITE_EEPROM_PAGE_gc 0x35 +#define NVM_CMD_ERASE_EEPROM_BUFFER_gc 0x36 +#define NVM_CMD_APP_CRC_gc 0x38 +#define NVM_CMD_BOOT_CRC_gc 0x39 +#define NVM_CMD_FLASH_RANGE_CRC_gc 0x3a +#define CCP_SPM_gc 0x9d +#define CCP_IOREG_gc 0xd8 + + +; Reads a byte from flash given by the address in R25:R24:R23:R22. +; +; Input: R25:R24:R23:R22. +; Returns: R24 - Read byte. +.section .text +.global SP_ReadByte + +SP_ReadByte: + in r19, RAMPZ ; Save RAMPZ. + out RAMPZ, r24 ; Load RAMPZ with the MSB of the address. + movw ZL, r22 ; Move the low bytes to the Z pointer + elpm r24, Z ; Extended load byte from address pointed to by Z. + out RAMPZ, r19 ; Restore RAMPZ register. + ret + + +; Reads a word from flash given by the address in R25:R24:R23:R22. +; +; Input: R25:R24:R23:R22. +; Returns: R25:R24 - Read word. +.section .text +.global SP_ReadWord + +SP_ReadWord: + in r19, RAMPZ ; Save RAMPZ. + out RAMPZ, r24 ; Load RAMPZ with the MSB of the address. + movw ZL, r22 ; Move the low bytes to the Z pointer + elpm r24, Z+ ; Extended load byte from address pointed to by Z. + elpm r25, Z ; Extended load byte from address pointed to by Z. + out RAMPZ, r19 ; Restore RAMPZ register. + ret + + +; Reads the calibration byte given by the index in R24. +; +; Input: R24 - Byte index. +; Returns: R24 - Calibration byte. +.section .text +.global SP_ReadCalibrationByte + +SP_ReadCalibrationByte: + ldi r20, NVM_CMD_READ_CALIB_ROW_gc ; Prepare NVM command in R20. + rjmp SP_CommonLPM ; Jump to common LPM code. + + +; Reads the user signature byte given by the index in R25:R24. +; +; Input: R25:R24 - Byte index. +; Returns: R24 - Signature byte. +.section .text +.global SP_ReadUserSignatureByte + +SP_ReadUserSignatureByte: + ldi r20, NVM_CMD_READ_USER_SIG_ROW_gc ; Prepare NVM command in R20. + rjmp SP_CommonLPM ; Jump to common LPM code. + + +; Reads the fuse byte given by the index in R24. +; +; Input: R24 - Byte index. +; Returns: R24 - Fuse byte. +.section .text +.global SP_ReadFuseByte + +SP_ReadFuseByte: + sts NVM_ADDR0, r24 ; Load fuse index into NVM Address Reg 0. + clr r24 ; Prepare a zero. + sts NVM_ADDR1, r24 ; Load zero into NVM Address Register 1. + sts NVM_ADDR2, r24 ; Load zero into NVM Address Register 2. + ldi r20, NVM_CMD_READ_FUSES_gc ; Prepare NVM command in R20. + rcall SP_CommonCMD ; Jump to common NVM Action code. + movw r24, r22 ; Move low byte to 1 byte return address. + ret + + +; Erases the user signature row. +.section .text +.global SP_EraseUserSignatureRow + +SP_EraseUserSignatureRow: + in r19, RAMPZ ; Save RAMPZ, restored in SP_CommonSPM. + ldi r20, NVM_CMD_ERASE_USER_SIG_ROW_gc ; Prepare NVM command in R20. + jmp SP_CommonSPM ; Jump to common SPM code. + + +; Writes the flash buffer to the user signature row. +.section .text +.global SP_WriteUserSignatureRow + +SP_WriteUserSignatureRow: + in r19, RAMPZ ; Save RAMPZ, restored in SP_CommonSPM. + ldi r20, NVM_CMD_WRITE_USER_SIG_ROW_gc ; Prepare NVM command in R20. + jmp SP_CommonSPM ; Jump to common SPM code. + + +; Erases the entire application section. +.section .text +.global SP_EraseApplicationSection + +SP_EraseApplicationSection: + in r19, RAMPZ ; Save RAMPZ, restored in SP_CommonSPM. + clr r24 ; Prepare a zero. + clr r25 + out RAMPZ, r24 ; Point into Application area. + ldi r20, NVM_CMD_ERASE_APP_gc ; Prepare NVM command in R20. + jmp SP_CommonSPM ; Jump to common SPM code. + + +; Writes the word from R23:R22 into the Flash page buffer at address R25:R24. +; +; Input: +; R25:R24 - Byte address into Flash page. +; R23:R22 - Word to write. +.section .text +.global SP_LoadFlashWord + +SP_LoadFlashWord: + in r19, RAMPZ ; Save RAMPZ, restored in SP_CommonSPM. + movw r0, r22 ; Prepare flash word in R1:R0. + ldi r20, NVM_CMD_LOAD_FLASH_BUFFER_gc ; Prepare NVM command in R20. + jmp SP_CommonSPM ; Jump to common SPM code. + + +; Writes an entire page from the SRAM buffer at +; address R25:R24 into the Flash page buffer. +; +; Note that you must define "-Wl,--section-start=.BOOT=0x020000" for the +; linker to place this function in the boot section with the correct address. +; +; Input: R25:R24 - 16-bit pointer to SRAM buffer. +.section .text +.global SP_LoadFlashPage + +SP_LoadFlashPage: + clr ZL ; Clear low byte of Z, to indicate start of page. + clr ZH ; Clear high byte of Z, to indicate start of page. + + out RAMPX, r1 ; Clear RAMPX pointer. + movw XL, r24 ; Load X with data buffer address. + + ldi r20, NVM_CMD_LOAD_FLASH_BUFFER_gc ; Prepare NVM command code in R20. + sts NVM_CMD, r20 ; Load it into NVM command register. + +#if APP_SECTION_PAGE_SIZE > 512 + ldi r22, ((APP_SECTION_PAGE_SIZE / 2) >> 8) +#endif + ldi r21, ((APP_SECTION_PAGE_SIZE / 2) & 0xff) ; Load R21 page word count. + ldi r18, CCP_SPM_gc ; Prepare Protect SPM signature in R16. + +SP_LoadFlashPage_1: + ld r0, X+ ; Load low byte from buffer into R0. + ld r1, X+ ; Load high byte from buffer into R1. + sts CCP, r18 ; Enable SPM operation (disables interrupts for 4 cycles). + spm ; Self-program. + adiw ZL, 2 ; Move Z to next Flash word. + +#if APP_SECTION_PAGE_SIZE > 512 + subi r21, 1 ; Decrement word count. + sbci r22, 0 +#else + dec r21 ; Decrement word count. +#endif + + brne SP_LoadFlashPage_1 ; Repeat until word cont is zero. + clr r1 ; Clear R1 for GCC _zero_reg_ to function properly. + ret + + +; Writes the page buffer to Flash at address R25:R24:R23:R22 +; in the application section. The address can point anywhere inside the page. +; +; Input: R25:R24:R23:R22 - Byte address into Flash page. +.section .text +.global SP_WriteApplicationPage + +SP_WriteApplicationPage: + in r19, RAMPZ ; Save RAMPZ, restored in SP_CommonSPM. + out RAMPZ, r24 ; Load RAMPZ with the MSB of the address. + movw r24, r22 ; Move low bytes of address to ZH:ZL from R23:R22 + ldi r20, NVM_CMD_WRITE_APP_PAGE_gc ; Prepare NVM command in R20. + jmp SP_CommonSPM ; Jump to common SPM code. + + +; Erases first and then writes the page buffer to the +; Flash page at address R25:R24:R23:R22 in the application section. The address +; can point anywhere inside the page. +; +; Input: R25:R24:R23:R22 - Byte address into Flash page. +.section .text +.global SP_EraseWriteApplicationPage + +SP_EraseWriteApplicationPage: + in r19, RAMPZ ; Save RAMPZ, restored in SP_CommonSPM. + out RAMPZ, r24 ; Load RAMPZ with the MSB of the address. + movw r24, r22 ; Move low bytes of address to ZH:ZL from R23:R22 + ldi r20, NVM_CMD_ERASE_WRITE_APP_PAGE_gc ; Prepare NVM command in R20. + jmp SP_CommonSPM ; Jump to common SPM code. + + + +; Locks all further access to SPM operations until next reset. +.section .text +.global SP_LockSPM + +SP_LockSPM: + ldi r18, CCP_IOREG_gc ; Prepare Protect IO-register signature in R18. + sts CCP, r18 ; Enable IO-register operation + ; (disables interrupts for 4 cycles). + ldi r18, NVM_SPMLOCK_bm ; Prepare bitmask for locking SPM into R18. + sts NVM_CTRLB, r18 ; Load bitmask into NVM Control Register B, + ; which locks SPM. + ret + + +; Wait for the SPM to finish and clears the command register. +; +; Note that this routine is blocking, and will halt any execution until the SPM +; is finished. +.section .text +.global SP_WaitForSPM + +SP_WaitForSPM: + lds r18, NVM_STATUS ; Load the NVM Status register. + sbrc r18, NVM_NVMBUSY_bp ; Check if bit is cleared. + rjmp SP_WaitForSPM ; Repeat check if bit is not cleared. + clr r18 + sts NVM_CMD, r18 ; Clear up command register to NO_OPERATION. + ret + + +; Called by several other routines, and contains common code +; for executing an NVM command, including the return statement itself. +; +; If the operation (NVM command) requires the NVM Address registers to be +; prepared, this must be done before jumping to this routine. +; +; Note that R25:R24:R23:R22 is used for returning results, even if the +; C-domain calling function only expects a single byte or even void. +; +; Input: R20 - NVM Command code. +; Returns: R25:R24:R23:R22 - 32-bit result from NVM operation. +.section .text + +SP_CommonCMD: + sts NVM_CMD, r20 ; Load command into NVM Command register. + ldi r18, CCP_IOREG_gc ; Prepare Protect IO-register signature in R18. + ldi r19, NVM_CMDEX_bm ; Prepare bitmask for setting NVM Command Execute + ; bit into R19. + sts CCP, r18 ; Enable IO-register operation + ; (disables interrupts for 4 cycles). + sts NVM_CTRLA, r19 ; Load bitmask into NVM Control Register A, + ; which executes the command. + lds r22, NVM_DATA0 ; Load NVM Data Register 0 into R22. + lds r23, NVM_DATA1 ; Load NVM Data Register 1 into R23. + lds r24, NVM_DATA2 ; Load NVM Data Register 2 into R24. + clr r25 ; Clear R25 in order to return a clean 32-bit value. + ret + + +; Called by several other routines, and contains common code +; for executing an LPM command, including the return statement itself. +; +; Note that R24 is used for returning results, even if the +; C-domain calling function expects a void. +; +; Input: +; R25:R24 - Low bytes of Z pointer. +; R20 - NVM Command code. +; +; Returns: R24 - Result from LPM operation. +.section .text + +SP_CommonLPM: + movw ZL, r24 ; Load index into Z. + sts NVM_CMD, r20 ; Load prepared command into NVM Command register. + lpm r24,Z + ret + + +; Called by several other routines, and contains common code +; for executing an SPM command, including the return statement itself. +; +; If the operation (SPM command) requires the R1:R0 registers to be +; prepared, this must be done before jumping to this routine. +; +; Note that you must define "-Wl,--section-start=.BOOT=0x020000" for the +; linker to place this function in the boot section with the correct address. +; +; Input: +; R1:R0 - Optional input to SPM command. +; R25:R24 - Low bytes of Z pointer. +; R20 - NVM Command code. +.section .text + +SP_CommonSPM: + movw ZL, r24 ; Load R25:R24 into Z. + sts NVM_CMD, r20 ; Load prepared command into NVM Command register. + ldi r18, CCP_SPM_gc ; Prepare Protect SPM signature in R18 + sts CCP, r18 ; Enable SPM operation (disables interrupts for 4 cycles). + spm ; Self-program. + clr r1 ; Clear R1 for GCC _zero_reg_ to function properly. + out RAMPZ, r19 ; Restore RAMPZ register. + ret diff --git a/src/boot/src/sp_driver.h b/src/boot/src/sp_driver.h new file mode 100644 index 0000000..0cb9180 --- /dev/null +++ b/src/boot/src/sp_driver.h @@ -0,0 +1,200 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +/******************************************************************************* + * $Revision: 1691 $ + * $Date: 2008-07-29 13:25:40 +0200 (ti, 29 jul 2008) $ \n + * + * Copyright (c) 2008, Atmel Corporation All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of ATMEL may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND + * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +/*! \file ********************************************************************** + * \brief XMEGA Self-programming driver header file. + * + * This file contains the function prototypes for the + * XMEGA Self-programming driver. + * If any SPM instructions are used, the linker file must define + * a segment named BOOT which must be located in the device boot section. + * + * + * None of these functions clean up the NVM Command Register after use. + * It is therefore important to write NVMCMD_NO_OPERATION (0x00) to this + * register when you are finished using any of the functions in this + * driver. + * + * For all functions, it is important that no interrupt handlers perform + * any NVM operations. The user must implement a scheme for mutually + * exclusive access to the NVM. However, the 4-cycle timeout will work + * fine, since writing to the Configuration Change Protection register + * (CCP) automatically disables interrupts for 4 instruction cycles. + * + * \par Application note: AVR1316: XMEGA Self-programming + * + * \par Documentation + * For comprehensive code documentation, supported compilers, compiler + * settings and supported devices see readme.html + * + * \author + * Atmel Corporation: http://www.atmel.com + * Support email: avr@atmel.com + ******************************************************************************/ + +#pragma once + +#include + +#include + +#ifndef APP_SECTION_PAGE_SIZE +#error APP_SECTION_PAGE_SIZE must be defined if not defined in header files. +#endif + +#ifndef APPTABLE_SECTION_START +#error APPTABLE_SECTION_START must be defined if not defined in header files. +#endif + + +/*! \brief Read a byte from flash. + * + * \param address Address to the location of the byte to read. + * \retval Byte read from flash. + */ +uint8_t SP_ReadByte(uint32_t address); + +/*! \brief Read a word from flash. + * + * This function reads one word from the flash. + * + * \param address Address to the location of the word to read. + * + * \retval word read from flash. + */ +uint16_t SP_ReadWord(uint32_t address); + +/*! \brief Read calibration byte at given index. + * + * This function reads one calibration byte from the Calibration signature row. + * + * \param index Index of the byte in the calibration signature row. + * + * \retval Calibration byte + */ +uint8_t SP_ReadCalibrationByte(uint8_t index); + +/*! \brief Read fuse byte from given index. + * + * This function reads the fuse byte at the given index. + * + * \param index Index of the fuse byte. + * \retval Fuse byte + */ +uint8_t SP_ReadFuseByte(uint8_t index); + +/*! \brief Read user signature at given index. + * + * \param index Index of the byte in the user signature row. + * \retval User signature byte + */ +uint8_t SP_ReadUserSignatureByte(uint16_t index); + +/// Erase user signature row. +void SP_EraseUserSignatureRow(); + +/// Write user signature row. +void SP_WriteUserSignatureRow(); + +/*! \brief Erase entire application section. + * + * \note If the lock bits is set to not allow SPM in the application or + * application table section the erase is not done. + */ +void SP_EraseApplicationSection(); + +/*! \brief Erase and write page buffer to application or application table + * section at byte address. + * + * \param address Byte address for flash page. + */ +void SP_EraseWriteApplicationPage(uint32_t address); + +/*! \brief Write page buffer to application or application table section at + * byte address. + * + * \note The page that is written to must be erased before it is written to. + * + * \param address Byte address for flash page. + */ +void SP_WriteApplicationPage(uint32_t address); + +/*! \brief Load one word into Flash page buffer. + * + * \param address Position in inside the flash page buffer. + * \param data Value to be put into the buffer. + */ +void SP_LoadFlashWord(uint16_t address, uint16_t data); + +/*! \brief Load entire page from SRAM buffer into Flash page buffer. + * + * \param data Pointer to the data to put in buffer. + * + * \note The __near keyword limits the pointer to two bytes which means that + * only data up to 64K (internal SRAM) can be used. + */ +void SP_LoadFlashPage(const uint8_t *data); + +/// Flush Flash page buffer. +void SP_EraseFlashBuffer(); + +/// Disables the use of SPM until the next reset. +void SP_LockSPM(); + +/// Waits for the SPM to finish and clears the command register. +void SP_WaitForSPM(); diff --git a/src/jig/.gitignore b/src/jig/.gitignore new file mode 100644 index 0000000..fa21f96 --- /dev/null +++ b/src/jig/.gitignore @@ -0,0 +1,3 @@ +/build +/*.hex +/*.elf diff --git a/src/jig/Makefile b/src/jig/Makefile new file mode 100644 index 0000000..e872121 --- /dev/null +++ b/src/jig/Makefile @@ -0,0 +1,131 @@ +# Makefile for the project Bulidbotics firmware +PROJECT = bbctrl-jig-firmware +MCU = atxmega192a3u +CLOCK = 32000000 +VERSION = 0.0.1 + +TARGET = $(PROJECT).elf + +# Compile flags +CC = avr-gcc +CPP = avr-g++ + +COMMON = -mmcu=$(MCU) -flto -fwhole-program + +CFLAGS += $(COMMON) +CFLAGS += -Wall -Werror +CFLAGS += -Wno-error=strict-aliasing # for _invsqrt +CFLAGS += -std=gnu99 -DF_CPU=$(CLOCK)UL -O3 +CFLAGS += -funsigned-bitfields -fpack-struct -fshort-enums -funsigned-char +CFLAGS += -MD -MP -MT $@ -MF build/dep/$(@F).d +CFLAGS += -Isrc -DVERSION=\"$(VERSION)\" + +# Linker flags +LDFLAGS += $(COMMON) -Wl,-u,vfprintf -lprintf_flt -lm +LIBS += -lm + +# EEPROM flags +EEFLAGS += -j .eeprom +EEFLAGS += --set-section-flags=.eeprom="alloc,load" +EEFLAGS += --change-section-lma .eeprom=0 --no-change-warnings + +# Programming flags +PROGRAMMER = avrispmkII +#PROGRAMMER = jtag3pdi +PDEV = usb +AVRDUDE_OPTS = -c $(PROGRAMMER) -p $(MCU) -P $(PDEV) + +FUSE0=0xff +FUSE1=0x00 +FUSE2=0xbe +FUSE4=0xff +FUSE5=0xeb + +# SRC +SRC = $(wildcard src/*.c) +OBJ = $(patsubst src/%.c,build/%.o,$(SRC)) + +# Build +all: + @$(MAKE) $(PROJECT).hex + @$(MAKE) size + +# Compile +build/%.o: src/%.c + @mkdir -p $(shell dirname $@) + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +build/%.o: src/%.S + @mkdir -p $(shell dirname $@) + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +# Link +$(TARGET): $(OBJ) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.hex: %.elf + avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $< $@ + +%.eep: %.elf + avr-objcopy $(EEFLAGS) -O ihex $< $@ + +%.lss: %.elf + avr-objdump -h -S $< > $@ + +_size: + @for X in A B C; do\ + echo '****************************************************************' ;\ + avr-size -$$X --mcu=$(MCU) $(SIZE_TARGET) ;\ + done + +size: $(TARGET) + @$(MAKE) SIZE_TARGET=$< _size + +# Program +init: + $(MAKE) erase + -$(MAKE) fuses + $(MAKE) program + +reset: + avrdude $(AVRDUDE_OPTS) + +erase: + avrdude $(AVRDUDE_OPTS) -e + +program: $(PROJECT).hex + avrdude $(AVRDUDE_OPTS) -U flash:w:$(PROJECT).hex:i + +verify: $(PROJECT).hex + avrdude $(AVRDUDE_OPTS) -U flash:v:$(PROJECT).hex:i + +fuses: + avrdude $(AVRDUDE_OPTS) -U fuse0:w:$(FUSE0):m -U fuse1:w:$(FUSE1):m \ + -U fuse2:w:$(FUSE2):m -U fuse4:w:$(FUSE4):m -U fuse5:w:$(FUSE5):m + +read_fuses: + avrdude $(AVRDUDE_OPTS) -q -q -U fuse0:r:-:h -U fuse1:r:-:h -U fuse2:r:-:h \ + -U fuse4:r:-:h -U fuse5:r:-:h + +signature: + avrdude $(AVRDUDE_OPTS) -q -q -U signature:r:-:h + +prodsig: + avrdude $(AVRDUDE_OPTS) -q -q -U prodsig:r:-:h + +usersig: + avrdude $(AVRDUDE_OPTS) -q -q -U usersig:r:-:h + +# Clean +tidy: + rm -f $(shell find -name \*~ -o -name \#\*) + +clean: tidy + rm -rf $(PROJECT).elf $(PROJECT).hex $(PROJECT).eep $(PROJECT).lss \ + $(PROJECT).map build + +.PHONY: tidy clean size all reset erase program fuses read_fuses prodsig +.PHONY: signature usersig + +# Dependencies +-include $(shell mkdir -p build/dep) $(wildcard build/dep/*) diff --git a/src/jig/src/config.h b/src/jig/src/config.h new file mode 100644 index 0000000..ae699d0 --- /dev/null +++ b/src/jig/src/config.h @@ -0,0 +1,103 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "pins.h" + + +// Pins +enum { + STALL_X_PIN = PORT_A << 3, + STALL_Y_PIN, + STALL_Z_PIN, + STALL_A_PIN, + SPIN_DIR_PIN, + SPIN_ENABLE_PIN, + ANALOG_PIN, + PROBE_PIN, + + MIN_X_PIN = PORT_B << 3, + MAX_X_PIN, + MIN_A_PIN, + MAX_A_PIN, + MIN_Y_PIN, + MAX_Y_PIN, + MIN_Z_PIN, + MAX_Z_PIN, + + SDA_PIN = PORT_C << 3, + SCL_PIN, + SERIAL_RX_PIN, + SERIAL_TX_PIN, + SERIAL_CTS_PIN, + SPI_CLK_PIN, + SPI_MISO_PIN, + SPI_MOSI_PIN, + + ENCODER_X_A_PIN = PORT_D << 3, + ENCODER_X_B_PIN, + SPI_CS_A_PIN, + SPI_CS_Z_PIN, + SPIN_PWM_PIN, + SWITCH_1_PIN, + RS485_RO_PIN, + RS485_DI_PIN, + + STEP_Y_PIN = PORT_E << 3, + SPI_CS_Y_PIN, + DIR_X_PIN, + DIR_Y_PIN, + STEP_A_PIN, + SWITCH_2_PIN, + DIR_Z_PIN, + DIR_A_PIN, + + STEP_Z_PIN = PORT_F << 3, + RS485_RW_PIN, + FAULT_PIN, + ESTOP_PIN, + MOTOR_FAULT_PIN, + MOTOR_ENABLE_PIN, + NC_0_PIN, + NC_1_PIN, +}; + + +// Serial settings +#define SERIAL_BAUD USART_BAUD_115200 +#define SERIAL_PORT USARTC0 +#define SERIAL_DRE_vect USARTC0_DRE_vect +#define SERIAL_RXC_vect USARTC0_RXC_vect + + +// Input +#define INPUT_BUFFER_LEN 255 // text buffer size (255 max) + + +// Encoder +#define ENCODER_LINES 600 diff --git a/src/jig/src/encoder.c b/src/jig/src/encoder.c new file mode 100644 index 0000000..3ef3004 --- /dev/null +++ b/src/jig/src/encoder.c @@ -0,0 +1,85 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "encoder.h" + +#include "pins.h" +#include "config.h" + +#include + + +typedef struct { + int8_t pin_a; + int8_t pin_b; + + uint16_t position; + float velocity; +} encoder_t; + + +encoder_t encoders[] = { + {ENCODER_X_A_PIN, ENCODER_X_B_PIN}, + {0, 0} +}; + + +void encoder_init() { + for (int i = 0; encoders[i].pin_a; i++) { + // Inputs + DIRCLR_PIN(encoders[i].pin_a); + DIRCLR_PIN(encoders[i].pin_b); + + // Pullup and level sensing + PINCTRL_PIN(encoders[i].pin_a) = PORT_OPC_PULLUP_gc | PORT_ISC_LEVEL_gc; + PINCTRL_PIN(encoders[i].pin_b) = PORT_OPC_PULLUP_gc | PORT_ISC_LEVEL_gc; + } + + // Event channel + EVSYS_CH0MUX = EVSYS_CHMUX_PORTD_PIN0_gc; + EVSYS_CH0CTRL = EVSYS_QDEN_bm | EVSYS_DIGFILT_2SAMPLES_gc; + + // Timer config + TCC0.CTRLD = TC_EVACT_QDEC_gc | TC_EVSEL_CH0_gc; + TCC0.PER = ENCODER_LINES * 4 - 1; + TCC0.CTRLA = TC_CLKSEL_DIV1_gc; // Enable +} + + +void encoder_rtc_callback() { + uint16_t position = TCC0.CNT; + + int32_t delta = + ((int32_t)position - encoders[0].position) % (ENCODER_LINES * 4); + + encoders[0].position = position; + encoders[0].velocity = encoders[0].velocity * 0.95 + delta * 5.0; // steps/sec +} + + +uint16_t encoder_get_position(int i) {return encoders[i].position;} +float encoder_get_velocity(int i) {return encoders[i].velocity;} diff --git a/src/jig/src/encoder.h b/src/jig/src/encoder.h new file mode 100644 index 0000000..1153b7b --- /dev/null +++ b/src/jig/src/encoder.h @@ -0,0 +1,36 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include + +void encoder_init(); +void encoder_rtc_callback(); + +uint16_t encoder_get_position(int i); +float encoder_get_velocity(int i); diff --git a/src/jig/src/main.c b/src/jig/src/main.c new file mode 100644 index 0000000..3397475 --- /dev/null +++ b/src/jig/src/main.c @@ -0,0 +1,97 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "usart.h" +#include "rtc.h" +#include "encoder.h" + +#include +#include + +#include + +#include +#include + + +static void _clock_init() { + // 12-16 MHz crystal; 0.4-16 MHz XTAL w/ 16K CLK startup + OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc; + OSC.CTRL = OSC_XOSCEN_bm; // enable external crystal oscillator + while (!(OSC.STATUS & OSC_XOSCRDY_bm)); // wait for oscillator ready + + OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | 2; // PLL source, 2x (32 MHz sys clock) + OSC.CTRL = OSC_PLLEN_bm | OSC_XOSCEN_bm; // Enable PLL & External Oscillator + while (!(OSC.STATUS & OSC_PLLRDY_bm)); // wait for PLL ready + + CCP = CCP_IOREG_gc; + CLK.CTRL = CLK_SCLKSEL_PLL_gc; // switch to PLL clock + + OSC.CTRL &= ~OSC_RC2MEN_bm; // disable internal 2 MHz clock +} + + +int main() { + // Init + cli(); // disable interrupts + + _clock_init(); + rtc_init(); // real-time clock + usart_init(); // serial port + encoder_init(); // rotary encoders + + sei(); // enable interrupts + + // Splash + fprintf_P(stdout, PSTR("\nBuildbotics Jig " VERSION "\n")); + + usart_set(USART_ECHO, true); + + uint32_t timer = 0; + uint16_t last = 0; + + // Main loop + while (true) { + char *line = usart_readline(); + if (line) { + printf("\n%s\n", line); + printf("%ld\n", rtc_get_time()); + } + + if (rtc_expired(timer)) { + timer += 100; + + uint16_t encoder = encoder_get_position(0); + if (encoder != last) { + last = encoder; + printf("%5d step %8.2f step/sec\n", encoder, encoder_get_velocity(0)); + } + } + } + + return 0; +} diff --git a/src/jig/src/pins.c b/src/jig/src/pins.c new file mode 100644 index 0000000..65782fd --- /dev/null +++ b/src/jig/src/pins.c @@ -0,0 +1,31 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "pins.h" + + +PORT_t *pin_ports[] = {&PORTA, &PORTB, &PORTC, &PORTD, &PORTE, &PORTF}; diff --git a/src/jig/src/pins.h b/src/jig/src/pins.h new file mode 100644 index 0000000..49ae19e --- /dev/null +++ b/src/jig/src/pins.h @@ -0,0 +1,52 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +enum {PORT_A = 1, PORT_B, PORT_C, PORT_D, PORT_E, PORT_F}; + +#define PORT(PIN) pin_ports[(PIN >> 3) - 1] +#define BM(PIN) (1 << (PIN & 7)) + +#ifdef __AVR__ +#include + +extern PORT_t *pin_ports[]; + +#define DIRSET_PIN(PIN) PORT(PIN)->DIRSET = BM(PIN) +#define DIRCLR_PIN(PIN) PORT(PIN)->DIRCLR = BM(PIN) +#define OUTCLR_PIN(PIN) PORT(PIN)->OUTCLR = BM(PIN) +#define OUTSET_PIN(PIN) PORT(PIN)->OUTSET = BM(PIN) +#define OUTTGL_PIN(PIN) PORT(PIN)->OUTTGL = BM(PIN) +#define OUT_PIN(PIN) (!!(PORT(PIN)->OUT & BM(PIN))) +#define IN_PIN(PIN) (!!(PORT(PIN)->IN & BM(PIN))) +#define PINCTRL_PIN(PIN) ((&PORT(PIN)->PIN0CTRL)[PIN & 7]) + +#define SET_PIN(PIN, X) \ + do {if (X) OUTSET_PIN(PIN); else OUTCLR_PIN(PIN);} while (0); + +#endif // __AVR__ diff --git a/src/jig/src/ringbuf.def b/src/jig/src/ringbuf.def new file mode 100644 index 0000000..5717a6d --- /dev/null +++ b/src/jig/src/ringbuf.def @@ -0,0 +1,158 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +/* This file defines an X-Macro ring buffer. It can be used like this: + * + * #define RING_BUF_NAME tx_buf + * #define RING_BUF_SIZE 256 + * #include "ringbuf.def" + * + * This will define the following functions: + * + * void _init(); + * int _empty(); + * int _full(); + * _peek(); + * void _pop(); + * void _push( data); + * + * Where is defined by RING_BUF_NAME and by RING_BUF_TYPE. + * RING_BUF_SIZE defines the length of the ring buffer and must be a power of 2. + * + * The data type and index type both default to uint8_t but can be changed by + * defining RING_BUF_TYPE and RING_BUF_INDEX_TYPE respectively. + * + * By default these functions are declared static inline but this can be changed + * by defining RING_BUF_FUNC. + */ + +#include + +#ifndef RING_BUF_NAME +#error Must define RING_BUF_NAME +#endif + +#ifndef RING_BUF_SIZE +#error Must define RING_BUF_SIZE +#endif + +#ifndef RING_BUF_TYPE +#define RING_BUF_TYPE uint8_t +#endif + +#ifndef RING_BUF_INDEX_TYPE +#define RING_BUF_INDEX_TYPE uint8_t +#endif + +#ifndef RING_BUF_FUNC +#define RING_BUF_FUNC static inline +#endif + +#define RING_BUF_MASK (RING_BUF_SIZE - 1) +#if (RING_BUF_SIZE & RING_BUF_MASK) +#error RING_BUF_SIZE is not a power of 2 +#endif + +#ifndef CONCAT +#define _CONCAT(prefix, name) prefix##name +#define CONCAT(prefix, name) _CONCAT(prefix, name) +#endif + +#define RING_BUF_STRUCT CONCAT(RING_BUF_NAME, _ring_buf_t) +#define RING_BUF CONCAT(RING_BUF_NAME, _ring_buf) + +typedef struct { + RING_BUF_TYPE buf[RING_BUF_SIZE]; + volatile RING_BUF_INDEX_TYPE head; + volatile RING_BUF_INDEX_TYPE tail; +} RING_BUF_STRUCT; + +static RING_BUF_STRUCT RING_BUF; + + +RING_BUF_FUNC void CONCAT(RING_BUF_NAME, _init)() { + RING_BUF.head = RING_BUF.tail = 0; +} + + +#define RING_BUF_INC CONCAT(RING_BUF_NAME, _inc) +RING_BUF_FUNC RING_BUF_INDEX_TYPE RING_BUF_INC(RING_BUF_INDEX_TYPE x) { + return (x + 1) & RING_BUF_MASK; +} + + +RING_BUF_FUNC int CONCAT(RING_BUF_NAME, _empty)() { + return RING_BUF.head == RING_BUF.tail; +} + + +RING_BUF_FUNC int CONCAT(RING_BUF_NAME, _full)() { + return RING_BUF.head == RING_BUF_INC(RING_BUF.tail); +} + + +RING_BUF_FUNC int CONCAT(RING_BUF_NAME, _fill)() { + return (RING_BUF.tail - RING_BUF.head) & RING_BUF_MASK; +} + + +RING_BUF_FUNC int CONCAT(RING_BUF_NAME, _space)() { + return RING_BUF_SIZE - CONCAT(RING_BUF_NAME, _fill)(); +} + + +RING_BUF_FUNC RING_BUF_TYPE CONCAT(RING_BUF_NAME, _peek)() { + return RING_BUF.buf[RING_BUF.head]; +} + + +RING_BUF_FUNC RING_BUF_TYPE CONCAT(RING_BUF_NAME, _get)(int offset) { + return RING_BUF.buf[(RING_BUF.head + offset) & RING_BUF_MASK]; +} + + +RING_BUF_FUNC void CONCAT(RING_BUF_NAME, _pop)() { + RING_BUF.head = RING_BUF_INC(RING_BUF.head); +} + + +RING_BUF_FUNC void CONCAT(RING_BUF_NAME, _push)(RING_BUF_TYPE data) { + RING_BUF.buf[RING_BUF.tail] = data; + RING_BUF.tail = RING_BUF_INC(RING_BUF.tail); +} + + +#undef RING_BUF +#undef RING_BUF_STRUCT +#undef RING_BUF_INC +#undef RING_BUF_MASK + +#undef RING_BUF_NAME +#undef RING_BUF_SIZE +#undef RING_BUF_TYPE +#undef RING_BUF_INDEX_TYPE +#undef RING_BUF_FUNC diff --git a/src/jig/src/rtc.c b/src/jig/src/rtc.c new file mode 100644 index 0000000..9ec4d3c --- /dev/null +++ b/src/jig/src/rtc.c @@ -0,0 +1,69 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "rtc.h" + +#include "encoder.h" + +#include +#include + + +static uint32_t ticks; + + +ISR(RTC_OVF_vect) { + ticks++; + encoder_rtc_callback(); +} + + +/// Initialize and start the clock +/// This routine follows the code in app note AVR1314. +void rtc_init() { + ticks = 0; + + PR.PRGEN &= ~PR_RTC_bm; // Disable power reduction + + OSC.CTRL |= OSC_RC32KEN_bm; // enable internal 32kHz. + while (!(OSC.STATUS & OSC_RC32KRDY_bm)); // 32kHz osc stabilize + while (RTC.STATUS & RTC_SYNCBUSY_bm); // wait RTC not busy + + CLK.RTCCTRL = CLK_RTCSRC_RCOSC32_gc | CLK_RTCEN_bm; // 32kHz clock as RTC src + while (RTC.STATUS & RTC_SYNCBUSY_bm); // wait RTC not busy + + // the following must be in this order or it doesn't work + RTC.PER = 33; // overflow period ~1ms + RTC.INTCTRL = RTC_OVFINTLVL_LO_gc; // overflow LO interrupt + RTC.CTRL = RTC_PRESCALER_DIV1_gc; // no prescale + + PMIC.CTRL |= PMIC_LOLVLEN_bm; // Interrupt level on +} + + +uint32_t rtc_get_time() {return ticks;} +bool rtc_expired(uint32_t t) {return 0 <= (int32_t)(ticks - t);} diff --git a/src/jig/src/rtc.h b/src/jig/src/rtc.h new file mode 100644 index 0000000..6313ae9 --- /dev/null +++ b/src/jig/src/rtc.h @@ -0,0 +1,37 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +#include +#include + +void rtc_init(); +uint32_t rtc_get_time(); +int32_t rtc_diff(uint32_t t); +bool rtc_expired(uint32_t t); diff --git a/src/jig/src/usart.c b/src/jig/src/usart.c new file mode 100644 index 0000000..38e723e --- /dev/null +++ b/src/jig/src/usart.c @@ -0,0 +1,282 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "usart.h" +//#include "cpp_magic.h" +#include "config.h" + +#include +#include + +#include +#include + +// Ring buffers +#define RING_BUF_NAME tx_buf +#define RING_BUF_SIZE USART_TX_RING_BUF_SIZE +#include "ringbuf.def" + +#define RING_BUF_NAME rx_buf +#define RING_BUF_SIZE USART_RX_RING_BUF_SIZE +#include "ringbuf.def" + +static int usart_flags = USART_CRLF; + + +static void _set_dre_interrupt(bool enable) { + if (enable) SERIAL_PORT.CTRLA |= USART_DREINTLVL_HI_gc; + else SERIAL_PORT.CTRLA &= ~USART_DREINTLVL_HI_gc; +} + + +static void _set_rxc_interrupt(bool enable) { + if (enable) { + SERIAL_PORT.CTRLA |= USART_RXCINTLVL_HI_gc; + if (4 <= rx_buf_space()) OUTCLR_PIN(SERIAL_CTS_PIN); // CTS Lo (enable) + + } else SERIAL_PORT.CTRLA &= ~USART_RXCINTLVL_HI_gc; +} + + +// Data register empty interrupt vector +ISR(SERIAL_DRE_vect) { + if (tx_buf_empty()) _set_dre_interrupt(false); // Disable interrupt + + else { + SERIAL_PORT.DATA = tx_buf_peek(); + tx_buf_pop(); + } +} + + +// Data received interrupt vector +ISR(SERIAL_RXC_vect) { + if (rx_buf_full()) _set_rxc_interrupt(false); // Disable interrupt + + else { + uint8_t data = SERIAL_PORT.DATA; + rx_buf_push(data); + if (rx_buf_space() < 4) OUTSET_PIN(SERIAL_CTS_PIN); // CTS Hi (disable) + } +} + + +static int _usart_putchar(char c, FILE *f) { + usart_putc(c); + return 0; +} + +static FILE _stdout = FDEV_SETUP_STREAM(_usart_putchar, 0, _FDEV_SETUP_WRITE); + + +void usart_init(void) { + // Setup ring buffer + tx_buf_init(); + rx_buf_init(); + + PR.PRPC &= ~PR_USART0_bm; // Disable power reduction + + // Setup pins + OUTSET_PIN(SERIAL_CTS_PIN); // CTS Hi (disable) + DIRSET_PIN(SERIAL_CTS_PIN); // CTS Output + OUTSET_PIN(SERIAL_TX_PIN); // Tx High + DIRSET_PIN(SERIAL_TX_PIN); // Tx Output + DIRCLR_PIN(SERIAL_RX_PIN); // Rx Input + + // Set baud rate + usart_set_baud(SERIAL_BAUD); + + // No parity, 8 data bits, 1 stop bit + SERIAL_PORT.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | + USART_CHSIZE_8BIT_gc; + + // Configure receiver and transmitter + SERIAL_PORT.CTRLB = USART_RXEN_bm | USART_TXEN_bm | USART_CLK2X_bm; + + PMIC.CTRL |= PMIC_HILVLEN_bm; // Interrupt level on + + // Connect IO + stdout = &_stdout; + stderr = &_stdout; + + // Enable Rx + _set_rxc_interrupt(true); +} + + +static void _set_baud(uint16_t bsel, uint8_t bscale) { + SERIAL_PORT.BAUDCTRLB = (uint8_t)((bscale << 4) | (bsel >> 8)); + SERIAL_PORT.BAUDCTRLA = bsel; +} + + +void usart_set_baud(int baud) { + // The BSEL / BSCALE values provided below assume a 32 Mhz clock + // Assumes CTRLB CLK2X bit (0x04) is set + // See http://www.avrcalc.elektronik-projekt.de/xmega/baud_rate_calculator + + switch (baud) { + case USART_BAUD_9600: _set_baud(3325, 0b1101); break; + case USART_BAUD_19200: _set_baud(3317, 0b1100); break; + case USART_BAUD_38400: _set_baud(3301, 0b1011); break; + case USART_BAUD_57600: _set_baud(1095, 0b1100); break; + case USART_BAUD_115200: _set_baud(1079, 0b1011); break; + case USART_BAUD_230400: _set_baud(1047, 0b1010); break; + case USART_BAUD_460800: _set_baud(983, 0b1001); break; + case USART_BAUD_921600: _set_baud(107, 0b1011); break; + case USART_BAUD_500000: _set_baud(1, 0b0010); break; + case USART_BAUD_1000000: _set_baud(1, 0b0001); break; + } +} + + +void usart_set(int flag, bool enable) { + if (enable) usart_flags |= flag; + else usart_flags &= ~flag; +} + + +bool usart_is_set(int flags) { + return (usart_flags & flags) == flags; +} + + +void usart_putc(char c) { + while (tx_buf_full() || (usart_flags & USART_FLUSH)) continue; + + tx_buf_push(c); + + _set_dre_interrupt(true); // Enable interrupt + + if ((usart_flags & USART_CRLF) && c == '\n') usart_putc('\r'); +} + + +void usart_puts(const char *s) { + while (*s) usart_putc(*s++); +} + + +int8_t usart_getc() { + while (rx_buf_empty()) continue; + + uint8_t data = rx_buf_peek(); + rx_buf_pop(); + + _set_rxc_interrupt(true); // Enable interrupt + + return data; +} + + +char *usart_readline() { + static char line[INPUT_BUFFER_LEN]; + static int i = 0; + bool eol = false; + + while (!rx_buf_empty()) { + char data = usart_getc(); + + if (usart_flags & USART_ECHO) usart_putc(data); + + switch (data) { + case '\r': case '\n': eol = true; break; + + case '\b': // BS - backspace + if (usart_flags & USART_ECHO) { + usart_putc(' '); + usart_putc('\b'); + } + if (i) i--; + break; + + case 0x18: // CAN - Cancel or CTRL-X + if (usart_flags & USART_ECHO) + while (i) { + usart_putc('\b'); + usart_putc(' '); + usart_putc('\b'); + i--; + } + + i = 0; + break; + + default: + line[i++] = data; + if (i == INPUT_BUFFER_LEN - 1) eol = true; + break; + } + + if (eol) { + line[i] = 0; + i = 0; + return line; + } + } + + return 0; +} + + +int16_t usart_peek() { + return rx_buf_empty() ? -1 : rx_buf_peek(); +} + + +void usart_flush() { + usart_set(USART_FLUSH, true); + + while (!tx_buf_empty() || !(SERIAL_PORT.STATUS & USART_DREIF_bm) || + !(SERIAL_PORT.STATUS & USART_TXCIF_bm)) + continue; +} + + +void usart_rx_flush() { + rx_buf_init(); +} + + +int16_t usart_rx_space() { + return rx_buf_space(); +} + + +int16_t usart_rx_fill() { + return rx_buf_fill(); +} + + +int16_t usart_tx_space() { + return tx_buf_space(); +} + + +int16_t usart_tx_fill() { + return tx_buf_fill(); +} diff --git a/src/jig/src/usart.h b/src/jig/src/usart.h new file mode 100644 index 0000000..1c95f1c --- /dev/null +++ b/src/jig/src/usart.h @@ -0,0 +1,77 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + + +#include +#include + +#define USART_TX_RING_BUF_SIZE 256 +#define USART_RX_RING_BUF_SIZE 256 + +enum { + USART_BAUD_9600, + USART_BAUD_19200, + USART_BAUD_38400, + USART_BAUD_57600, + USART_BAUD_115200, + USART_BAUD_230400, + USART_BAUD_460800, + USART_BAUD_921600, + USART_BAUD_500000, + USART_BAUD_1000000 +}; + +enum { + USART_CRLF = 1 << 0, + USART_ECHO = 1 << 1, + USART_XOFF = 1 << 2, + USART_FLUSH = 1 << 3, +}; + +void usart_init(); +void usart_set_baud(int baud); +void usart_set(int flag, bool enable); +bool usart_is_set(int flags); +void usart_putc(char c); +void usart_puts(const char *s); +int8_t usart_getc(); +char *usart_readline(); +int16_t usart_peek(); +void usart_flush(); + +void usart_rx_flush(); +int16_t usart_rx_fill(); +int16_t usart_rx_space(); +inline bool usart_rx_empty() {return !usart_rx_fill();} +inline bool usart_rx_full() {return !usart_rx_space();} + +int16_t usart_tx_fill(); +int16_t usart_tx_space(); +inline bool usart_tx_empty() {return !usart_tx_fill();} +inline bool usart_tx_full() {return !usart_tx_space();} diff --git a/src/js/admin-general-view.js b/src/js/admin-general-view.js new file mode 100644 index 0000000..6c5d5fc --- /dev/null +++ b/src/js/admin-general-view.js @@ -0,0 +1,138 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +var api = require('./api'); + + +module.exports = { + template: '#admin-general-view-template', + props: ['config', 'state'], + + + data: function () { + return { + configRestored: false, + confirmReset: false, + configReset: false, + latest: '', + autoCheckUpgrade: true + } + }, + + + events: { + latest_version: function (version) {this.latest = version} + }, + + + ready: function () { + this.autoCheckUpgrade = this.config.admin['auto-check-upgrade'] + }, + + + methods: { + backup: function () { + document.getElementById('download-target').src = '/api/config/download'; + }, + + + restore_config: function () { + // If we don't reset the form the browser may cache file if name is same + // even if contents have changed + $('.restore-config')[0].reset(); + $('.restore-config input').click(); + }, + + + restore: function (e) { + var files = e.target.files || e.dataTransfer.files; + if (!files.length) return; + + var fr = new FileReader(); + fr.onload = function (e) { + var config; + + try { + config = JSON.parse(e.target.result); + } catch (ex) { + api.alert("Invalid config file"); + return; + } + + api.put('config/save', config).done(function (data) { + this.$dispatch('update'); + this.configRestored = true; + + }.bind(this)).fail(function (error) { + api.alert('Restore failed', error); + }) + }.bind(this); + + fr.readAsText(files[0]); + }, + + + reset: function () { + this.confirmReset = false; + api.put('config/reset').done(function () { + this.$dispatch('update'); + this.configReset = true; + + }.bind(this)).fail(function (error) { + api.alert('Reset failed', error); + }); + }, + + + check: function () {this.$dispatch('check')}, + upgrade: function () {this.$dispatch('upgrade')}, + + + upload_firmware: function () { + // If we don't reset the form the browser may cache file if name is same + // even if contents have changed + $('.upload-firmware')[0].reset(); + $('.upload-firmware input').click(); + }, + + + upload: function (e) { + var files = e.target.files || e.dataTransfer.files; + if (!files.length) return; + this.$dispatch('upload', files[0]); + }, + + + change_auto_check_upgrade: function () { + this.config.admin['auto-check-upgrade'] = this.autoCheckUpgrade; + this.$dispatch('config-changed'); + } + } +} diff --git a/src/js/admin-network-view.js b/src/js/admin-network-view.js new file mode 100644 index 0000000..1dae7b2 --- /dev/null +++ b/src/js/admin-network-view.js @@ -0,0 +1,177 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +var api = require('./api'); + + +module.exports = { + template: '#admin-network-view-template', + props: ['config', 'state'], + + + data: function () { + return { + hostnameSet: false, + usernameSet: false, + passwordSet: false, + redirectTimeout: 0, + hostname: '', + username: '', + current: '', + password: '', + password2: '', + wifi_mode: 'client', + wifi_ssid: '', + wifi_ch: undefined, + wifi_pass: '', + wifiConfirm: false, + rebooting: false + } + }, + + + ready: function () { + api.get('hostname').done(function (hostname) { + this.hostname = hostname; + }.bind(this)); + + api.get('remote/username').done(function (username) { + this.username = username; + }.bind(this)); + + api.get('wifi').done(function (config) { + this.wifi_mode = config.mode; + this.wifi_ssid = config.ssid; + this.wifi_ch = config.channel; + }.bind(this)); + }, + + + methods: { + redirect: function (hostname) { + if (0 < this.redirectTimeout) { + this.redirectTimeout -= 1; + setTimeout(function () {this.redirect(hostname)}.bind(this), 1000); + + } else location.hostname = hostname; + }, + + + set_hostname: function () { + api.put('hostname', {hostname: this.hostname}).done(function () { + this.redirectTimeout = 45; + this.hostnameSet = true; + + api.put('reboot').always(function () { + if (String(location.hostname) == 'localhost') return; + + var hostname = this.hostname; + if (String(location.hostname).endsWith('.local')) + hostname += '.local' + this.$dispatch('hostname-changed', hostname); + this.redirect(hostname); + }.bind(this)); + + }.bind(this)).fail(function (error) { + api.alert('Set hostname failed', error); + }) + }, + + + set_username: function () { + api.put('remote/username', {username: this.username}).done(function () { + this.usernameSet = true; + }.bind(this)).fail(function (error) { + api.alert('Set username failed', error); + }) + }, + + + set_password: function () { + if (this.password != this.password2) { + alert('Passwords to not match'); + return; + } + + if (this.password.length < 6) { + alert('Password too short'); + return; + } + + api.put('remote/password', { + current: this.current, + password: this.password + }).done(function () { + this.passwordSet = true; + }.bind(this)).fail(function (error) { + api.alert('Set password failed', error); + }) + }, + + + config_wifi: function () { + this.wifiConfirm = false; + + if (!this.wifi_ssid.length) { + alert('SSID not set'); + return; + } + + if (32 < this.wifi_ssid.length) { + alert('SSID longer than 32 characters'); + return; + } + + if (this.wifi_pass.length && this.wifi_pass.length < 8) { + alert('WiFi password shorter than 8 characters'); + return; + } + + if (128 < this.wifi_pass.length) { + alert('WiFi password longer than 128 characters'); + return; + } + + this.rebooting = true; + + var config = { + mode: this.wifi_mode, + channel: this.wifi_ch, + ssid: this.wifi_ssid, + pass: this.wifi_pass + } + + api.put('wifi', config).fail(function (error) { + api.alert('Failed to configure WiFi', error); + this.rebooting = false; + }.bind(this)) + } + } +} diff --git a/src/js/api.js b/src/js/api.js new file mode 100644 index 0000000..8c07a02 --- /dev/null +++ b/src/js/api.js @@ -0,0 +1,104 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +function api_cb(method, url, data, config) { + config = $.extend({ + type: method, + url: '/api/' + url, + dataType: 'json', + cache: false + }, config); + + if (typeof data == 'object') { + config.data = JSON.stringify(data); + config.contentType = 'application/json; charset=utf-8'; + } + + var d = $.Deferred(); + + $.ajax(config).success(function (data, status, xhr) { + d.resolve(data, status, xhr); + + }).error(function (xhr, status, error) { + var text = xhr.responseText; + try {text = $.parseJSON(xhr.responseText)} catch(e) {} + if (!text) text = error; + + d.reject(text, xhr, status, error); + console.debug('API Error: ' + url + ': ' + text); + }); + + return d.promise(); +} + + +module.exports = { + get: function (url, config) { + return api_cb('GET', url, undefined, config); + }, + + + put: function(url, data, config) { + return api_cb('PUT', url, data, config); + }, + + + post: function(url, data, config) { + return api_cb('POST', url, data, config); + }, + + + upload: function(url, data, config) { + config = $.extend({ + processData: false, + contentType: false, + cache: false, + data: data + }, config); + + return api_cb('PUT', url, undefined, config); + }, + + + 'delete': function (url, config) { + return api_cb('DELETE', url, undefined, config); + }, + + + alert: function (msg, error) { + if (typeof error != 'undefined') { + if (typeof error.message != 'undefined') + msg += '\n' + error.message; + else msg += '\n' + JSON.stringify(error); + } + + alert(msg); + } +} diff --git a/src/js/app.js b/src/js/app.js new file mode 100644 index 0000000..9dc099a --- /dev/null +++ b/src/js/app.js @@ -0,0 +1,431 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + +var api = require('./api'); +var cookie = require('./cookie')('bbctrl-'); +var Sock = require('./sock'); +const { exec } = require('child_process'); + +function compare_versions(a, b) { + var reStripTrailingZeros = /(\.0+)+$/; + var segsA = a.replace(reStripTrailingZeros, '').split('.'); + var segsB = b.replace(reStripTrailingZeros, '').split('.'); + var l = Math.min(segsA.length, segsB.length); + + for (var i = 0; i < l; i++) { + var diff = parseInt(segsA[i], 10) - parseInt(segsB[i], 10); + if (diff) return diff; + } + + return segsA.length - segsB.length; +} + + +function is_object(o) {return o !== null && typeof o == 'object'} +function is_array(o) {return Array.isArray(o)} + + +function update_array(dst, src) { + while (dst.length) dst.pop() + for (var i = 0; i < src.length; i++) + Vue.set(dst, i, src[i]); +} + + +function update_object(dst, src, remove) { + var props, index, key, value; + + if (remove) { + props = Object.getOwnPropertyNames(dst); + + for (index in props) { + key = props[index]; + if (!src.hasOwnProperty(key)) + Vue.delete(dst, key); + } + } + + props = Object.getOwnPropertyNames(src); + for (index in props) { + key = props[index]; + value = src[key]; + + if (is_array(value) && dst.hasOwnProperty(key) && is_array(dst[key])) + update_array(dst[key], value); + + else if (is_object(value) && dst.hasOwnProperty(key) && is_object(dst[key])) + update_object(dst[key], value, remove); + + else Vue.set(dst, key, value); + } +} + +module.exports = new Vue({ + el: 'body', + + + data: function () { + return { + status: 'connecting', + currentView: 'loading', + index: -1, + modified: false, + template: require('../resources/config-template.json'), + config: { + settings: {units: 'METRIC'}, + motors: [{}, {}, {}, {}], + version: '' + }, + state: {messages: []}, + video_size: cookie.get('video-size', 'small'), + crosshair: cookie.get('crosshair', 'false') != 'false', + errorTimeout: 30, + errorTimeoutStart: 0, + errorShow: false, + errorMessage: '', + confirmUpgrade: false, + confirmUpload: false, + firmwareUpgrading: false, + checkedUpgrade: false, + firmwareName: '', + latestVersion: '', + password: '', + ipAddress: '0.0.0.0', + wifiSSID: '' + } + }, + + + components: { + 'estop': {template: '#estop-template'}, + 'loading-view': {template: '

Loading...

'}, + 'control-view': require('./control-view'), + 'settings-view': require('./settings-view'), + 'motor-view': require('./motor-view'), + 'tool-view': require('./tool-view'), + 'io-view': require('./io-view'), + 'admin-general-view': require('./admin-general-view'), + 'admin-network-view': require('./admin-network-view'), + 'help-view': {template: '#help-view-template'}, + 'cheat-sheet-view': { + template: '#cheat-sheet-view-template', + data: function () {return {showUnimplemented: false}} + } + }, + + + events: { + 'config-changed': function () {this.modified = true;}, + 'hostname-changed': function (hostname) {this.hostname = hostname}, + + + send: function (msg) { + if (this.status == 'connected') { + console.debug('>', msg); + this.sock.send(msg); + } + }, + + + connected: function () {this.update()}, + update: function () {this.update()}, + + + check: function () { + this.latestVersion = ''; + + $.ajax({ + type: 'GET', + url: 'https://https://raw.githubusercontent.com/OneFinityCNC/onefinity/master/latest.txt', + data: {hid: this.state.hid}, + cache: false + + }).done(function (data) { + this.latestVersion = data; + this.$broadcast('latest_version', data); + }.bind(this)) + }, + + + upgrade: function () { + this.password = ''; + this.confirmUpgrade = true; + }, + + + upload: function (firmware) { + this.firmware = firmware; + this.firmwareName = firmware.name; + this.password = ''; + this.confirmUpload = true; + }, + + + error: function (msg) { + // Honor user error blocking + if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000) + return; + + // Wait at least 1 sec to pop up repeated errors + if (1 < msg.repeat && Date.now() - msg.ts < 1000) return; + + // Popup error dialog + this.errorShow = true; + this.errorMessage = msg.msg; + } + }, + + + computed: { + popupMessages: function () { + var msgs = []; + + for (var i = 0; i < this.state.messages.length; i++) { + var text = this.state.messages[i].text; + if (!/^#/.test(text)) msgs.push(text); + } + + return msgs; + } + }, + + + ready: function () { + $(window).on('hashchange', this.parse_hash); + this.connect(); + }, + + + methods: { + metric: function () {return this.config.settings.units != 'IMPERIAL'}, + + + block_error_dialog: function () { + this.errorTimeoutStart = Date.now(); + this.errorShow = false; + }, + + + toggle_video: function (e) { + if (this.video_size == 'small') this.video_size = 'large'; + else if (this.video_size == 'large') this.video_size = 'small'; + cookie.set('video-size', this.video_size); + }, + + + toggle_crosshair: function (e) { + e.preventDefault(); + this.crosshair = !this.crosshair; + cookie.set('crosshair', this.crosshair); + }, + + + estop: function () { + if (this.state.xx == 'ESTOPPED') api.put('clear'); + else api.put('estop'); + }, + + + upgrade_confirmed: function () { + this.confirmUpgrade = false; + + api.put('upgrade', {password: this.password}).done(function () { + this.firmwareUpgrading = true; + + }.bind(this)).fail(function () { + api.alert('Invalid password'); + }.bind(this)) + }, + + + upload_confirmed: function () { + this.confirmUpload = false; + + var form = new FormData(); + form.append('firmware', this.firmware); + if (this.password) form.append('password', this.password); + + $.ajax({ + url: '/api/firmware/update', + type: 'PUT', + data: form, + cache: false, + contentType: false, + processData: false + + }).success(function () { + this.firmwareUpgrading = true; + + }.bind(this)).error(function () { + api.alert('Invalid password or bad firmware'); + }.bind(this)) + }, + + + show_upgrade: function () { + if (!this.latestVersion) return false; + return compare_versions(this.config.version, this.latestVersion) < 0; + }, + + update: function () { + api.get('config/load').done(function (config) { + update_object(this.config, config, true); + this.parse_hash(); + + if (!this.checkedUpgrade) { + this.checkedUpgrade = true; + + var check = this.config.admin['auto-check-upgrade']; + if (typeof check == 'undefined' || check) + this.$emit('check'); + } + + this.check_ip_address(); + this.check_ssid(); + + }.bind(this)) + }, + + check_ip_address : function() { + $.ajax({ + type: 'GET', + url: 'hostinfo.txt', + data: {hid: this.state.hid}, + cache: false + + }).done(function (data) { + console.debug('>', data); + this.ipAddress = data; + this.$broadcast('ipAddress', data); + }.bind(this)) + }, + + check_ssid : function() { + $.ajax({ + type: 'GET', + url: 'ssidinfo.txt', + data: {hid: this.state.hid}, + cache: false + + }).done(function (data) { + console.debug('>', data); + this.wifiSSID = data; + this.$broadcast('wifiSSID', data); + }.bind(this)) + }, + + get_ip_address : function() { + console.debug('get_ip>', this.ipAddress); + return this.ipAddress; + }, + + get_ssid : function() { + console.debug('get_ssid>', this.wifiSSID); + return this.wifiSSID; + }, + + connect: function () { + this.sock = new Sock('//' + window.location.host + '/sockjs'); + + this.sock.onmessage = function (e) { + if (typeof e.data != 'object') return; + + if ('log' in e.data) { + this.$broadcast('log', e.data.log); + delete e.data.log; + } + + // Check for session ID change on controller + if ('sid' in e.data) { + if (typeof this.sid == 'undefined') this.sid = e.data.sid; + + else if (this.sid != e.data.sid) { + if (typeof this.hostname != 'undefined' && + String(location.hostname) != 'localhost') + location.hostname = this.hostname; + location.reload(true); + } + } + + update_object(this.state, e.data, false); + this.$broadcast('update'); + + }.bind(this) + + this.sock.onopen = function (e) { + this.status = 'connected'; + this.$emit(this.status); + this.$broadcast(this.status); + }.bind(this) + + this.sock.onclose = function (e) { + this.status = 'disconnected'; + this.$emit(this.status); + this.$broadcast(this.status); + }.bind(this) + }, + + + parse_hash: function () { + var hash = location.hash.substr(1); + + if (!hash.trim().length) { + location.hash = 'control'; + return; + } + + var parts = hash.split(':'); + + if (parts.length == 2) this.index = parts[1]; + + this.currentView = parts[0]; + }, + + + save: function () { + api.put('config/save', this.config).done(function (data) { + this.modified = false; + }.bind(this)).fail(function (error) { + api.alert('Save failed', error); + }); + }, + + + close_messages: function (action) { + if (action == 'stop') api.put('stop'); + if (action == 'continue') api.put('unpause'); + + // Acknowledge messages + if (this.state.messages.length) { + var id = this.state.messages.slice(-1)[0].id + api.put('message/' + id + '/ack'); + } + } + } +}) diff --git a/src/js/axis-control.js b/src/js/axis-control.js new file mode 100644 index 0000000..c6fa5c3 --- /dev/null +++ b/src/js/axis-control.js @@ -0,0 +1,65 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +module.exports = { + template: '#axis-control-template', + props: ['axes', 'colors', 'enabled', 'adjust', 'step'], + + + methods: { + jog: function (axis, ring, direction) { + var value = direction * this.value(ring); + this.$dispatch(this.step ? 'step' : 'jog', this.axes[axis], value); + }, + + back2zero: function(axis0,axis1) { + this.$dispatch('back2zero',this.axes[axis0],this.axes[axis1]) + }, + + + release: function (axis) { + if (!this.step) this.$dispatch('jog', this.axes[axis], 0) + }, + + + value: function (ring) { + var adjust = [0.01, 0.1, 1][this.adjust]; + if (this.step) return adjust * [0.1, 1, 10, 100][ring]; + return adjust * [0.1, 0.25, 0.5, 1][ring]; + }, + + + text: function (ring) { + var value = this.value(ring) * (this.step ? 1 : 100); + value = parseFloat(value.toFixed(3)); + return value + (this.step ? '' : '%'); + } + } +} diff --git a/src/js/axis-vars.js b/src/js/axis-vars.js new file mode 100644 index 0000000..bf2096b --- /dev/null +++ b/src/js/axis-vars.js @@ -0,0 +1,203 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +function is_defined(x) {return typeof x != 'undefined'} + + +module.exports = { + props: ['state', 'config'], + + + computed: { + x: function () {return this._compute_axis('x')}, + y: function () {return this._compute_axis('y')}, + z: function () {return this._compute_axis('z')}, + a: function () {return this._compute_axis('a')}, + b: function () {return this._compute_axis('b')}, + c: function () {return this._compute_axis('c')}, + axes: function () {return this._compute_axes()} + }, + + + methods: { + _convert_length: function (value) { + return this.state.imperial ? value / 25.4 : value; + }, + + + _length_str: function (value) { + return this._convert_length(value).toLocaleString() + + (this.state.imperial ? ' in' : ' mm'); + }, + + + _compute_axis: function (axis) { + var abs = this.state[axis + 'p'] || 0; + var off = this.state['offset_' + axis]; + var motor_id = this._get_motor_id(axis); + var motor = motor_id == -1 ? {} : this.config.motors[motor_id]; + var enabled = typeof motor.enabled != 'undefined' && motor.enabled; + var homingMode = motor['homing-mode'] + var homed = this.state[motor_id + 'homed']; + var min = this.state[motor_id + 'tn']; + var max = this.state[motor_id + 'tm']; + var dim = max - min; + var pathMin = this.state['path_min_' + axis]; + var pathMax = this.state['path_max_' + axis]; + var pathDim = pathMax - pathMin; + var under = pathMin + off < min; + var over = max < pathMax + off; + var klass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis; + var state = 'UNHOMED'; + var icon = 'question-circle'; + var fault = this.state[motor_id + 'df'] & 0x1f; + var shutdown = this.state.power_shutdown; + var title; + + if (fault || shutdown) { + state = shutdown ? 'SHUTDOWN' : 'FAULT'; + klass += ' error'; + icon = 'exclamation-circle'; + + } else if (0 < dim && dim < pathDim) { + state = 'NO FIT'; + klass += ' error'; + icon = 'ban'; + + } else if (homed) { + state = 'HOMED' + icon = 'check-circle'; + + if (over || under) { + state = over ? 'OVER' : 'UNDER'; + klass += ' warn'; + icon = 'exclamation-circle'; + } + } + + switch (state) { + case 'UNHOMED': title = 'Click the home button to home axis.'; break; + case 'HOMED': title = 'Axis successfuly homed.'; break; + + case 'OVER': + title = 'Tool path would move ' + + this._length_str(pathMax + off - max) + ' beyond axis bounds.'; + break; + + case 'UNDER': + title = 'Tool path would move ' + + this._length_str(min - pathMin - off) + ' below axis bounds.'; + break; + + case 'NO FIT': + title = 'Tool path dimensions exceed axis dimensions by ' + + this._length_str(pathDim - dim) + '.'; + break; + + case 'FAULT': + title = 'Motor driver fault. A potentially damaging electrical ' + + 'condition was detected and the motor driver was shutdown. ' + + 'Please power down the controller and check your motor cabling. ' + + 'See the "Motor Faults" table on the "Indicators" tab for more ' + + 'information.'; + break; + + case 'SHUTDOWN': + title = 'Motor power fault. All motors in shutdown. ' + + 'See the "Power Faults" table on the "Indicators" tab for more ' + + 'information. Reboot controller to reset.'; + } + + return { + pos: abs - off, + abs: abs, + off: off, + min: min, + max: max, + dim: dim, + pathMin: pathMin, + pathMax: pathMax, + pathDim: pathDim, + motor: motor_id, + enabled: enabled, + homingMode: homingMode, + homed: homed, + klass: klass, + state: state, + icon: icon, + title: title + } + }, + + + _get_motor_id: function (axis) { + for (var i = 0; i < this.config.motors.length; i++) { + var motor = this.config.motors[i]; + if (motor.axis.toLowerCase() == axis) return i; + } + + return -1; + }, + + + _compute_axes: function () { + var homed = false; + + for (var name of 'xyzabc') { + var axis = this[name]; + + if (!axis.enabled) continue + if (!axis.homed) {homed = false; break} + homed = true; + } + + var error = false; + var warn = false; + + if (homed) + for (name of 'xyzabc') { + axis = this[name]; + + if (!axis.enabled) continue; + if (axis.klass.indexOf('error') != -1) error = true; + if (axis.klass.indexOf('warn') != -1) warn = true; + } + + var klass = homed ? 'homed' : 'unhomed'; + if (error) klass += ' error'; + else if (warn) klass += ' warn'; + + return { + homed: homed, + klass: klass + } + } + } +} diff --git a/src/js/console.js b/src/js/console.js new file mode 100644 index 0000000..5607bba --- /dev/null +++ b/src/js/console.js @@ -0,0 +1,87 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +function _msg_equal(a, b) { + return a.level == b.level && a.source == b.source && a.where == b.where && + a.msg == b.msg; +} + + +// Shared among all instances +var messages = []; + + +module.exports = { + template: '#console-template', + + + data: function () { + return {messages: messages} + }, + + + events: { + log: function (msg) { + // There may be multiple instances of this module so ignore messages + // that have already been processed. + if (msg.logged) return; + msg.logged = true; + + // Make sure we have a message level + msg.level = msg.level || 'info'; + + // Add to message log and count and collapse repeats + var repeat = messages.length && _msg_equal(msg, messages[0]); + if (repeat) messages[0].repeat++; + else { + msg.repeat = msg.repeat || 1; + messages.unshift(msg); + while (256 < messages.length) messages.pop(); + } + msg.ts = Date.now(); + + // Write message to browser console for debugging + var text = JSON.stringify(msg); + if (msg.level == 'error' || msg.level == 'critical') console.error(text); + else if (msg.level == 'warning') console.warn(text); + else if (msg.level == 'debug' && console.debug) console.debug(text); + else console.log(text); + + // Event on errors + if (msg.level == 'error' || msg.level == 'critical') + this.$dispatch('error', msg); + } + }, + + + methods: { + clear: function () {messages.splice(0, messages.length);}, + } +} diff --git a/src/js/control-view.js b/src/js/control-view.js new file mode 100644 index 0000000..0536ebf --- /dev/null +++ b/src/js/control-view.js @@ -0,0 +1,667 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + +var api = require('./api'); +var cookie = require('./cookie')('bbctrl-'); + + +function _is_array(x) { + return Object.prototype.toString.call(x) === '[object Array]'; +} + + +function escapeHTML(s) { + var entityMap = {'&': '&', '<': '<', '>': '>'}; + return String(s).replace(/[&<>]/g, function (s) {return entityMap[s];}); +} + + +module.exports = { + template: '#control-view-template', + props: ['config', 'template', 'state'], + + + data: function () { + return { + mach_units: 'METRIC', + mdi: '', + last_file: undefined, + last_file_time: undefined, + toolpath: {}, + toolpath_progress: 0, + axes: 'xyzabc', + history: [], + speed_override: 1, + feed_override: 1, + manual_home: {x: false, y: false, z: false, a: false, b: false, c: false}, + position_msg: + {x: false, y: false, z: false, a: false, b: false, c: false}, + axis_position: 0, + jog_step: cookie.get_bool('jog-step'), + jog_adjust: parseInt(cookie.get('jog-adjust', 2)), + deleteGCode: false, + tab: 'auto', + jog_incr: 1.0, + tool_msg: false, + tool_diameter: 6.35 + } + }, + + + components: { + 'axis-control': require('./axis-control'), + 'path-viewer': require('./path-viewer'), + 'gcode-viewer': require('./gcode-viewer') + }, + + + watch: { + 'state.imperial': { + handler: function (imperial) { + this.mach_units = imperial ? 'IMPERIAL' : 'METRIC'; + }, + immediate: true + }, + + + mach_units: function (units) { + if ((units == 'METRIC') != this.metric) + this.send(units == 'METRIC' ? 'G21' : 'G20'); + + this.units_changed(); + }, + + + 'state.line': function () { + if (this.mach_state != 'HOMING') + this.$broadcast('gcode-line', this.state.line); + }, + + + 'state.selected_time': function () {this.load()}, + jog_step: function () {cookie.set_bool('jog-step', this.jog_step)}, + jog_adjust: function () {cookie.set('jog-adjust', this.jog_adjust)} + }, + + + computed: { + metric: function () {return !this.state.imperial}, + + + mach_state: function () { + var cycle = this.state.cycle; + var state = this.state.xx; + + if (typeof cycle != 'undefined' && state != 'ESTOPPED' && + (cycle == 'jogging' || cycle == 'homing')) + return cycle.toUpperCase(); + return state || '' + }, + + + pause_reason: function () {return this.state.pr}, + + + is_running: function () { + return this.mach_state == 'RUNNING' || this.mach_state == 'HOMING'; + }, + + + is_stopping: function () {return this.mach_state == 'STOPPING'}, + is_holding: function () {return this.mach_state == 'HOLDING'}, + is_ready: function () {return this.mach_state == 'READY'}, + is_idle: function () {return this.state.cycle == 'idle'}, + + + is_paused: function () { + return this.is_holding && + (this.pause_reason == 'User pause' || + this.pause_reason == 'Program pause') + }, + + + can_mdi: function () {return this.is_idle || this.state.cycle == 'mdi'}, + + + can_set_axis: function () { + return this.is_idle + // TODO allow setting axis position during pause + return this.is_idle || this.is_paused + }, + + + message: function () { + if (this.mach_state == 'ESTOPPED') return this.state.er; + if (this.mach_state == 'HOLDING') return this.state.pr; + if (this.state.messages.length) + return this.state.messages.slice(-1)[0].text; + return ''; + }, + + + highlight_state: function () { + return this.mach_state == 'ESTOPPED' || this.mach_state == 'HOLDING'; + }, + + + plan_time: function () {return this.state.plan_time}, + + + plan_time_remaining: function () { + if (!(this.is_stopping || this.is_running || this.is_holding)) return 0; + return this.toolpath.time - this.plan_time + }, + + + eta: function () { + if (this.mach_state != 'RUNNING') return ''; + var remaining = this.plan_time_remaining; + var d = new Date(); + d.setSeconds(d.getSeconds() + remaining); + return d.toLocaleString(); + }, + + + progress: function () { + if (!this.toolpath.time || this.is_ready) return 0; + var p = this.plan_time / this.toolpath.time; + return p < 1 ? p : 1; + } + }, + + + events: { + jog: function (axis, power) { + var data = {ts: new Date().getTime()}; + data[axis] = power; + api.put('jog', data); + }, + + back2zero: function(axis0,axis1) { + this.send("G0"+axis0+"0"+axis1+"0"); + }, + + step: function (axis, value) { + this.send('M70\nG91\nG0' + axis + value + '\nM72'); + } + }, + + + ready: function () {this.load()}, + + + methods: { + + units_changed : function() { + console.log("Units changed!"); + + if(this.mach_units == 'METRIC') { + document.getElementById("jog_button_fine").innerHTML = "0.1"; + document.getElementById("jog_button_small").innerHTML = "1.0"; + document.getElementById("jog_button_medium").innerHTML = "10"; + document.getElementById("jog_button_large").innerHTML = "100"; + this.tool_diameter = this.tool_diameter * 25.4; + this.tool_diameter = this.tool_diameter.toFixed(3); + } else { + document.getElementById("jog_button_fine").innerHTML = "0.005"; + document.getElementById("jog_button_small").innerHTML = "0.05"; + document.getElementById("jog_button_medium").innerHTML = "0.5"; + document.getElementById("jog_button_large").innerHTML = "5"; + this.tool_diameter = this.tool_diameter / 25.4; + this.tool_diameter = this.tool_diameter.toFixed(3); + } + + this.set_jog_incr('small'); + + }, + + set_tool_diameter : function (new_diameter) { + + if(isNaN(new_diameter)) + return; + + this.tool_msg = false; + + this.tool_diameter = parseFloat(new_diameter); + + this.probe_xyz(); + + }, + + set_jog_incr: function(newValue) { + //this.jog_incr = newValue; + + document.getElementById("jog_button_fine").style.fontWeight = 'normal'; + document.getElementById("jog_button_small").style.fontWeight = 'normal'; + document.getElementById("jog_button_medium").style.fontWeight = 'normal'; + document.getElementById("jog_button_large").style.fontWeight = 'normal'; + + if(newValue == 'fine') + { + document.getElementById("jog_button_fine").style.fontWeight = 'bold'; + if(this.mach_units == 'METRIC') + this.jog_incr = 0.1; + else + this.jog_incr = 0.005; + } else if(newValue == 'small') { + document.getElementById("jog_button_small").style.fontWeight = 'bold'; + if(this.mach_units == 'METRIC') + this.jog_incr = 1.0; + else + this.jog_incr = 0.05; + } else if(newValue == 'medium') { + document.getElementById("jog_button_medium").style.fontWeight = 'bold'; + if(this.mach_units == 'METRIC') + this.jog_incr = 10; + else + this.jog_incr = 0.5; + } else if(newValue == 'large') { + document.getElementById("jog_button_large").style.fontWeight = 'bold'; + if(this.mach_units == 'METRIC') + this.jog_incr = 100; + else + this.jog_incr = 5; + } + + + + }, + + goto_zero(zero_x,zero_y,zero_z,zero_a) { + var xcmd = ""; + var ycmd = ""; + var zcmd = ""; + var acmd = ""; + if(zero_x) xcmd = "X0"; + if(zero_y) ycmd = "Y0"; + if(zero_z) zcmd = "Z0"; + if(zero_a) acmd = "A0"; + + this.send('M70\nG90\nG0' + xcmd + ycmd + zcmd + acmd + '\nM72'); + }, + + probe_xyz() { + var pcmd = ""; + var xoffset = this.config.probe["probe-xdim"]; + var yoffset = this.config.probe["probe-ydim"]; + var zoffset = this.config.probe["probe-zdim"]; + var fastSeek = this.config.probe["probe-fast-seek"]; + var slowSeek = this.config.probe["probe-slow-seek"]; + debugger; + + if(this.mach_units == "METRIC") { + + fastSeek = "F" + fastSeek; + slowSeek = "F" + slowSeek; + + //Metric Probing + pcmd += "G92 X0\n"; + pcmd += "G92 Y0\n"; + pcmd += "G92 Z0\n"; + pcmd += "G21\n"; + pcmd += "G38.2 Z-25.4 " + fastSeek + "\n"; + pcmd += "G91 G0 Z1.5\n"; + pcmd += "G38.2 Z-2.5 " + slowSeek + "\n"; + + //var zoffset = 16.383; + pcmd += "G92 Z " + zoffset + "\n"; + + pcmd += "G91 G0 Z 3.175\n"; + pcmd += "G91 G0 X 19.05\n"; + pcmd += "G91 G0 Z -12.7\n"; + pcmd += "G38.2 X -19.05 " + fastSeek + "\n"; + pcmd += "G91 G1 X 1.27 " + fastSeek +"\n"; + pcmd += "G38.2 X -4 " + slowSeek + "\n"; + + xoffset += this.tool_diameter/2.0; + xoffset = xoffset.toFixed(5); + pcmd += "G92 X " + xoffset + "\n"; + + pcmd += "G91 G0 X 2.5\n"; + pcmd += "G91 G0 Y 17\n"; + pcmd += "G91 G0 X -13\n"; + pcmd += "G38.2 Y -17 " + fastSeek + "\n"; + pcmd += "G91 G0 Y 1.27\n"; + pcmd += "G38.2 Y -4 " + slowSeek +"\n"; + + yoffset += this.tool_diameter/2.0; + yoffset = yoffset.toFixed(5); + pcmd += "G92 Y " + yoffset + "\n"; + + pcmd += "G91 G0 Y2.54\n"; + pcmd += "G91 G0 Z 25.4\n"; + pcmd += "G90 G0 X0 Y0\n"; + } else { + + //Imperial Probing + + xoffset = xoffset / 25.4; + yoffset = yoffset / 25.4; + zoffset = zoffset / 25.4; + slowSeek = slowSeek / 25.4; + slowSeek = slowSeek.toFixed(5); + slowSeek = "F" + slowSeek; + fastSeek = fastSeek / 25.4; + fastSeek = fastSeek.toFixed(5); + fastSeek = "F" + fastSeek; + + pcmd += "G92 X0\n"; + pcmd += "G92 Y0\n"; + pcmd += "G92 Z0\n"; + pcmd += "G20\n"; + pcmd += "G38.2 Z-1.0 " + fastSeek + "\n"; + pcmd += "G91 G0 Z0.06\n"; + pcmd += "G38.2 Z-0.1 " + slowSeek + "\n"; + + //var zoffset = 0.645; + zoffset = zoffset.toFixed(5); + pcmd += "G92 Z " + zoffset + "\n"; + + + pcmd += "G91 G0 Z 0.125\n"; + pcmd += "G91 G0 X 0.75\n"; + pcmd += "G91 G0 Z -0.5\n"; + pcmd += "G38.2 X -0.75 " + fastSeek + "\n"; + pcmd += "G91 G1 X 0.05 " + fastSeek + "\n"; + pcmd += "G38.2 X -0.15 " + slowSeek + "\n"; + + xoffset += this.tool_diameter/2.0; + xoffset = xoffset.toFixed(5); + pcmd += "G92 X " + xoffset + "\n"; + + pcmd += "G91 G0 X 0.1\n"; + pcmd += "G91 G0 Y 0.75\n"; + pcmd += "G91 G0 X -0.5\n"; + pcmd += "G38.2 Y -0.75 " + fastSeek + "\n"; + pcmd += "G91 G0 Y 0.05\n"; + pcmd += "G38.2 Y -0.15 " + slowSeek +"\n"; + + yoffset += this.tool_diameter/2.0; + yoffset = yoffset.toFixed(5); + pcmd += "G92 Y " + yoffset + "\n"; + + pcmd += "G91 G0 Y0.1\n"; + pcmd += "G91 G0 Z1\n"; + pcmd += "G90 G0 X0 Y0\n"; + } + + this.send(pcmd); + + }, + + probe_z() { + var pcmd = ""; + var fastSeek = this.config.probe["probe-fast-seek"]; + var slowSeek = this.config.probe["probe-slow-seek"]; + var zoffset = this.config.probe["probe-zdim"]; + + debugger; + + if(this.mach_units == "METRIC") { + fastSeek = "F" + fastSeek; + slowSeek = "F" + slowSeek; + + + pcmd += "G92 Z0\n"; + pcmd += "G21\n"; + pcmd += "G38.2 Z-25 " + fastSeek + "\n"; + pcmd += "G91 G0 Z1.5\n"; + pcmd += "G38.2 Z-2.5 " + slowSeek + "\n"; + pcmd += "G92 Z " + zoffset + "\n"; + pcmd += "G91 G0 Z3\n"; + + + } else { + zoffset = zoffset / 25.4; + slowSeek = slowSeek / 25.4; + slowSeek = slowSeek.toFixed(5); + slowSeek = "F" + slowSeek; + fastSeek = fastSeek / 25.4; + fastSeek = fastSeek.toFixed(5); + fastSeek = "F" + fastSeek; + + pcmd += "G92 Z0\n"; + pcmd += "G20\n"; + pcmd += "G38.2 Z-1.0 " + fastSeek +"\n"; + pcmd += "G91 G0 Z0.06\n"; + pcmd += "G38.2 Z-0.1 " + slowSeek + "\n"; + zoffset = zoffset.toFixed(5); + pcmd += "G92 Z " + zoffset + "\n"; + pcmd += "G91 G0 Z0.125\n"; + + } + + this.send(pcmd); + + }, + + jog_fn: function (x_jog,y_jog,z_jog,a_jog) { + var xcmd = "X" + x_jog * this.jog_incr; + var ycmd = "Y" + y_jog * this.jog_incr; + var zcmd = "Z" + z_jog * this.jog_incr; + var acmd = "A" + a_jog * this.jog_incr; + + console.log("Jog command: " + this.jog_incr); + //debugger; + + this.send('M70\nG91\nG0' + xcmd + ycmd + zcmd + acmd + '\nM72'); + }, + + send: function (msg) {this.$dispatch('send', msg)}, + + + load: function () { + var file_time = this.state.selected_time; + var file = this.state.selected; + if (this.last_file == file && this.last_file_time == file_time) return; + this.last_file = file; + this.last_file_time = file_time; + + this.$broadcast('gcode-load', file); + this.$broadcast('gcode-line', this.state.line); + this.toolpath_progress = 0; + this.load_toolpath(file, file_time); + }, + + + load_toolpath: function (file, file_time) { + this.toolpath = {}; + + if (!file) return; + + api.get('path/' + file).done(function (toolpath) { + if (this.last_file_time != file_time) return; + + if (typeof toolpath.progress == 'undefined') { + toolpath.filename = file; + this.toolpath_progress = 1; + this.toolpath = toolpath; + + var state = this.$root.state; + var bounds = toolpath.bounds; + for (var axis of 'xyzabc') { + Vue.set(state, 'path_min_' + axis, bounds.min[axis]); + Vue.set(state, 'path_max_' + axis, bounds.max[axis]); + } + + } else { + this.toolpath_progress = toolpath.progress; + this.load_toolpath(file, file_time); // Try again + } + }.bind(this)); + }, + + + submit_mdi: function () { + this.send(this.mdi); + if (!this.history.length || this.history[0] != this.mdi) + this.history.unshift(this.mdi); + this.mdi = ''; + }, + + + mdi_start_pause: function () { + if (this.state.xx == 'RUNNING') this.pause(); + + else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING') + this.unpause(); + + else this.submit_mdi(); + }, + + + load_history: function (index) {this.mdi = this.history[index];}, + + + open: function (e) { + // If we don't reset the form the browser may cache file if name is same + // even if contents have changed + $('.gcode-file-input')[0].reset(); + $('.gcode-file-input input').click(); + }, + + + upload: function (e) { + var files = e.target.files || e.dataTransfer.files; + if (!files.length) return; + + var file = files[0]; + var fd = new FormData(); + + fd.append('gcode', file); + + api.upload('file', fd) + .done(function () { + this.last_file_time = undefined; // Force reload + this.$broadcast('gcode-reload', file.name); + + }.bind(this)).fail(function (error) { + api.alert('Upload failed', error) + }.bind(this)); + }, + + + delete_current: function () { + if (this.state.selected) + api.delete('file/' + this.state.selected); + this.deleteGCode = false; + }, + + + delete_all: function () { + api.delete('file'); + this.deleteGCode = false; + }, + + + home: function (axis) { + if (typeof axis == 'undefined') api.put('home'); + + else { + if (this[axis].homingMode != 'manual') api.put('home/' + axis); + else this.manual_home[axis] = true; + } + }, + + + set_home: function (axis, position) { + this.manual_home[axis] = false; + api.put('home/' + axis + '/set', {position: parseFloat(position)}); + }, + + + unhome: function (axis) { + this.position_msg[axis] = false; + api.put('home/' + axis + '/clear'); + }, + + + show_set_position: function (axis) { + this.axis_position = 0; + this.position_msg[axis] = true; + }, + + + set_position: function (axis, position) { + this.position_msg[axis] = false; + api.put('position/' + axis, {'position': parseFloat(position)}); + }, + + + zero_all: function () { + for (var axis of 'xyzabc') + if (this[axis].enabled) this.zero(axis); + }, + + + zero: function (axis) { + if (typeof axis == 'undefined') this.zero_all(); + else this.set_position(axis, 0); + }, + + + start_pause: function () { + if (this.state.xx == 'RUNNING') this.pause(); + + else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING') + this.unpause(); + + else this.start(); + }, + + + start: function () {api.put('start')}, + pause: function () {api.put('pause')}, + unpause: function () {api.put('unpause')}, + optional_pause: function () {api.put('pause/optional')}, + stop: function () {api.put('stop')}, + step: function () {api.put('step')}, + + + override_feed: function () {api.put('override/feed/' + this.feed_override)}, + + + override_speed: function () { + api.put('override/speed/' + this.speed_override) + }, + + + current: function (axis, value) { + var x = value / 32.0; + if (this.state[axis + 'pl'] == x) return; + + var data = {}; + data[axis + 'pl'] = x; + this.send(JSON.stringify(data)); + } + }, + + + mixins: [require('./axis-vars')] +} diff --git a/src/js/cookie.js b/src/js/cookie.js new file mode 100644 index 0000000..6244436 --- /dev/null +++ b/src/js/cookie.js @@ -0,0 +1,69 @@ +/******************************************************************************\ + + Copyright 2018. Buildbotics LLC + All Rights Reserved. + + For information regarding this software email: + Joseph Coffland + joseph@buildbotics.com + + This software is free software: you clan redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 2.1 of + the License, or (at your option) any later version. + + This software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the C! library. If not, see + . + +\******************************************************************************/ + +'use strict' + + +module.exports = function (prefix) { + if (typeof prefix == 'undefined') prefix = ''; + + var cookie = { + get: function (name, defaultValue) { + var decodedCookie = decodeURIComponent(document.cookie); + var ca = decodedCookie.split(';'); + name = prefix + name + '='; + + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1); + if (!c.indexOf(name)) return c.substring(name.length, c.length); + } + + return defaultValue; + }, + + + set: function (name, value, days) { + var offset = 2147483647; // Max value + if (typeof days != 'undefined') offset = days * 24 * 60 * 60 * 1000; + var d = new Date(); + d.setTime(d.getTime() + offset); + var expires = 'expires=' + d.toUTCString(); + document.cookie = prefix + name + '=' + value + ';' + expires + ';path=/'; + }, + + + set_bool: function (name, value) { + cookie.set(name, value ? 'true' : 'false'); + }, + + + get_bool: function (name, defaultValue) { + return cookie.get(name, defaultValue ? 'true' : 'false') == 'true'; + } + } + + return cookie; +} diff --git a/src/js/gcode-viewer.js b/src/js/gcode-viewer.js new file mode 100644 index 0000000..658382c --- /dev/null +++ b/src/js/gcode-viewer.js @@ -0,0 +1,172 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + +var api = require('./api'); + + +var entityMap = { + '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', + '/': '/', '`': '`', '=': '='} + + +function escapeHTML(s) { + return s.replace(/[&<>"'`=\/]/g, function (c) {return entityMap[c]}) +} + + +module.exports = { + template: '#gcode-viewer-template', + + + data: function () { + return { + empty: true, + file: '', + line: -1, + scrolling: false + } + }, + + + events: { + 'gcode-load': function (file) {this.load(file)}, + 'gcode-clear': function () {this.clear()}, + 'gcode-reload': function (file) {this.reload(file)}, + 'gcode-line': function (line) {this.update_line(line)} + }, + + + ready: function () { + this.clusterize = new Clusterize({ + rows: [], + scrollElem: $(this.$el).find('.clusterize-scroll')[0], + contentElem: $(this.$el).find('.clusterize-content')[0], + no_data_text: 'GCode view...', + callbacks: {clusterChanged: this.highlight} + }); + }, + + + attached: function () { + if (typeof this.clusterize != 'undefined') + this.clusterize.refresh(true); + }, + + + methods: { + load: function (file) { + if (file == this.file) return; + this.clear(); + this.file = file; + + if (!file) return; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', '/api/file/' + file + '?' + Math.random(), true); + xhr.responseType = 'text'; + + xhr.onload = function (e) { + if (this.file != file) return; + var lines = escapeHTML(xhr.response.trimRight()).split(/\r?\n/); + + for (var i = 0; i < lines.length; i++) { + lines[i] = '
  • ' + + '' + (i + 1) + '' + lines[i] + '
  • '; + } + + this.clusterize.update(lines); + this.empty = false; + + Vue.nextTick(this.update_line); + }.bind(this) + + xhr.send(); + }, + + + clear: function () { + this.empty = true; + this.file = ''; + this.line = -1; + this.clusterize.clear(); + }, + + + reload: function (file) { + if (file != this.file) return; + this.clear(); + this.load(file); + }, + + + highlight: function () { + var e = $(this.$el).find('.highlight'); + if (e.length) e.removeClass('highlight'); + + e = $(this.$el).find('.ln' + this.line); + if (e.length) e.addClass('highlight'); + }, + + + update_line: function(line) { + if (typeof line != 'undefined') { + if (this.line == line) return; + this.line = line; + + } else line = this.line; + + var totalLines = this.clusterize.getRowsAmount(); + + if (line <= 0) line = 1; + if (totalLines < line) line = totalLines; + + var e = $(this.$el).find('.clusterize-scroll'); + + var lineHeight = e[0].scrollHeight / totalLines; + var linesPerPage = Math.floor(e[0].clientHeight / lineHeight); + var current = e[0].scrollTop / lineHeight; + var target = line - 1 - Math.floor(linesPerPage / 2); + + // Update scroll position + if (!this.scrolling) { + if (target < current - 20 || current + 20 < target) + e[0].scrollTop = target * lineHeight; + + else { + this.scrolling = true; + e.animate({scrollTop: target * lineHeight}, { + complete: function () {this.scrolling = false}.bind(this) + }) + } + } + + Vue.nextTick(this.highlight); + } + } +} diff --git a/src/js/helvetiker_regular.typeface.json b/src/js/helvetiker_regular.typeface.json new file mode 100644 index 0000000..d19293b --- /dev/null +++ b/src/js/helvetiker_regular.typeface.json @@ -0,0 +1 @@ +{"glyphs":{"ο":{"x_min":0,"x_max":712,"ha":815,"o":"m 356 -25 q 96 88 192 -25 q 0 368 0 201 q 92 642 0 533 q 356 761 192 761 q 617 644 517 761 q 712 368 712 533 q 619 91 712 201 q 356 -25 520 -25 m 356 85 q 527 175 465 85 q 583 369 583 255 q 528 562 583 484 q 356 651 466 651 q 189 560 250 651 q 135 369 135 481 q 187 177 135 257 q 356 85 250 85 "},"S":{"x_min":0,"x_max":788,"ha":890,"o":"m 788 291 q 662 54 788 144 q 397 -26 550 -26 q 116 68 226 -26 q 0 337 0 168 l 131 337 q 200 152 131 220 q 384 85 269 85 q 557 129 479 85 q 650 270 650 183 q 490 429 650 379 q 194 513 341 470 q 33 739 33 584 q 142 964 33 881 q 388 1041 242 1041 q 644 957 543 1041 q 756 716 756 867 l 625 716 q 561 874 625 816 q 395 933 497 933 q 243 891 309 933 q 164 759 164 841 q 325 609 164 656 q 625 526 475 568 q 788 291 788 454 "},"¦":{"x_min":343,"x_max":449,"ha":792,"o":"m 449 462 l 343 462 l 343 986 l 449 986 l 449 462 m 449 -242 l 343 -242 l 343 280 l 449 280 l 449 -242 "},"/":{"x_min":183.25,"x_max":608.328125,"ha":792,"o":"m 608 1041 l 266 -129 l 183 -129 l 520 1041 l 608 1041 "},"Τ":{"x_min":-0.4375,"x_max":777.453125,"ha":839,"o":"m 777 893 l 458 893 l 458 0 l 319 0 l 319 892 l 0 892 l 0 1013 l 777 1013 l 777 893 "},"y":{"x_min":0,"x_max":684.78125,"ha":771,"o":"m 684 738 l 388 -83 q 311 -216 356 -167 q 173 -279 252 -279 q 97 -266 133 -279 l 97 -149 q 132 -155 109 -151 q 168 -160 155 -160 q 240 -114 213 -160 q 274 -26 248 -98 l 0 738 l 137 737 l 341 139 l 548 737 l 684 738 "},"Π":{"x_min":0,"x_max":803,"ha":917,"o":"m 803 0 l 667 0 l 667 886 l 140 886 l 140 0 l 0 0 l 0 1012 l 803 1012 l 803 0 "},"ΐ":{"x_min":-111,"x_max":339,"ha":361,"o":"m 339 800 l 229 800 l 229 925 l 339 925 l 339 800 m -1 800 l -111 800 l -111 925 l -1 925 l -1 800 m 284 3 q 233 -10 258 -5 q 182 -15 207 -15 q 85 26 119 -15 q 42 200 42 79 l 42 737 l 167 737 l 168 215 q 172 141 168 157 q 226 101 183 101 q 248 103 239 101 q 284 112 257 104 l 284 3 m 302 1040 l 113 819 l 30 819 l 165 1040 l 302 1040 "},"g":{"x_min":0,"x_max":686,"ha":838,"o":"m 686 34 q 586 -213 686 -121 q 331 -306 487 -306 q 131 -252 216 -306 q 31 -84 31 -190 l 155 -84 q 228 -174 166 -138 q 345 -207 284 -207 q 514 -109 454 -207 q 564 89 564 -27 q 461 6 521 36 q 335 -23 401 -23 q 88 100 184 -23 q 0 370 0 215 q 87 634 0 522 q 330 758 183 758 q 457 728 398 758 q 564 644 515 699 l 564 737 l 686 737 l 686 34 m 582 367 q 529 560 582 481 q 358 652 468 652 q 189 561 250 652 q 135 369 135 482 q 189 176 135 255 q 361 85 251 85 q 529 176 468 85 q 582 367 582 255 "},"²":{"x_min":0,"x_max":442,"ha":539,"o":"m 442 383 l 0 383 q 91 566 0 492 q 260 668 176 617 q 354 798 354 727 q 315 875 354 845 q 227 905 277 905 q 136 869 173 905 q 99 761 99 833 l 14 761 q 82 922 14 864 q 232 974 141 974 q 379 926 316 974 q 442 797 442 878 q 351 635 442 704 q 183 539 321 611 q 92 455 92 491 l 442 455 l 442 383 "},"–":{"x_min":0,"x_max":705.5625,"ha":803,"o":"m 705 334 l 0 334 l 0 410 l 705 410 l 705 334 "},"Κ":{"x_min":0,"x_max":819.5625,"ha":893,"o":"m 819 0 l 650 0 l 294 509 l 139 356 l 139 0 l 0 0 l 0 1013 l 139 1013 l 139 526 l 626 1013 l 809 1013 l 395 600 l 819 0 "},"ƒ":{"x_min":-46.265625,"x_max":392,"ha":513,"o":"m 392 651 l 259 651 l 79 -279 l -46 -278 l 134 651 l 14 651 l 14 751 l 135 751 q 151 948 135 900 q 304 1041 185 1041 q 334 1040 319 1041 q 392 1034 348 1039 l 392 922 q 337 931 360 931 q 271 883 287 931 q 260 793 260 853 l 260 751 l 392 751 l 392 651 "},"e":{"x_min":0,"x_max":714,"ha":813,"o":"m 714 326 l 140 326 q 200 157 140 227 q 359 87 260 87 q 488 130 431 87 q 561 245 545 174 l 697 245 q 577 48 670 123 q 358 -26 484 -26 q 97 85 195 -26 q 0 363 0 197 q 94 642 0 529 q 358 765 195 765 q 626 627 529 765 q 714 326 714 503 m 576 429 q 507 583 564 522 q 355 650 445 650 q 206 583 266 650 q 140 429 152 522 l 576 429 "},"ό":{"x_min":0,"x_max":712,"ha":815,"o":"m 356 -25 q 94 91 194 -25 q 0 368 0 202 q 92 642 0 533 q 356 761 192 761 q 617 644 517 761 q 712 368 712 533 q 619 91 712 201 q 356 -25 520 -25 m 356 85 q 527 175 465 85 q 583 369 583 255 q 528 562 583 484 q 356 651 466 651 q 189 560 250 651 q 135 369 135 481 q 187 177 135 257 q 356 85 250 85 m 576 1040 l 387 819 l 303 819 l 438 1040 l 576 1040 "},"J":{"x_min":0,"x_max":588,"ha":699,"o":"m 588 279 q 287 -26 588 -26 q 58 73 126 -26 q 0 327 0 158 l 133 327 q 160 172 133 227 q 288 96 198 96 q 426 171 391 96 q 449 336 449 219 l 449 1013 l 588 1013 l 588 279 "},"»":{"x_min":-1,"x_max":503,"ha":601,"o":"m 503 302 l 280 136 l 281 256 l 429 373 l 281 486 l 280 608 l 503 440 l 503 302 m 221 302 l 0 136 l 0 255 l 145 372 l 0 486 l -1 608 l 221 440 l 221 302 "},"©":{"x_min":-3,"x_max":1008,"ha":1106,"o":"m 502 -7 q 123 151 263 -7 q -3 501 -3 294 q 123 851 -3 706 q 502 1011 263 1011 q 881 851 739 1011 q 1008 501 1008 708 q 883 151 1008 292 q 502 -7 744 -7 m 502 60 q 830 197 709 60 q 940 501 940 322 q 831 805 940 681 q 502 944 709 944 q 174 805 296 944 q 65 501 65 680 q 173 197 65 320 q 502 60 294 60 m 741 394 q 661 246 731 302 q 496 190 591 190 q 294 285 369 190 q 228 497 228 370 q 295 714 228 625 q 499 813 370 813 q 656 762 588 813 q 733 625 724 711 l 634 625 q 589 704 629 673 q 498 735 550 735 q 377 666 421 735 q 334 504 334 597 q 374 340 334 408 q 490 272 415 272 q 589 304 549 272 q 638 394 628 337 l 741 394 "},"ώ":{"x_min":0,"x_max":922,"ha":1030,"o":"m 687 1040 l 498 819 l 415 819 l 549 1040 l 687 1040 m 922 339 q 856 97 922 203 q 650 -26 780 -26 q 538 9 587 -26 q 461 103 489 44 q 387 12 436 46 q 277 -22 339 -22 q 69 97 147 -22 q 0 338 0 202 q 45 551 0 444 q 161 737 84 643 l 302 737 q 175 552 219 647 q 124 336 124 446 q 155 179 124 248 q 275 88 197 88 q 375 163 341 88 q 400 294 400 219 l 400 572 l 524 572 l 524 294 q 561 135 524 192 q 643 88 591 88 q 762 182 719 88 q 797 341 797 257 q 745 555 797 450 q 619 737 705 637 l 760 737 q 874 551 835 640 q 922 339 922 444 "},"^":{"x_min":193.0625,"x_max":598.609375,"ha":792,"o":"m 598 772 l 515 772 l 395 931 l 277 772 l 193 772 l 326 1013 l 462 1013 l 598 772 "},"«":{"x_min":0,"x_max":507.203125,"ha":604,"o":"m 506 136 l 284 302 l 284 440 l 506 608 l 507 485 l 360 371 l 506 255 l 506 136 m 222 136 l 0 302 l 0 440 l 222 608 l 221 486 l 73 373 l 222 256 l 222 136 "},"D":{"x_min":0,"x_max":828,"ha":935,"o":"m 389 1013 q 714 867 593 1013 q 828 521 828 729 q 712 161 828 309 q 382 0 587 0 l 0 0 l 0 1013 l 389 1013 m 376 124 q 607 247 523 124 q 681 510 681 355 q 607 771 681 662 q 376 896 522 896 l 139 896 l 139 124 l 376 124 "},"∙":{"x_min":0,"x_max":142,"ha":239,"o":"m 142 585 l 0 585 l 0 738 l 142 738 l 142 585 "},"ÿ":{"x_min":0,"x_max":47,"ha":125,"o":"m 47 3 q 37 -7 47 -7 q 28 0 30 -7 q 39 -4 32 -4 q 45 3 45 -1 l 37 0 q 28 9 28 0 q 39 19 28 19 l 47 16 l 47 19 l 47 3 m 37 1 q 44 8 44 1 q 37 16 44 16 q 30 8 30 16 q 37 1 30 1 m 26 1 l 23 22 l 14 0 l 3 22 l 3 3 l 0 25 l 13 1 l 22 25 l 26 1 "},"w":{"x_min":0,"x_max":1009.71875,"ha":1100,"o":"m 1009 738 l 783 0 l 658 0 l 501 567 l 345 0 l 222 0 l 0 738 l 130 738 l 284 174 l 432 737 l 576 738 l 721 173 l 881 737 l 1009 738 "},"$":{"x_min":0,"x_max":700,"ha":793,"o":"m 664 717 l 542 717 q 490 825 531 785 q 381 872 450 865 l 381 551 q 620 446 540 522 q 700 241 700 370 q 618 45 700 116 q 381 -25 536 -25 l 381 -152 l 307 -152 l 307 -25 q 81 62 162 -25 q 0 297 0 149 l 124 297 q 169 146 124 204 q 307 81 215 89 l 307 441 q 80 536 148 469 q 13 725 13 603 q 96 910 13 839 q 307 982 180 982 l 307 1077 l 381 1077 l 381 982 q 574 917 494 982 q 664 717 664 845 m 307 565 l 307 872 q 187 831 233 872 q 142 724 142 791 q 180 618 142 656 q 307 565 218 580 m 381 76 q 562 237 562 96 q 517 361 562 313 q 381 423 472 409 l 381 76 "},"\\":{"x_min":-0.015625,"x_max":425.0625,"ha":522,"o":"m 425 -129 l 337 -129 l 0 1041 l 83 1041 l 425 -129 "},"µ":{"x_min":0,"x_max":697.21875,"ha":747,"o":"m 697 -4 q 629 -14 658 -14 q 498 97 513 -14 q 422 9 470 41 q 313 -23 374 -23 q 207 4 258 -23 q 119 81 156 32 l 119 -278 l 0 -278 l 0 738 l 124 738 l 124 343 q 165 173 124 246 q 308 83 216 83 q 452 178 402 83 q 493 359 493 255 l 493 738 l 617 738 l 617 214 q 623 136 617 160 q 673 92 637 92 q 697 96 684 92 l 697 -4 "},"Ι":{"x_min":42,"x_max":181,"ha":297,"o":"m 181 0 l 42 0 l 42 1013 l 181 1013 l 181 0 "},"Ύ":{"x_min":0,"x_max":1144.5,"ha":1214,"o":"m 1144 1012 l 807 416 l 807 0 l 667 0 l 667 416 l 325 1012 l 465 1012 l 736 533 l 1004 1012 l 1144 1012 m 277 1040 l 83 799 l 0 799 l 140 1040 l 277 1040 "},"’":{"x_min":0,"x_max":139,"ha":236,"o":"m 139 851 q 102 737 139 784 q 0 669 65 690 l 0 734 q 59 787 42 741 q 72 873 72 821 l 0 873 l 0 1013 l 139 1013 l 139 851 "},"Ν":{"x_min":0,"x_max":801,"ha":915,"o":"m 801 0 l 651 0 l 131 822 l 131 0 l 0 0 l 0 1013 l 151 1013 l 670 191 l 670 1013 l 801 1013 l 801 0 "},"-":{"x_min":8.71875,"x_max":350.390625,"ha":478,"o":"m 350 317 l 8 317 l 8 428 l 350 428 l 350 317 "},"Q":{"x_min":0,"x_max":968,"ha":1072,"o":"m 954 5 l 887 -79 l 744 35 q 622 -11 687 2 q 483 -26 556 -26 q 127 130 262 -26 q 0 504 0 279 q 127 880 0 728 q 484 1041 262 1041 q 841 884 708 1041 q 968 507 968 735 q 933 293 968 398 q 832 104 899 188 l 954 5 m 723 191 q 802 330 777 248 q 828 499 828 412 q 744 790 828 673 q 483 922 650 922 q 228 791 322 922 q 142 505 142 673 q 227 221 142 337 q 487 91 323 91 q 632 123 566 91 l 520 215 l 587 301 l 723 191 "},"ς":{"x_min":1,"x_max":676.28125,"ha":740,"o":"m 676 460 l 551 460 q 498 595 542 546 q 365 651 448 651 q 199 578 263 651 q 136 401 136 505 q 266 178 136 241 q 508 106 387 142 q 640 -50 640 62 q 625 -158 640 -105 q 583 -278 611 -211 l 465 -278 q 498 -182 490 -211 q 515 -80 515 -126 q 381 12 515 -15 q 134 91 197 51 q 1 388 1 179 q 100 651 1 542 q 354 761 199 761 q 587 680 498 761 q 676 460 676 599 "},"M":{"x_min":0,"x_max":954,"ha":1067,"o":"m 954 0 l 819 0 l 819 869 l 537 0 l 405 0 l 128 866 l 128 0 l 0 0 l 0 1013 l 200 1013 l 472 160 l 757 1013 l 954 1013 l 954 0 "},"Ψ":{"x_min":0,"x_max":1006,"ha":1094,"o":"m 1006 678 q 914 319 1006 429 q 571 200 814 200 l 571 0 l 433 0 l 433 200 q 92 319 194 200 q 0 678 0 429 l 0 1013 l 139 1013 l 139 679 q 191 417 139 492 q 433 326 255 326 l 433 1013 l 571 1013 l 571 326 l 580 326 q 813 423 747 326 q 868 679 868 502 l 868 1013 l 1006 1013 l 1006 678 "},"C":{"x_min":0,"x_max":886,"ha":944,"o":"m 886 379 q 760 87 886 201 q 455 -26 634 -26 q 112 136 236 -26 q 0 509 0 283 q 118 882 0 737 q 469 1041 245 1041 q 748 955 630 1041 q 879 708 879 859 l 745 708 q 649 862 724 805 q 473 920 573 920 q 219 791 312 920 q 136 509 136 675 q 217 229 136 344 q 470 99 311 99 q 672 179 591 99 q 753 379 753 259 l 886 379 "},"!":{"x_min":0,"x_max":138,"ha":236,"o":"m 138 684 q 116 409 138 629 q 105 244 105 299 l 33 244 q 16 465 33 313 q 0 684 0 616 l 0 1013 l 138 1013 l 138 684 m 138 0 l 0 0 l 0 151 l 138 151 l 138 0 "},"{":{"x_min":0,"x_max":480.5625,"ha":578,"o":"m 480 -286 q 237 -213 303 -286 q 187 -45 187 -159 q 194 48 187 -15 q 201 141 201 112 q 164 264 201 225 q 0 314 118 314 l 0 417 q 164 471 119 417 q 201 605 201 514 q 199 665 201 644 q 193 772 193 769 q 241 941 193 887 q 480 1015 308 1015 l 480 915 q 336 866 375 915 q 306 742 306 828 q 310 662 306 717 q 314 577 314 606 q 288 452 314 500 q 176 365 256 391 q 289 275 257 337 q 314 143 314 226 q 313 84 314 107 q 310 -11 310 -5 q 339 -131 310 -94 q 480 -182 377 -182 l 480 -286 "},"X":{"x_min":-0.015625,"x_max":854.15625,"ha":940,"o":"m 854 0 l 683 0 l 423 409 l 166 0 l 0 0 l 347 519 l 18 1013 l 186 1013 l 428 637 l 675 1013 l 836 1013 l 504 520 l 854 0 "},"#":{"x_min":0,"x_max":963.890625,"ha":1061,"o":"m 963 690 l 927 590 l 719 590 l 655 410 l 876 410 l 840 310 l 618 310 l 508 -3 l 393 -2 l 506 309 l 329 310 l 215 -2 l 102 -3 l 212 310 l 0 310 l 36 410 l 248 409 l 312 590 l 86 590 l 120 690 l 347 690 l 459 1006 l 573 1006 l 462 690 l 640 690 l 751 1006 l 865 1006 l 754 690 l 963 690 m 606 590 l 425 590 l 362 410 l 543 410 l 606 590 "},"ι":{"x_min":42,"x_max":284,"ha":361,"o":"m 284 3 q 233 -10 258 -5 q 182 -15 207 -15 q 85 26 119 -15 q 42 200 42 79 l 42 738 l 167 738 l 168 215 q 172 141 168 157 q 226 101 183 101 q 248 103 239 101 q 284 112 257 104 l 284 3 "},"Ά":{"x_min":0,"x_max":906.953125,"ha":982,"o":"m 283 1040 l 88 799 l 5 799 l 145 1040 l 283 1040 m 906 0 l 756 0 l 650 303 l 251 303 l 143 0 l 0 0 l 376 1012 l 529 1012 l 906 0 m 609 421 l 452 866 l 293 421 l 609 421 "},")":{"x_min":0,"x_max":318,"ha":415,"o":"m 318 365 q 257 25 318 191 q 87 -290 197 -141 l 0 -290 q 140 21 93 -128 q 193 360 193 189 q 141 704 193 537 q 0 1024 97 850 l 87 1024 q 257 706 197 871 q 318 365 318 542 "},"ε":{"x_min":0,"x_max":634.71875,"ha":714,"o":"m 634 234 q 527 38 634 110 q 300 -25 433 -25 q 98 29 183 -25 q 0 204 0 93 q 37 314 0 265 q 128 390 67 353 q 56 460 82 419 q 26 555 26 505 q 114 712 26 654 q 295 763 191 763 q 499 700 416 763 q 589 515 589 631 l 478 515 q 419 618 464 580 q 307 657 374 657 q 207 630 253 657 q 151 547 151 598 q 238 445 151 469 q 389 434 280 434 l 389 331 l 349 331 q 206 315 255 331 q 125 210 125 287 q 183 107 125 145 q 302 76 233 76 q 436 117 379 76 q 509 234 493 159 l 634 234 "},"Δ":{"x_min":0,"x_max":952.78125,"ha":1028,"o":"m 952 0 l 0 0 l 400 1013 l 551 1013 l 952 0 m 762 124 l 476 867 l 187 124 l 762 124 "},"}":{"x_min":0,"x_max":481,"ha":578,"o":"m 481 314 q 318 262 364 314 q 282 136 282 222 q 284 65 282 97 q 293 -58 293 -48 q 241 -217 293 -166 q 0 -286 174 -286 l 0 -182 q 143 -130 105 -182 q 171 -2 171 -93 q 168 81 171 22 q 165 144 165 140 q 188 275 165 229 q 306 365 220 339 q 191 455 224 391 q 165 588 165 505 q 168 681 165 624 q 171 742 171 737 q 141 865 171 827 q 0 915 102 915 l 0 1015 q 243 942 176 1015 q 293 773 293 888 q 287 675 293 741 q 282 590 282 608 q 318 466 282 505 q 481 417 364 417 l 481 314 "},"‰":{"x_min":-3,"x_max":1672,"ha":1821,"o":"m 846 0 q 664 76 732 0 q 603 244 603 145 q 662 412 603 344 q 846 489 729 489 q 1027 412 959 489 q 1089 244 1089 343 q 1029 76 1089 144 q 846 0 962 0 m 845 103 q 945 143 910 103 q 981 243 981 184 q 947 340 981 301 q 845 385 910 385 q 745 342 782 385 q 709 243 709 300 q 742 147 709 186 q 845 103 781 103 m 888 986 l 284 -25 l 199 -25 l 803 986 l 888 986 m 241 468 q 58 545 126 468 q -3 715 -3 615 q 56 881 -3 813 q 238 958 124 958 q 421 881 353 958 q 483 712 483 813 q 423 544 483 612 q 241 468 356 468 m 241 855 q 137 811 175 855 q 100 710 100 768 q 136 612 100 653 q 240 572 172 572 q 344 614 306 572 q 382 713 382 656 q 347 810 382 771 q 241 855 308 855 m 1428 0 q 1246 76 1314 0 q 1185 244 1185 145 q 1244 412 1185 344 q 1428 489 1311 489 q 1610 412 1542 489 q 1672 244 1672 343 q 1612 76 1672 144 q 1428 0 1545 0 m 1427 103 q 1528 143 1492 103 q 1564 243 1564 184 q 1530 340 1564 301 q 1427 385 1492 385 q 1327 342 1364 385 q 1291 243 1291 300 q 1324 147 1291 186 q 1427 103 1363 103 "},"a":{"x_min":0,"x_max":698.609375,"ha":794,"o":"m 698 0 q 661 -12 679 -7 q 615 -17 643 -17 q 536 12 564 -17 q 500 96 508 41 q 384 6 456 37 q 236 -25 312 -25 q 65 31 130 -25 q 0 194 0 88 q 118 390 0 334 q 328 435 180 420 q 488 483 476 451 q 495 523 495 504 q 442 619 495 584 q 325 654 389 654 q 209 617 257 654 q 152 513 161 580 l 33 513 q 123 705 33 633 q 332 772 207 772 q 528 712 448 772 q 617 531 617 645 l 617 163 q 624 108 617 126 q 664 90 632 90 l 698 94 l 698 0 m 491 262 l 491 372 q 272 329 350 347 q 128 201 128 294 q 166 113 128 144 q 264 83 205 83 q 414 130 346 83 q 491 262 491 183 "},"—":{"x_min":0,"x_max":941.671875,"ha":1039,"o":"m 941 334 l 0 334 l 0 410 l 941 410 l 941 334 "},"=":{"x_min":8.71875,"x_max":780.953125,"ha":792,"o":"m 780 510 l 8 510 l 8 606 l 780 606 l 780 510 m 780 235 l 8 235 l 8 332 l 780 332 l 780 235 "},"N":{"x_min":0,"x_max":801,"ha":914,"o":"m 801 0 l 651 0 l 131 823 l 131 0 l 0 0 l 0 1013 l 151 1013 l 670 193 l 670 1013 l 801 1013 l 801 0 "},"ρ":{"x_min":0,"x_max":712,"ha":797,"o":"m 712 369 q 620 94 712 207 q 362 -26 521 -26 q 230 2 292 -26 q 119 83 167 30 l 119 -278 l 0 -278 l 0 362 q 91 643 0 531 q 355 764 190 764 q 617 647 517 764 q 712 369 712 536 m 583 366 q 530 559 583 480 q 359 651 469 651 q 190 562 252 651 q 135 370 135 483 q 189 176 135 257 q 359 85 250 85 q 528 175 466 85 q 583 366 583 254 "},"2":{"x_min":59,"x_max":731,"ha":792,"o":"m 731 0 l 59 0 q 197 314 59 188 q 457 487 199 315 q 598 691 598 580 q 543 819 598 772 q 411 867 488 867 q 272 811 328 867 q 209 630 209 747 l 81 630 q 182 901 81 805 q 408 986 271 986 q 629 909 536 986 q 731 694 731 826 q 613 449 731 541 q 378 316 495 383 q 201 122 235 234 l 731 122 l 731 0 "},"¯":{"x_min":0,"x_max":941.671875,"ha":938,"o":"m 941 1033 l 0 1033 l 0 1109 l 941 1109 l 941 1033 "},"Z":{"x_min":0,"x_max":779,"ha":849,"o":"m 779 0 l 0 0 l 0 113 l 621 896 l 40 896 l 40 1013 l 779 1013 l 778 887 l 171 124 l 779 124 l 779 0 "},"u":{"x_min":0,"x_max":617,"ha":729,"o":"m 617 0 l 499 0 l 499 110 q 391 10 460 45 q 246 -25 322 -25 q 61 58 127 -25 q 0 258 0 136 l 0 738 l 125 738 l 125 284 q 156 148 125 202 q 273 82 197 82 q 433 165 369 82 q 493 340 493 243 l 493 738 l 617 738 l 617 0 "},"k":{"x_min":0,"x_max":612.484375,"ha":697,"o":"m 612 738 l 338 465 l 608 0 l 469 0 l 251 382 l 121 251 l 121 0 l 0 0 l 0 1013 l 121 1013 l 121 402 l 456 738 l 612 738 "},"Η":{"x_min":0,"x_max":803,"ha":917,"o":"m 803 0 l 667 0 l 667 475 l 140 475 l 140 0 l 0 0 l 0 1013 l 140 1013 l 140 599 l 667 599 l 667 1013 l 803 1013 l 803 0 "},"Α":{"x_min":0,"x_max":906.953125,"ha":985,"o":"m 906 0 l 756 0 l 650 303 l 251 303 l 143 0 l 0 0 l 376 1013 l 529 1013 l 906 0 m 609 421 l 452 866 l 293 421 l 609 421 "},"s":{"x_min":0,"x_max":604,"ha":697,"o":"m 604 217 q 501 36 604 104 q 292 -23 411 -23 q 86 43 166 -23 q 0 238 0 114 l 121 237 q 175 122 121 164 q 300 85 223 85 q 415 112 363 85 q 479 207 479 147 q 361 309 479 276 q 140 372 141 370 q 21 544 21 426 q 111 708 21 647 q 298 761 190 761 q 492 705 413 761 q 583 531 583 643 l 462 531 q 412 625 462 594 q 298 657 363 657 q 199 636 242 657 q 143 558 143 608 q 262 454 143 486 q 484 394 479 397 q 604 217 604 341 "},"B":{"x_min":0,"x_max":778,"ha":876,"o":"m 580 546 q 724 469 670 535 q 778 311 778 403 q 673 83 778 171 q 432 0 575 0 l 0 0 l 0 1013 l 411 1013 q 629 957 541 1013 q 732 768 732 892 q 691 633 732 693 q 580 546 650 572 m 393 899 l 139 899 l 139 588 l 379 588 q 521 624 462 588 q 592 744 592 667 q 531 859 592 819 q 393 899 471 899 m 419 124 q 566 169 504 124 q 635 303 635 219 q 559 436 635 389 q 402 477 494 477 l 139 477 l 139 124 l 419 124 "},"…":{"x_min":0,"x_max":614,"ha":708,"o":"m 142 0 l 0 0 l 0 151 l 142 151 l 142 0 m 378 0 l 236 0 l 236 151 l 378 151 l 378 0 m 614 0 l 472 0 l 472 151 l 614 151 l 614 0 "},"?":{"x_min":0,"x_max":607,"ha":704,"o":"m 607 777 q 543 599 607 674 q 422 474 482 537 q 357 272 357 391 l 236 272 q 297 487 236 395 q 411 619 298 490 q 474 762 474 691 q 422 885 474 838 q 301 933 371 933 q 179 880 228 933 q 124 706 124 819 l 0 706 q 94 963 0 872 q 302 1044 177 1044 q 511 973 423 1044 q 607 777 607 895 m 370 0 l 230 0 l 230 151 l 370 151 l 370 0 "},"H":{"x_min":0,"x_max":803,"ha":915,"o":"m 803 0 l 667 0 l 667 475 l 140 475 l 140 0 l 0 0 l 0 1013 l 140 1013 l 140 599 l 667 599 l 667 1013 l 803 1013 l 803 0 "},"ν":{"x_min":0,"x_max":675,"ha":761,"o":"m 675 738 l 404 0 l 272 0 l 0 738 l 133 738 l 340 147 l 541 738 l 675 738 "},"c":{"x_min":1,"x_max":701.390625,"ha":775,"o":"m 701 264 q 584 53 681 133 q 353 -26 487 -26 q 91 91 188 -26 q 1 370 1 201 q 92 645 1 537 q 353 761 190 761 q 572 688 479 761 q 690 493 666 615 l 556 493 q 487 606 545 562 q 356 650 428 650 q 186 563 246 650 q 134 372 134 487 q 188 179 134 258 q 359 88 250 88 q 492 136 437 88 q 566 264 548 185 l 701 264 "},"¶":{"x_min":0,"x_max":566.671875,"ha":678,"o":"m 21 892 l 52 892 l 98 761 l 145 892 l 176 892 l 178 741 l 157 741 l 157 867 l 108 741 l 88 741 l 40 871 l 40 741 l 21 741 l 21 892 m 308 854 l 308 731 q 252 691 308 691 q 227 691 240 691 q 207 696 213 695 l 207 712 l 253 706 q 288 733 288 706 l 288 763 q 244 741 279 741 q 193 797 193 741 q 261 860 193 860 q 287 860 273 860 q 308 854 302 855 m 288 842 l 263 843 q 213 796 213 843 q 248 756 213 756 q 288 796 288 756 l 288 842 m 566 988 l 502 988 l 502 -1 l 439 -1 l 439 988 l 317 988 l 317 -1 l 252 -1 l 252 602 q 81 653 155 602 q 0 805 0 711 q 101 989 0 918 q 309 1053 194 1053 l 566 1053 l 566 988 "},"β":{"x_min":0,"x_max":660,"ha":745,"o":"m 471 550 q 610 450 561 522 q 660 280 660 378 q 578 64 660 151 q 367 -22 497 -22 q 239 5 299 -22 q 126 82 178 32 l 126 -278 l 0 -278 l 0 593 q 54 903 0 801 q 318 1042 127 1042 q 519 964 436 1042 q 603 771 603 887 q 567 644 603 701 q 471 550 532 586 m 337 79 q 476 138 418 79 q 535 279 535 198 q 427 437 535 386 q 226 477 344 477 l 226 583 q 398 620 329 583 q 486 762 486 668 q 435 884 486 833 q 312 935 384 935 q 169 861 219 935 q 126 698 126 797 l 126 362 q 170 169 126 242 q 337 79 224 79 "},"Μ":{"x_min":0,"x_max":954,"ha":1068,"o":"m 954 0 l 819 0 l 819 868 l 537 0 l 405 0 l 128 865 l 128 0 l 0 0 l 0 1013 l 199 1013 l 472 158 l 758 1013 l 954 1013 l 954 0 "},"Ό":{"x_min":0.109375,"x_max":1120,"ha":1217,"o":"m 1120 505 q 994 132 1120 282 q 642 -29 861 -29 q 290 130 422 -29 q 167 505 167 280 q 294 883 167 730 q 650 1046 430 1046 q 999 882 868 1046 q 1120 505 1120 730 m 977 504 q 896 784 977 669 q 644 915 804 915 q 391 785 484 915 q 307 504 307 669 q 391 224 307 339 q 644 95 486 95 q 894 224 803 95 q 977 504 977 339 m 277 1040 l 83 799 l 0 799 l 140 1040 l 277 1040 "},"Ή":{"x_min":0,"x_max":1158,"ha":1275,"o":"m 1158 0 l 1022 0 l 1022 475 l 496 475 l 496 0 l 356 0 l 356 1012 l 496 1012 l 496 599 l 1022 599 l 1022 1012 l 1158 1012 l 1158 0 m 277 1040 l 83 799 l 0 799 l 140 1040 l 277 1040 "},"•":{"x_min":0,"x_max":663.890625,"ha":775,"o":"m 663 529 q 566 293 663 391 q 331 196 469 196 q 97 294 194 196 q 0 529 0 393 q 96 763 0 665 q 331 861 193 861 q 566 763 469 861 q 663 529 663 665 "},"¥":{"x_min":0.1875,"x_max":819.546875,"ha":886,"o":"m 563 561 l 697 561 l 696 487 l 520 487 l 482 416 l 482 380 l 697 380 l 695 308 l 482 308 l 482 0 l 342 0 l 342 308 l 125 308 l 125 380 l 342 380 l 342 417 l 303 487 l 125 487 l 125 561 l 258 561 l 0 1013 l 140 1013 l 411 533 l 679 1013 l 819 1013 l 563 561 "},"(":{"x_min":0,"x_max":318.0625,"ha":415,"o":"m 318 -290 l 230 -290 q 61 23 122 -142 q 0 365 0 190 q 62 712 0 540 q 230 1024 119 869 l 318 1024 q 175 705 219 853 q 125 360 125 542 q 176 22 125 187 q 318 -290 223 -127 "},"U":{"x_min":0,"x_max":796,"ha":904,"o":"m 796 393 q 681 93 796 212 q 386 -25 566 -25 q 101 95 208 -25 q 0 393 0 211 l 0 1013 l 138 1013 l 138 391 q 204 191 138 270 q 394 107 276 107 q 586 191 512 107 q 656 391 656 270 l 656 1013 l 796 1013 l 796 393 "},"γ":{"x_min":0.5,"x_max":744.953125,"ha":822,"o":"m 744 737 l 463 54 l 463 -278 l 338 -278 l 338 54 l 154 495 q 104 597 124 569 q 13 651 67 651 l 0 651 l 0 751 l 39 753 q 168 711 121 753 q 242 594 207 676 l 403 208 l 617 737 l 744 737 "},"α":{"x_min":0,"x_max":765.5625,"ha":809,"o":"m 765 -4 q 698 -14 726 -14 q 564 97 586 -14 q 466 7 525 40 q 337 -26 407 -26 q 88 98 186 -26 q 0 369 0 212 q 88 637 0 525 q 337 760 184 760 q 465 728 407 760 q 563 637 524 696 l 563 739 l 685 739 l 685 222 q 693 141 685 168 q 748 94 708 94 q 765 96 760 94 l 765 -4 m 584 371 q 531 562 584 485 q 360 653 470 653 q 192 566 254 653 q 135 379 135 489 q 186 181 135 261 q 358 84 247 84 q 528 176 465 84 q 584 371 584 260 "},"F":{"x_min":0,"x_max":683.328125,"ha":717,"o":"m 683 888 l 140 888 l 140 583 l 613 583 l 613 458 l 140 458 l 140 0 l 0 0 l 0 1013 l 683 1013 l 683 888 "},"­":{"x_min":0,"x_max":705.5625,"ha":803,"o":"m 705 334 l 0 334 l 0 410 l 705 410 l 705 334 "},":":{"x_min":0,"x_max":142,"ha":239,"o":"m 142 585 l 0 585 l 0 738 l 142 738 l 142 585 m 142 0 l 0 0 l 0 151 l 142 151 l 142 0 "},"Χ":{"x_min":0,"x_max":854.171875,"ha":935,"o":"m 854 0 l 683 0 l 423 409 l 166 0 l 0 0 l 347 519 l 18 1013 l 186 1013 l 427 637 l 675 1013 l 836 1013 l 504 521 l 854 0 "},"*":{"x_min":116,"x_max":674,"ha":792,"o":"m 674 768 l 475 713 l 610 544 l 517 477 l 394 652 l 272 478 l 178 544 l 314 713 l 116 766 l 153 876 l 341 812 l 342 1013 l 446 1013 l 446 811 l 635 874 l 674 768 "},"†":{"x_min":0,"x_max":777,"ha":835,"o":"m 458 804 l 777 804 l 777 683 l 458 683 l 458 0 l 319 0 l 319 681 l 0 683 l 0 804 l 319 804 l 319 1015 l 458 1013 l 458 804 "},"°":{"x_min":0,"x_max":347,"ha":444,"o":"m 173 802 q 43 856 91 802 q 0 977 0 905 q 45 1101 0 1049 q 173 1153 90 1153 q 303 1098 255 1153 q 347 977 347 1049 q 303 856 347 905 q 173 802 256 802 m 173 884 q 238 910 214 884 q 262 973 262 937 q 239 1038 262 1012 q 173 1064 217 1064 q 108 1037 132 1064 q 85 973 85 1010 q 108 910 85 937 q 173 884 132 884 "},"V":{"x_min":0,"x_max":862.71875,"ha":940,"o":"m 862 1013 l 505 0 l 361 0 l 0 1013 l 143 1013 l 434 165 l 718 1012 l 862 1013 "},"Ξ":{"x_min":0,"x_max":734.71875,"ha":763,"o":"m 723 889 l 9 889 l 9 1013 l 723 1013 l 723 889 m 673 463 l 61 463 l 61 589 l 673 589 l 673 463 m 734 0 l 0 0 l 0 124 l 734 124 l 734 0 "}," ":{"x_min":0,"x_max":0,"ha":853},"Ϋ":{"x_min":0.328125,"x_max":819.515625,"ha":889,"o":"m 588 1046 l 460 1046 l 460 1189 l 588 1189 l 588 1046 m 360 1046 l 232 1046 l 232 1189 l 360 1189 l 360 1046 m 819 1012 l 482 416 l 482 0 l 342 0 l 342 416 l 0 1012 l 140 1012 l 411 533 l 679 1012 l 819 1012 "},"0":{"x_min":73,"x_max":715,"ha":792,"o":"m 394 -29 q 153 129 242 -29 q 73 479 73 272 q 152 829 73 687 q 394 989 241 989 q 634 829 545 989 q 715 479 715 684 q 635 129 715 270 q 394 -29 546 -29 m 394 89 q 546 211 489 89 q 598 479 598 322 q 548 748 598 640 q 394 871 491 871 q 241 748 298 871 q 190 479 190 637 q 239 211 190 319 q 394 89 296 89 "},"”":{"x_min":0,"x_max":347,"ha":454,"o":"m 139 851 q 102 737 139 784 q 0 669 65 690 l 0 734 q 59 787 42 741 q 72 873 72 821 l 0 873 l 0 1013 l 139 1013 l 139 851 m 347 851 q 310 737 347 784 q 208 669 273 690 l 208 734 q 267 787 250 741 q 280 873 280 821 l 208 873 l 208 1013 l 347 1013 l 347 851 "},"@":{"x_min":0,"x_max":1260,"ha":1357,"o":"m 1098 -45 q 877 -160 1001 -117 q 633 -203 752 -203 q 155 -29 327 -203 q 0 360 0 127 q 176 802 0 616 q 687 1008 372 1008 q 1123 854 969 1008 q 1260 517 1260 718 q 1155 216 1260 341 q 868 82 1044 82 q 772 106 801 82 q 737 202 737 135 q 647 113 700 144 q 527 82 594 82 q 367 147 420 82 q 314 312 314 212 q 401 565 314 452 q 639 690 498 690 q 810 588 760 690 l 849 668 l 938 668 q 877 441 900 532 q 833 226 833 268 q 853 182 833 198 q 902 167 873 167 q 1088 272 1012 167 q 1159 512 1159 372 q 1051 793 1159 681 q 687 925 925 925 q 248 747 415 925 q 97 361 97 586 q 226 26 97 159 q 627 -122 370 -122 q 856 -87 737 -122 q 1061 8 976 -53 l 1098 -45 m 786 488 q 738 580 777 545 q 643 615 700 615 q 483 517 548 615 q 425 322 425 430 q 457 203 425 250 q 552 156 490 156 q 722 273 665 156 q 786 488 738 309 "},"Ί":{"x_min":0,"x_max":499,"ha":613,"o":"m 277 1040 l 83 799 l 0 799 l 140 1040 l 277 1040 m 499 0 l 360 0 l 360 1012 l 499 1012 l 499 0 "},"i":{"x_min":14,"x_max":136,"ha":275,"o":"m 136 873 l 14 873 l 14 1013 l 136 1013 l 136 873 m 136 0 l 14 0 l 14 737 l 136 737 l 136 0 "},"Β":{"x_min":0,"x_max":778,"ha":877,"o":"m 580 545 q 724 468 671 534 q 778 310 778 402 q 673 83 778 170 q 432 0 575 0 l 0 0 l 0 1013 l 411 1013 q 629 957 541 1013 q 732 768 732 891 q 691 632 732 692 q 580 545 650 571 m 393 899 l 139 899 l 139 587 l 379 587 q 521 623 462 587 q 592 744 592 666 q 531 859 592 819 q 393 899 471 899 m 419 124 q 566 169 504 124 q 635 302 635 219 q 559 435 635 388 q 402 476 494 476 l 139 476 l 139 124 l 419 124 "},"υ":{"x_min":0,"x_max":617,"ha":725,"o":"m 617 352 q 540 94 617 199 q 308 -24 455 -24 q 76 94 161 -24 q 0 352 0 199 l 0 739 l 126 739 l 126 355 q 169 185 126 257 q 312 98 220 98 q 451 185 402 98 q 492 355 492 257 l 492 739 l 617 739 l 617 352 "},"]":{"x_min":0,"x_max":275,"ha":372,"o":"m 275 -281 l 0 -281 l 0 -187 l 151 -187 l 151 920 l 0 920 l 0 1013 l 275 1013 l 275 -281 "},"m":{"x_min":0,"x_max":1019,"ha":1128,"o":"m 1019 0 l 897 0 l 897 454 q 860 591 897 536 q 739 660 816 660 q 613 586 659 660 q 573 436 573 522 l 573 0 l 447 0 l 447 455 q 412 591 447 535 q 294 657 372 657 q 165 586 213 657 q 122 437 122 521 l 122 0 l 0 0 l 0 738 l 117 738 l 117 640 q 202 730 150 697 q 316 763 254 763 q 437 730 381 763 q 525 642 494 697 q 621 731 559 700 q 753 763 682 763 q 943 694 867 763 q 1019 512 1019 625 l 1019 0 "},"χ":{"x_min":8.328125,"x_max":780.5625,"ha":815,"o":"m 780 -278 q 715 -294 747 -294 q 616 -257 663 -294 q 548 -175 576 -227 l 379 133 l 143 -277 l 9 -277 l 313 254 l 163 522 q 127 586 131 580 q 36 640 91 640 q 8 637 27 640 l 8 752 l 52 757 q 162 719 113 757 q 236 627 200 690 l 383 372 l 594 737 l 726 737 l 448 250 l 625 -69 q 670 -153 647 -110 q 743 -188 695 -188 q 780 -184 759 -188 l 780 -278 "},"8":{"x_min":55,"x_max":736,"ha":792,"o":"m 571 527 q 694 424 652 491 q 736 280 736 358 q 648 71 736 158 q 395 -26 551 -26 q 142 69 238 -26 q 55 279 55 157 q 96 425 55 359 q 220 527 138 491 q 120 615 153 562 q 88 726 88 668 q 171 904 88 827 q 395 986 261 986 q 618 905 529 986 q 702 727 702 830 q 670 616 702 667 q 571 527 638 565 m 394 565 q 519 610 475 565 q 563 717 563 655 q 521 823 563 781 q 392 872 474 872 q 265 824 312 872 q 224 720 224 783 q 265 613 224 656 q 394 565 312 565 m 395 91 q 545 150 488 91 q 597 280 597 204 q 546 408 597 355 q 395 465 492 465 q 244 408 299 465 q 194 280 194 356 q 244 150 194 203 q 395 91 299 91 "},"ί":{"x_min":42,"x_max":326.71875,"ha":361,"o":"m 284 3 q 233 -10 258 -5 q 182 -15 207 -15 q 85 26 119 -15 q 42 200 42 79 l 42 737 l 167 737 l 168 215 q 172 141 168 157 q 226 101 183 101 q 248 102 239 101 q 284 112 257 104 l 284 3 m 326 1040 l 137 819 l 54 819 l 189 1040 l 326 1040 "},"Ζ":{"x_min":0,"x_max":779.171875,"ha":850,"o":"m 779 0 l 0 0 l 0 113 l 620 896 l 40 896 l 40 1013 l 779 1013 l 779 887 l 170 124 l 779 124 l 779 0 "},"R":{"x_min":0,"x_max":781.953125,"ha":907,"o":"m 781 0 l 623 0 q 587 242 590 52 q 407 433 585 433 l 138 433 l 138 0 l 0 0 l 0 1013 l 396 1013 q 636 946 539 1013 q 749 731 749 868 q 711 597 749 659 q 608 502 674 534 q 718 370 696 474 q 729 207 722 352 q 781 26 736 62 l 781 0 m 373 551 q 533 594 465 551 q 614 731 614 645 q 532 859 614 815 q 373 896 465 896 l 138 896 l 138 551 l 373 551 "},"o":{"x_min":0,"x_max":713,"ha":821,"o":"m 357 -25 q 94 91 194 -25 q 0 368 0 202 q 93 642 0 533 q 357 761 193 761 q 618 644 518 761 q 713 368 713 533 q 619 91 713 201 q 357 -25 521 -25 m 357 85 q 528 175 465 85 q 584 369 584 255 q 529 562 584 484 q 357 651 467 651 q 189 560 250 651 q 135 369 135 481 q 187 177 135 257 q 357 85 250 85 "},"5":{"x_min":54.171875,"x_max":738,"ha":792,"o":"m 738 314 q 626 60 738 153 q 382 -23 526 -23 q 155 47 248 -23 q 54 256 54 125 l 183 256 q 259 132 204 174 q 382 91 314 91 q 533 149 471 91 q 602 314 602 213 q 538 469 602 411 q 386 528 475 528 q 284 506 332 528 q 197 439 237 484 l 81 439 l 159 958 l 684 958 l 684 840 l 254 840 l 214 579 q 306 627 258 612 q 407 643 354 643 q 636 552 540 643 q 738 314 738 457 "},"7":{"x_min":58.71875,"x_max":730.953125,"ha":792,"o":"m 730 839 q 469 448 560 641 q 335 0 378 255 l 192 0 q 328 441 235 252 q 593 830 421 630 l 58 830 l 58 958 l 730 958 l 730 839 "},"K":{"x_min":0,"x_max":819.46875,"ha":906,"o":"m 819 0 l 649 0 l 294 509 l 139 355 l 139 0 l 0 0 l 0 1013 l 139 1013 l 139 526 l 626 1013 l 809 1013 l 395 600 l 819 0 "},",":{"x_min":0,"x_max":142,"ha":239,"o":"m 142 -12 q 105 -132 142 -82 q 0 -205 68 -182 l 0 -138 q 57 -82 40 -124 q 70 0 70 -51 l 0 0 l 0 151 l 142 151 l 142 -12 "},"d":{"x_min":0,"x_max":683,"ha":796,"o":"m 683 0 l 564 0 l 564 93 q 456 6 516 38 q 327 -25 395 -25 q 87 100 181 -25 q 0 365 0 215 q 90 639 0 525 q 343 763 187 763 q 564 647 486 763 l 564 1013 l 683 1013 l 683 0 m 582 373 q 529 562 582 484 q 361 653 468 653 q 190 561 253 653 q 135 365 135 479 q 189 175 135 254 q 358 85 251 85 q 529 178 468 85 q 582 373 582 258 "},"¨":{"x_min":-109,"x_max":247,"ha":232,"o":"m 247 1046 l 119 1046 l 119 1189 l 247 1189 l 247 1046 m 19 1046 l -109 1046 l -109 1189 l 19 1189 l 19 1046 "},"E":{"x_min":0,"x_max":736.109375,"ha":789,"o":"m 736 0 l 0 0 l 0 1013 l 725 1013 l 725 889 l 139 889 l 139 585 l 677 585 l 677 467 l 139 467 l 139 125 l 736 125 l 736 0 "},"Y":{"x_min":0,"x_max":820,"ha":886,"o":"m 820 1013 l 482 416 l 482 0 l 342 0 l 342 416 l 0 1013 l 140 1013 l 411 534 l 679 1012 l 820 1013 "},"\"":{"x_min":0,"x_max":299,"ha":396,"o":"m 299 606 l 203 606 l 203 988 l 299 988 l 299 606 m 96 606 l 0 606 l 0 988 l 96 988 l 96 606 "},"‹":{"x_min":17.984375,"x_max":773.609375,"ha":792,"o":"m 773 40 l 18 376 l 17 465 l 773 799 l 773 692 l 159 420 l 773 149 l 773 40 "},"„":{"x_min":0,"x_max":364,"ha":467,"o":"m 141 -12 q 104 -132 141 -82 q 0 -205 67 -182 l 0 -138 q 56 -82 40 -124 q 69 0 69 -51 l 0 0 l 0 151 l 141 151 l 141 -12 m 364 -12 q 327 -132 364 -82 q 222 -205 290 -182 l 222 -138 q 279 -82 262 -124 q 292 0 292 -51 l 222 0 l 222 151 l 364 151 l 364 -12 "},"δ":{"x_min":1,"x_max":710,"ha":810,"o":"m 710 360 q 616 87 710 196 q 356 -28 518 -28 q 99 82 197 -28 q 1 356 1 192 q 100 606 1 509 q 355 703 199 703 q 180 829 288 754 q 70 903 124 866 l 70 1012 l 643 1012 l 643 901 l 258 901 q 462 763 422 794 q 636 592 577 677 q 710 360 710 485 m 584 365 q 552 501 584 447 q 451 602 521 555 q 372 611 411 611 q 197 541 258 611 q 136 355 136 472 q 190 171 136 245 q 358 85 252 85 q 528 173 465 85 q 584 365 584 252 "},"έ":{"x_min":0,"x_max":634.71875,"ha":714,"o":"m 634 234 q 527 38 634 110 q 300 -25 433 -25 q 98 29 183 -25 q 0 204 0 93 q 37 313 0 265 q 128 390 67 352 q 56 459 82 419 q 26 555 26 505 q 114 712 26 654 q 295 763 191 763 q 499 700 416 763 q 589 515 589 631 l 478 515 q 419 618 464 580 q 307 657 374 657 q 207 630 253 657 q 151 547 151 598 q 238 445 151 469 q 389 434 280 434 l 389 331 l 349 331 q 206 315 255 331 q 125 210 125 287 q 183 107 125 145 q 302 76 233 76 q 436 117 379 76 q 509 234 493 159 l 634 234 m 520 1040 l 331 819 l 248 819 l 383 1040 l 520 1040 "},"ω":{"x_min":0,"x_max":922,"ha":1031,"o":"m 922 339 q 856 97 922 203 q 650 -26 780 -26 q 538 9 587 -26 q 461 103 489 44 q 387 12 436 46 q 277 -22 339 -22 q 69 97 147 -22 q 0 339 0 203 q 45 551 0 444 q 161 738 84 643 l 302 738 q 175 553 219 647 q 124 336 124 446 q 155 179 124 249 q 275 88 197 88 q 375 163 341 88 q 400 294 400 219 l 400 572 l 524 572 l 524 294 q 561 135 524 192 q 643 88 591 88 q 762 182 719 88 q 797 342 797 257 q 745 556 797 450 q 619 738 705 638 l 760 738 q 874 551 835 640 q 922 339 922 444 "},"´":{"x_min":0,"x_max":96,"ha":251,"o":"m 96 606 l 0 606 l 0 988 l 96 988 l 96 606 "},"±":{"x_min":11,"x_max":781,"ha":792,"o":"m 781 490 l 446 490 l 446 255 l 349 255 l 349 490 l 11 490 l 11 586 l 349 586 l 349 819 l 446 819 l 446 586 l 781 586 l 781 490 m 781 21 l 11 21 l 11 115 l 781 115 l 781 21 "},"|":{"x_min":343,"x_max":449,"ha":792,"o":"m 449 462 l 343 462 l 343 986 l 449 986 l 449 462 m 449 -242 l 343 -242 l 343 280 l 449 280 l 449 -242 "},"ϋ":{"x_min":0,"x_max":617,"ha":725,"o":"m 482 800 l 372 800 l 372 925 l 482 925 l 482 800 m 239 800 l 129 800 l 129 925 l 239 925 l 239 800 m 617 352 q 540 93 617 199 q 308 -24 455 -24 q 76 93 161 -24 q 0 352 0 199 l 0 738 l 126 738 l 126 354 q 169 185 126 257 q 312 98 220 98 q 451 185 402 98 q 492 354 492 257 l 492 738 l 617 738 l 617 352 "},"§":{"x_min":0,"x_max":593,"ha":690,"o":"m 593 425 q 554 312 593 369 q 467 233 516 254 q 537 83 537 172 q 459 -74 537 -12 q 288 -133 387 -133 q 115 -69 184 -133 q 47 96 47 -6 l 166 96 q 199 7 166 40 q 288 -26 232 -26 q 371 -5 332 -26 q 420 60 420 21 q 311 201 420 139 q 108 309 210 255 q 0 490 0 383 q 33 602 0 551 q 124 687 66 654 q 75 743 93 712 q 58 812 58 773 q 133 984 58 920 q 300 1043 201 1043 q 458 987 394 1043 q 529 814 529 925 l 411 814 q 370 908 404 877 q 289 939 336 939 q 213 911 246 939 q 180 841 180 883 q 286 720 180 779 q 484 612 480 615 q 593 425 593 534 m 467 409 q 355 544 467 473 q 196 630 228 612 q 146 587 162 609 q 124 525 124 558 q 239 387 124 462 q 398 298 369 315 q 448 345 429 316 q 467 409 467 375 "},"b":{"x_min":0,"x_max":685,"ha":783,"o":"m 685 372 q 597 99 685 213 q 347 -25 501 -25 q 219 5 277 -25 q 121 93 161 36 l 121 0 l 0 0 l 0 1013 l 121 1013 l 121 634 q 214 723 157 692 q 341 754 272 754 q 591 637 493 754 q 685 372 685 526 m 554 356 q 499 550 554 470 q 328 644 437 644 q 162 556 223 644 q 108 369 108 478 q 160 176 108 256 q 330 83 221 83 q 498 169 435 83 q 554 356 554 245 "},"q":{"x_min":0,"x_max":683,"ha":876,"o":"m 683 -278 l 564 -278 l 564 97 q 474 8 533 39 q 345 -23 415 -23 q 91 93 188 -23 q 0 364 0 203 q 87 635 0 522 q 337 760 184 760 q 466 727 408 760 q 564 637 523 695 l 564 737 l 683 737 l 683 -278 m 582 375 q 527 564 582 488 q 358 652 466 652 q 190 565 253 652 q 135 377 135 488 q 189 179 135 261 q 361 84 251 84 q 530 179 469 84 q 582 375 582 260 "},"Ω":{"x_min":-0.171875,"x_max":969.5625,"ha":1068,"o":"m 969 0 l 555 0 l 555 123 q 744 308 675 194 q 814 558 814 423 q 726 812 814 709 q 484 922 633 922 q 244 820 334 922 q 154 567 154 719 q 223 316 154 433 q 412 123 292 199 l 412 0 l 0 0 l 0 124 l 217 124 q 68 327 122 210 q 15 572 15 444 q 144 911 15 781 q 484 1041 274 1041 q 822 909 691 1041 q 953 569 953 777 q 899 326 953 443 q 750 124 846 210 l 969 124 l 969 0 "},"ύ":{"x_min":0,"x_max":617,"ha":725,"o":"m 617 352 q 540 93 617 199 q 308 -24 455 -24 q 76 93 161 -24 q 0 352 0 199 l 0 738 l 126 738 l 126 354 q 169 185 126 257 q 312 98 220 98 q 451 185 402 98 q 492 354 492 257 l 492 738 l 617 738 l 617 352 m 535 1040 l 346 819 l 262 819 l 397 1040 l 535 1040 "},"z":{"x_min":-0.015625,"x_max":613.890625,"ha":697,"o":"m 613 0 l 0 0 l 0 100 l 433 630 l 20 630 l 20 738 l 594 738 l 593 636 l 163 110 l 613 110 l 613 0 "},"™":{"x_min":0,"x_max":894,"ha":1000,"o":"m 389 951 l 229 951 l 229 503 l 160 503 l 160 951 l 0 951 l 0 1011 l 389 1011 l 389 951 m 894 503 l 827 503 l 827 939 l 685 503 l 620 503 l 481 937 l 481 503 l 417 503 l 417 1011 l 517 1011 l 653 580 l 796 1010 l 894 1011 l 894 503 "},"ή":{"x_min":0.78125,"x_max":697,"ha":810,"o":"m 697 -278 l 572 -278 l 572 454 q 540 587 572 536 q 425 650 501 650 q 271 579 337 650 q 206 420 206 509 l 206 0 l 81 0 l 81 489 q 73 588 81 562 q 0 644 56 644 l 0 741 q 68 755 38 755 q 158 721 124 755 q 200 630 193 687 q 297 726 234 692 q 434 761 359 761 q 620 692 544 761 q 697 516 697 624 l 697 -278 m 479 1040 l 290 819 l 207 819 l 341 1040 l 479 1040 "},"Θ":{"x_min":0,"x_max":960,"ha":1056,"o":"m 960 507 q 833 129 960 280 q 476 -32 698 -32 q 123 129 255 -32 q 0 507 0 280 q 123 883 0 732 q 476 1045 255 1045 q 832 883 696 1045 q 960 507 960 732 m 817 500 q 733 789 817 669 q 476 924 639 924 q 223 792 317 924 q 142 507 142 675 q 222 222 142 339 q 476 89 315 89 q 730 218 636 89 q 817 500 817 334 m 716 449 l 243 449 l 243 571 l 716 571 l 716 449 "},"®":{"x_min":-3,"x_max":1008,"ha":1106,"o":"m 503 532 q 614 562 566 532 q 672 658 672 598 q 614 747 672 716 q 503 772 569 772 l 338 772 l 338 532 l 503 532 m 502 -7 q 123 151 263 -7 q -3 501 -3 294 q 123 851 -3 706 q 502 1011 263 1011 q 881 851 739 1011 q 1008 501 1008 708 q 883 151 1008 292 q 502 -7 744 -7 m 502 60 q 830 197 709 60 q 940 501 940 322 q 831 805 940 681 q 502 944 709 944 q 174 805 296 944 q 65 501 65 680 q 173 197 65 320 q 502 60 294 60 m 788 146 l 678 146 q 653 316 655 183 q 527 449 652 449 l 338 449 l 338 146 l 241 146 l 241 854 l 518 854 q 688 808 621 854 q 766 658 766 755 q 739 563 766 607 q 668 497 713 519 q 751 331 747 472 q 788 164 756 190 l 788 146 "},"~":{"x_min":0,"x_max":833,"ha":931,"o":"m 833 958 q 778 753 833 831 q 594 665 716 665 q 402 761 502 665 q 240 857 302 857 q 131 795 166 857 q 104 665 104 745 l 0 665 q 54 867 0 789 q 237 958 116 958 q 429 861 331 958 q 594 765 527 765 q 704 827 670 765 q 729 958 729 874 l 833 958 "},"Ε":{"x_min":0,"x_max":736.21875,"ha":778,"o":"m 736 0 l 0 0 l 0 1013 l 725 1013 l 725 889 l 139 889 l 139 585 l 677 585 l 677 467 l 139 467 l 139 125 l 736 125 l 736 0 "},"³":{"x_min":0,"x_max":450,"ha":547,"o":"m 450 552 q 379 413 450 464 q 220 366 313 366 q 69 414 130 366 q 0 567 0 470 l 85 567 q 126 470 85 504 q 225 437 168 437 q 320 467 280 437 q 360 552 360 498 q 318 632 360 608 q 213 657 276 657 q 195 657 203 657 q 176 657 181 657 l 176 722 q 279 733 249 722 q 334 815 334 752 q 300 881 334 856 q 220 907 267 907 q 133 875 169 907 q 97 781 97 844 l 15 781 q 78 926 15 875 q 220 972 135 972 q 364 930 303 972 q 426 817 426 888 q 344 697 426 733 q 421 642 392 681 q 450 552 450 603 "},"[":{"x_min":0,"x_max":273.609375,"ha":371,"o":"m 273 -281 l 0 -281 l 0 1013 l 273 1013 l 273 920 l 124 920 l 124 -187 l 273 -187 l 273 -281 "},"L":{"x_min":0,"x_max":645.828125,"ha":696,"o":"m 645 0 l 0 0 l 0 1013 l 140 1013 l 140 126 l 645 126 l 645 0 "},"σ":{"x_min":0,"x_max":803.390625,"ha":894,"o":"m 803 628 l 633 628 q 713 368 713 512 q 618 93 713 204 q 357 -25 518 -25 q 94 91 194 -25 q 0 368 0 201 q 94 644 0 533 q 356 761 194 761 q 481 750 398 761 q 608 739 564 739 l 803 739 l 803 628 m 360 85 q 529 180 467 85 q 584 374 584 262 q 527 566 584 490 q 352 651 463 651 q 187 559 247 651 q 135 368 135 478 q 189 175 135 254 q 360 85 251 85 "},"ζ":{"x_min":0,"x_max":573,"ha":642,"o":"m 573 -40 q 553 -162 573 -97 q 510 -278 543 -193 l 400 -278 q 441 -187 428 -219 q 462 -90 462 -132 q 378 -14 462 -14 q 108 45 197 -14 q 0 290 0 117 q 108 631 0 462 q 353 901 194 767 l 55 901 l 55 1012 l 561 1012 l 561 924 q 261 669 382 831 q 128 301 128 489 q 243 117 128 149 q 458 98 350 108 q 573 -40 573 80 "},"θ":{"x_min":0,"x_max":674,"ha":778,"o":"m 674 496 q 601 160 674 304 q 336 -26 508 -26 q 73 153 165 -26 q 0 485 0 296 q 72 840 0 683 q 343 1045 166 1045 q 605 844 516 1045 q 674 496 674 692 m 546 579 q 498 798 546 691 q 336 935 437 935 q 178 798 237 935 q 126 579 137 701 l 546 579 m 546 475 l 126 475 q 170 233 126 348 q 338 80 230 80 q 504 233 447 80 q 546 475 546 346 "},"Ο":{"x_min":0,"x_max":958,"ha":1054,"o":"m 485 1042 q 834 883 703 1042 q 958 511 958 735 q 834 136 958 287 q 481 -26 701 -26 q 126 130 261 -26 q 0 504 0 279 q 127 880 0 729 q 485 1042 263 1042 m 480 98 q 731 225 638 98 q 815 504 815 340 q 733 783 815 670 q 480 913 640 913 q 226 785 321 913 q 142 504 142 671 q 226 224 142 339 q 480 98 319 98 "},"Γ":{"x_min":0,"x_max":705.28125,"ha":749,"o":"m 705 886 l 140 886 l 140 0 l 0 0 l 0 1012 l 705 1012 l 705 886 "}," ":{"x_min":0,"x_max":0,"ha":375},"%":{"x_min":-3,"x_max":1089,"ha":1186,"o":"m 845 0 q 663 76 731 0 q 602 244 602 145 q 661 412 602 344 q 845 489 728 489 q 1027 412 959 489 q 1089 244 1089 343 q 1029 76 1089 144 q 845 0 962 0 m 844 103 q 945 143 909 103 q 981 243 981 184 q 947 340 981 301 q 844 385 909 385 q 744 342 781 385 q 708 243 708 300 q 741 147 708 186 q 844 103 780 103 m 888 986 l 284 -25 l 199 -25 l 803 986 l 888 986 m 241 468 q 58 545 126 468 q -3 715 -3 615 q 56 881 -3 813 q 238 958 124 958 q 421 881 353 958 q 483 712 483 813 q 423 544 483 612 q 241 468 356 468 m 241 855 q 137 811 175 855 q 100 710 100 768 q 136 612 100 653 q 240 572 172 572 q 344 614 306 572 q 382 713 382 656 q 347 810 382 771 q 241 855 308 855 "},"P":{"x_min":0,"x_max":726,"ha":806,"o":"m 424 1013 q 640 931 555 1013 q 726 719 726 850 q 637 506 726 587 q 413 426 548 426 l 140 426 l 140 0 l 0 0 l 0 1013 l 424 1013 m 379 889 l 140 889 l 140 548 l 372 548 q 522 589 459 548 q 593 720 593 637 q 528 845 593 801 q 379 889 463 889 "},"Έ":{"x_min":0,"x_max":1078.21875,"ha":1118,"o":"m 1078 0 l 342 0 l 342 1013 l 1067 1013 l 1067 889 l 481 889 l 481 585 l 1019 585 l 1019 467 l 481 467 l 481 125 l 1078 125 l 1078 0 m 277 1040 l 83 799 l 0 799 l 140 1040 l 277 1040 "},"Ώ":{"x_min":0.125,"x_max":1136.546875,"ha":1235,"o":"m 1136 0 l 722 0 l 722 123 q 911 309 842 194 q 981 558 981 423 q 893 813 981 710 q 651 923 800 923 q 411 821 501 923 q 321 568 321 720 q 390 316 321 433 q 579 123 459 200 l 579 0 l 166 0 l 166 124 l 384 124 q 235 327 289 210 q 182 572 182 444 q 311 912 182 782 q 651 1042 441 1042 q 989 910 858 1042 q 1120 569 1120 778 q 1066 326 1120 443 q 917 124 1013 210 l 1136 124 l 1136 0 m 277 1040 l 83 800 l 0 800 l 140 1041 l 277 1040 "},"_":{"x_min":0,"x_max":705.5625,"ha":803,"o":"m 705 -334 l 0 -334 l 0 -234 l 705 -234 l 705 -334 "},"Ϊ":{"x_min":-110,"x_max":246,"ha":275,"o":"m 246 1046 l 118 1046 l 118 1189 l 246 1189 l 246 1046 m 18 1046 l -110 1046 l -110 1189 l 18 1189 l 18 1046 m 136 0 l 0 0 l 0 1012 l 136 1012 l 136 0 "},"+":{"x_min":23,"x_max":768,"ha":792,"o":"m 768 372 l 444 372 l 444 0 l 347 0 l 347 372 l 23 372 l 23 468 l 347 468 l 347 840 l 444 840 l 444 468 l 768 468 l 768 372 "},"½":{"x_min":0,"x_max":1050,"ha":1149,"o":"m 1050 0 l 625 0 q 712 178 625 108 q 878 277 722 187 q 967 385 967 328 q 932 456 967 429 q 850 484 897 484 q 759 450 798 484 q 721 352 721 416 l 640 352 q 706 502 640 448 q 851 551 766 551 q 987 509 931 551 q 1050 385 1050 462 q 976 251 1050 301 q 829 179 902 215 q 717 68 740 133 l 1050 68 l 1050 0 m 834 985 l 215 -28 l 130 -28 l 750 984 l 834 985 m 224 422 l 142 422 l 142 811 l 0 811 l 0 867 q 104 889 62 867 q 164 973 157 916 l 224 973 l 224 422 "},"Ρ":{"x_min":0,"x_max":720,"ha":783,"o":"m 424 1013 q 637 933 554 1013 q 720 723 720 853 q 633 508 720 591 q 413 426 546 426 l 140 426 l 140 0 l 0 0 l 0 1013 l 424 1013 m 378 889 l 140 889 l 140 548 l 371 548 q 521 589 458 548 q 592 720 592 637 q 527 845 592 801 q 378 889 463 889 "},"'":{"x_min":0,"x_max":139,"ha":236,"o":"m 139 851 q 102 737 139 784 q 0 669 65 690 l 0 734 q 59 787 42 741 q 72 873 72 821 l 0 873 l 0 1013 l 139 1013 l 139 851 "},"ª":{"x_min":0,"x_max":350,"ha":397,"o":"m 350 625 q 307 616 328 616 q 266 631 281 616 q 247 673 251 645 q 190 628 225 644 q 116 613 156 613 q 32 641 64 613 q 0 722 0 669 q 72 826 0 800 q 247 866 159 846 l 247 887 q 220 934 247 916 q 162 953 194 953 q 104 934 129 953 q 76 882 80 915 l 16 882 q 60 976 16 941 q 166 1011 104 1011 q 266 979 224 1011 q 308 891 308 948 l 308 706 q 311 679 308 688 q 331 670 315 670 l 350 672 l 350 625 m 247 757 l 247 811 q 136 790 175 798 q 64 726 64 773 q 83 682 64 697 q 132 667 103 667 q 207 690 174 667 q 247 757 247 718 "},"΅":{"x_min":0,"x_max":450,"ha":553,"o":"m 450 800 l 340 800 l 340 925 l 450 925 l 450 800 m 406 1040 l 212 800 l 129 800 l 269 1040 l 406 1040 m 110 800 l 0 800 l 0 925 l 110 925 l 110 800 "},"T":{"x_min":0,"x_max":777,"ha":835,"o":"m 777 894 l 458 894 l 458 0 l 319 0 l 319 894 l 0 894 l 0 1013 l 777 1013 l 777 894 "},"Φ":{"x_min":0,"x_max":915,"ha":997,"o":"m 527 0 l 389 0 l 389 122 q 110 231 220 122 q 0 509 0 340 q 110 785 0 677 q 389 893 220 893 l 389 1013 l 527 1013 l 527 893 q 804 786 693 893 q 915 509 915 679 q 805 231 915 341 q 527 122 696 122 l 527 0 m 527 226 q 712 310 641 226 q 779 507 779 389 q 712 705 779 627 q 527 787 641 787 l 527 226 m 389 226 l 389 787 q 205 698 275 775 q 136 505 136 620 q 206 308 136 391 q 389 226 276 226 "},"⁋":{"x_min":0,"x_max":0,"ha":694},"j":{"x_min":-77.78125,"x_max":167,"ha":349,"o":"m 167 871 l 42 871 l 42 1013 l 167 1013 l 167 871 m 167 -80 q 121 -231 167 -184 q -26 -278 76 -278 l -77 -278 l -77 -164 l -41 -164 q 26 -143 11 -164 q 42 -65 42 -122 l 42 737 l 167 737 l 167 -80 "},"Σ":{"x_min":0,"x_max":756.953125,"ha":819,"o":"m 756 0 l 0 0 l 0 107 l 395 523 l 22 904 l 22 1013 l 745 1013 l 745 889 l 209 889 l 566 523 l 187 125 l 756 125 l 756 0 "},"1":{"x_min":215.671875,"x_max":574,"ha":792,"o":"m 574 0 l 442 0 l 442 697 l 215 697 l 215 796 q 386 833 330 796 q 475 986 447 875 l 574 986 l 574 0 "},"›":{"x_min":18.0625,"x_max":774,"ha":792,"o":"m 774 376 l 18 40 l 18 149 l 631 421 l 18 692 l 18 799 l 774 465 l 774 376 "},"<":{"x_min":17.984375,"x_max":773.609375,"ha":792,"o":"m 773 40 l 18 376 l 17 465 l 773 799 l 773 692 l 159 420 l 773 149 l 773 40 "},"£":{"x_min":0,"x_max":704.484375,"ha":801,"o":"m 704 41 q 623 -10 664 5 q 543 -26 583 -26 q 359 15 501 -26 q 243 36 288 36 q 158 23 197 36 q 73 -21 119 10 l 6 76 q 125 195 90 150 q 175 331 175 262 q 147 443 175 383 l 0 443 l 0 512 l 108 512 q 43 734 43 623 q 120 929 43 854 q 358 1010 204 1010 q 579 936 487 1010 q 678 729 678 857 l 678 684 l 552 684 q 504 838 552 780 q 362 896 457 896 q 216 852 263 896 q 176 747 176 815 q 199 627 176 697 q 248 512 217 574 l 468 512 l 468 443 l 279 443 q 297 356 297 398 q 230 194 297 279 q 153 107 211 170 q 227 133 190 125 q 293 142 264 142 q 410 119 339 142 q 516 96 482 96 q 579 105 550 96 q 648 142 608 115 l 704 41 "},"t":{"x_min":0,"x_max":367,"ha":458,"o":"m 367 0 q 312 -5 339 -2 q 262 -8 284 -8 q 145 28 183 -8 q 108 143 108 64 l 108 638 l 0 638 l 0 738 l 108 738 l 108 944 l 232 944 l 232 738 l 367 738 l 367 638 l 232 638 l 232 185 q 248 121 232 140 q 307 102 264 102 q 345 104 330 102 q 367 107 360 107 l 367 0 "},"¬":{"x_min":0,"x_max":706,"ha":803,"o":"m 706 411 l 706 158 l 630 158 l 630 335 l 0 335 l 0 411 l 706 411 "},"λ":{"x_min":0,"x_max":750,"ha":803,"o":"m 750 -7 q 679 -15 716 -15 q 538 59 591 -15 q 466 214 512 97 l 336 551 l 126 0 l 0 0 l 270 705 q 223 837 247 770 q 116 899 190 899 q 90 898 100 899 l 90 1004 q 152 1011 125 1011 q 298 938 244 1011 q 373 783 326 901 l 605 192 q 649 115 629 136 q 716 95 669 95 l 736 95 q 750 97 745 97 l 750 -7 "},"W":{"x_min":0,"x_max":1263.890625,"ha":1351,"o":"m 1263 1013 l 995 0 l 859 0 l 627 837 l 405 0 l 265 0 l 0 1013 l 136 1013 l 342 202 l 556 1013 l 701 1013 l 921 207 l 1133 1012 l 1263 1013 "},">":{"x_min":18.0625,"x_max":774,"ha":792,"o":"m 774 376 l 18 40 l 18 149 l 631 421 l 18 692 l 18 799 l 774 465 l 774 376 "},"v":{"x_min":0,"x_max":675.15625,"ha":761,"o":"m 675 738 l 404 0 l 272 0 l 0 738 l 133 737 l 340 147 l 541 737 l 675 738 "},"τ":{"x_min":0.28125,"x_max":644.5,"ha":703,"o":"m 644 628 l 382 628 l 382 179 q 388 120 382 137 q 436 91 401 91 q 474 94 447 91 q 504 97 501 97 l 504 0 q 454 -9 482 -5 q 401 -14 426 -14 q 278 67 308 -14 q 260 233 260 118 l 260 628 l 0 628 l 0 739 l 644 739 l 644 628 "},"ξ":{"x_min":0,"x_max":624.9375,"ha":699,"o":"m 624 -37 q 608 -153 624 -96 q 563 -278 593 -211 l 454 -278 q 491 -183 486 -200 q 511 -83 511 -126 q 484 -23 511 -44 q 370 1 452 1 q 323 0 354 1 q 283 -1 293 -1 q 84 76 169 -1 q 0 266 0 154 q 56 431 0 358 q 197 538 108 498 q 94 613 134 562 q 54 730 54 665 q 77 823 54 780 q 143 901 101 867 l 27 901 l 27 1012 l 576 1012 l 576 901 l 380 901 q 244 863 303 901 q 178 745 178 820 q 312 600 178 636 q 532 582 380 582 l 532 479 q 276 455 361 479 q 118 281 118 410 q 165 173 118 217 q 274 120 208 133 q 494 101 384 110 q 624 -37 624 76 "},"&":{"x_min":-3,"x_max":894.25,"ha":992,"o":"m 894 0 l 725 0 l 624 123 q 471 0 553 40 q 306 -41 390 -41 q 168 -7 231 -41 q 62 92 105 26 q 14 187 31 139 q -3 276 -3 235 q 55 433 -3 358 q 248 581 114 508 q 170 689 196 640 q 137 817 137 751 q 214 985 137 922 q 384 1041 284 1041 q 548 988 483 1041 q 622 824 622 928 q 563 666 622 739 q 431 556 516 608 l 621 326 q 649 407 639 361 q 663 493 653 426 l 781 493 q 703 229 781 352 l 894 0 m 504 818 q 468 908 504 877 q 384 940 433 940 q 293 907 331 940 q 255 818 255 875 q 289 714 255 767 q 363 628 313 678 q 477 729 446 682 q 504 818 504 771 m 556 209 l 314 499 q 179 395 223 449 q 135 283 135 341 q 146 222 135 253 q 183 158 158 192 q 333 80 241 80 q 556 209 448 80 "},"Λ":{"x_min":0,"x_max":862.5,"ha":942,"o":"m 862 0 l 719 0 l 426 847 l 143 0 l 0 0 l 356 1013 l 501 1013 l 862 0 "},"I":{"x_min":41,"x_max":180,"ha":293,"o":"m 180 0 l 41 0 l 41 1013 l 180 1013 l 180 0 "},"G":{"x_min":0,"x_max":921,"ha":1011,"o":"m 921 0 l 832 0 l 801 136 q 655 15 741 58 q 470 -28 568 -28 q 126 133 259 -28 q 0 499 0 284 q 125 881 0 731 q 486 1043 259 1043 q 763 957 647 1043 q 905 709 890 864 l 772 709 q 668 866 747 807 q 486 926 589 926 q 228 795 322 926 q 142 507 142 677 q 228 224 142 342 q 483 94 323 94 q 712 195 625 94 q 796 435 796 291 l 477 435 l 477 549 l 921 549 l 921 0 "},"ΰ":{"x_min":0,"x_max":617,"ha":725,"o":"m 524 800 l 414 800 l 414 925 l 524 925 l 524 800 m 183 800 l 73 800 l 73 925 l 183 925 l 183 800 m 617 352 q 540 93 617 199 q 308 -24 455 -24 q 76 93 161 -24 q 0 352 0 199 l 0 738 l 126 738 l 126 354 q 169 185 126 257 q 312 98 220 98 q 451 185 402 98 q 492 354 492 257 l 492 738 l 617 738 l 617 352 m 489 1040 l 300 819 l 216 819 l 351 1040 l 489 1040 "},"`":{"x_min":0,"x_max":138.890625,"ha":236,"o":"m 138 699 l 0 699 l 0 861 q 36 974 0 929 q 138 1041 72 1020 l 138 977 q 82 931 95 969 q 69 839 69 893 l 138 839 l 138 699 "},"·":{"x_min":0,"x_max":142,"ha":239,"o":"m 142 585 l 0 585 l 0 738 l 142 738 l 142 585 "},"Υ":{"x_min":0.328125,"x_max":819.515625,"ha":889,"o":"m 819 1013 l 482 416 l 482 0 l 342 0 l 342 416 l 0 1013 l 140 1013 l 411 533 l 679 1013 l 819 1013 "},"r":{"x_min":0,"x_max":355.5625,"ha":432,"o":"m 355 621 l 343 621 q 179 569 236 621 q 122 411 122 518 l 122 0 l 0 0 l 0 737 l 117 737 l 117 604 q 204 719 146 686 q 355 753 262 753 l 355 621 "},"x":{"x_min":0,"x_max":675,"ha":764,"o":"m 675 0 l 525 0 l 331 286 l 144 0 l 0 0 l 256 379 l 12 738 l 157 737 l 336 473 l 516 738 l 661 738 l 412 380 l 675 0 "},"μ":{"x_min":0,"x_max":696.609375,"ha":747,"o":"m 696 -4 q 628 -14 657 -14 q 498 97 513 -14 q 422 8 470 41 q 313 -24 374 -24 q 207 3 258 -24 q 120 80 157 31 l 120 -278 l 0 -278 l 0 738 l 124 738 l 124 343 q 165 172 124 246 q 308 82 216 82 q 451 177 402 82 q 492 358 492 254 l 492 738 l 616 738 l 616 214 q 623 136 616 160 q 673 92 636 92 q 696 95 684 92 l 696 -4 "},"h":{"x_min":0,"x_max":615,"ha":724,"o":"m 615 472 l 615 0 l 490 0 l 490 454 q 456 590 490 535 q 338 654 416 654 q 186 588 251 654 q 122 436 122 522 l 122 0 l 0 0 l 0 1013 l 122 1013 l 122 633 q 218 727 149 694 q 362 760 287 760 q 552 676 484 760 q 615 472 615 600 "},".":{"x_min":0,"x_max":142,"ha":239,"o":"m 142 0 l 0 0 l 0 151 l 142 151 l 142 0 "},"φ":{"x_min":-2,"x_max":878,"ha":974,"o":"m 496 -279 l 378 -279 l 378 -17 q 101 88 204 -17 q -2 367 -2 194 q 68 626 -2 510 q 283 758 151 758 l 283 646 q 167 537 209 626 q 133 373 133 462 q 192 177 133 254 q 378 93 259 93 l 378 758 q 445 764 426 763 q 476 765 464 765 q 765 659 653 765 q 878 377 878 553 q 771 96 878 209 q 496 -17 665 -17 l 496 -279 m 496 93 l 514 93 q 687 183 623 93 q 746 380 746 265 q 691 569 746 491 q 522 658 629 658 l 496 656 l 496 93 "},";":{"x_min":0,"x_max":142,"ha":239,"o":"m 142 585 l 0 585 l 0 738 l 142 738 l 142 585 m 142 -12 q 105 -132 142 -82 q 0 -206 68 -182 l 0 -138 q 58 -82 43 -123 q 68 0 68 -56 l 0 0 l 0 151 l 142 151 l 142 -12 "},"f":{"x_min":0,"x_max":378,"ha":472,"o":"m 378 638 l 246 638 l 246 0 l 121 0 l 121 638 l 0 638 l 0 738 l 121 738 q 137 935 121 887 q 290 1028 171 1028 q 320 1027 305 1028 q 378 1021 334 1026 l 378 908 q 323 918 346 918 q 257 870 273 918 q 246 780 246 840 l 246 738 l 378 738 l 378 638 "},"“":{"x_min":1,"x_max":348.21875,"ha":454,"o":"m 140 670 l 1 670 l 1 830 q 37 943 1 897 q 140 1011 74 990 l 140 947 q 82 900 97 940 q 68 810 68 861 l 140 810 l 140 670 m 348 670 l 209 670 l 209 830 q 245 943 209 897 q 348 1011 282 990 l 348 947 q 290 900 305 940 q 276 810 276 861 l 348 810 l 348 670 "},"A":{"x_min":0.03125,"x_max":906.953125,"ha":1008,"o":"m 906 0 l 756 0 l 648 303 l 251 303 l 142 0 l 0 0 l 376 1013 l 529 1013 l 906 0 m 610 421 l 452 867 l 293 421 l 610 421 "},"6":{"x_min":53,"x_max":739,"ha":792,"o":"m 739 312 q 633 62 739 162 q 400 -31 534 -31 q 162 78 257 -31 q 53 439 53 206 q 178 859 53 712 q 441 986 284 986 q 643 912 559 986 q 732 713 732 833 l 601 713 q 544 830 594 786 q 426 875 494 875 q 268 793 331 875 q 193 517 193 697 q 301 597 240 570 q 427 624 362 624 q 643 540 552 624 q 739 312 739 451 m 603 298 q 540 461 603 400 q 404 516 484 516 q 268 461 323 516 q 207 300 207 401 q 269 137 207 198 q 405 83 325 83 q 541 137 486 83 q 603 298 603 197 "},"‘":{"x_min":1,"x_max":139.890625,"ha":236,"o":"m 139 670 l 1 670 l 1 830 q 37 943 1 897 q 139 1011 74 990 l 139 947 q 82 900 97 940 q 68 810 68 861 l 139 810 l 139 670 "},"ϊ":{"x_min":-70,"x_max":283,"ha":361,"o":"m 283 800 l 173 800 l 173 925 l 283 925 l 283 800 m 40 800 l -70 800 l -70 925 l 40 925 l 40 800 m 283 3 q 232 -10 257 -5 q 181 -15 206 -15 q 84 26 118 -15 q 41 200 41 79 l 41 737 l 166 737 l 167 215 q 171 141 167 157 q 225 101 182 101 q 247 103 238 101 q 283 112 256 104 l 283 3 "},"π":{"x_min":-0.21875,"x_max":773.21875,"ha":857,"o":"m 773 -7 l 707 -11 q 575 40 607 -11 q 552 174 552 77 l 552 226 l 552 626 l 222 626 l 222 0 l 97 0 l 97 626 l 0 626 l 0 737 l 773 737 l 773 626 l 676 626 l 676 171 q 695 103 676 117 q 773 90 714 90 l 773 -7 "},"ά":{"x_min":0,"x_max":765.5625,"ha":809,"o":"m 765 -4 q 698 -14 726 -14 q 564 97 586 -14 q 466 7 525 40 q 337 -26 407 -26 q 88 98 186 -26 q 0 369 0 212 q 88 637 0 525 q 337 760 184 760 q 465 727 407 760 q 563 637 524 695 l 563 738 l 685 738 l 685 222 q 693 141 685 168 q 748 94 708 94 q 765 95 760 94 l 765 -4 m 584 371 q 531 562 584 485 q 360 653 470 653 q 192 566 254 653 q 135 379 135 489 q 186 181 135 261 q 358 84 247 84 q 528 176 465 84 q 584 371 584 260 m 604 1040 l 415 819 l 332 819 l 466 1040 l 604 1040 "},"O":{"x_min":0,"x_max":958,"ha":1057,"o":"m 485 1041 q 834 882 702 1041 q 958 512 958 734 q 834 136 958 287 q 481 -26 702 -26 q 126 130 261 -26 q 0 504 0 279 q 127 880 0 728 q 485 1041 263 1041 m 480 98 q 731 225 638 98 q 815 504 815 340 q 733 783 815 669 q 480 912 640 912 q 226 784 321 912 q 142 504 142 670 q 226 224 142 339 q 480 98 319 98 "},"n":{"x_min":0,"x_max":615,"ha":724,"o":"m 615 463 l 615 0 l 490 0 l 490 454 q 453 592 490 537 q 331 656 410 656 q 178 585 240 656 q 117 421 117 514 l 117 0 l 0 0 l 0 738 l 117 738 l 117 630 q 218 728 150 693 q 359 764 286 764 q 552 675 484 764 q 615 463 615 593 "},"3":{"x_min":54,"x_max":737,"ha":792,"o":"m 737 284 q 635 55 737 141 q 399 -25 541 -25 q 156 52 248 -25 q 54 308 54 140 l 185 308 q 245 147 185 202 q 395 96 302 96 q 539 140 484 96 q 602 280 602 190 q 510 429 602 390 q 324 454 451 454 l 324 565 q 487 584 441 565 q 565 719 565 617 q 515 835 565 791 q 395 879 466 879 q 255 824 307 879 q 203 661 203 769 l 78 661 q 166 909 78 822 q 387 992 250 992 q 603 921 513 992 q 701 723 701 844 q 669 607 701 656 q 578 524 637 558 q 696 434 655 499 q 737 284 737 369 "},"9":{"x_min":53,"x_max":739,"ha":792,"o":"m 739 524 q 619 94 739 241 q 362 -32 516 -32 q 150 47 242 -32 q 59 244 59 126 l 191 244 q 246 129 191 176 q 373 82 301 82 q 526 161 466 82 q 597 440 597 255 q 363 334 501 334 q 130 432 216 334 q 53 650 53 521 q 134 880 53 786 q 383 986 226 986 q 659 841 566 986 q 739 524 739 719 m 388 449 q 535 514 480 449 q 585 658 585 573 q 535 805 585 744 q 388 873 480 873 q 242 809 294 873 q 191 658 191 745 q 239 514 191 572 q 388 449 292 449 "},"l":{"x_min":41,"x_max":166,"ha":279,"o":"m 166 0 l 41 0 l 41 1013 l 166 1013 l 166 0 "},"¤":{"x_min":40.09375,"x_max":728.796875,"ha":825,"o":"m 728 304 l 649 224 l 512 363 q 383 331 458 331 q 256 363 310 331 l 119 224 l 40 304 l 177 441 q 150 553 150 493 q 184 673 150 621 l 40 818 l 119 898 l 267 749 q 321 766 291 759 q 384 773 351 773 q 447 766 417 773 q 501 749 477 759 l 649 898 l 728 818 l 585 675 q 612 618 604 648 q 621 553 621 587 q 591 441 621 491 l 728 304 m 384 682 q 280 643 318 682 q 243 551 243 604 q 279 461 243 499 q 383 423 316 423 q 487 461 449 423 q 525 553 525 500 q 490 641 525 605 q 384 682 451 682 "},"κ":{"x_min":0,"x_max":632.328125,"ha":679,"o":"m 632 0 l 482 0 l 225 384 l 124 288 l 124 0 l 0 0 l 0 738 l 124 738 l 124 446 l 433 738 l 596 738 l 312 466 l 632 0 "},"4":{"x_min":48,"x_max":742.453125,"ha":792,"o":"m 742 243 l 602 243 l 602 0 l 476 0 l 476 243 l 48 243 l 48 368 l 476 958 l 602 958 l 602 354 l 742 354 l 742 243 m 476 354 l 476 792 l 162 354 l 476 354 "},"p":{"x_min":0,"x_max":685,"ha":786,"o":"m 685 364 q 598 96 685 205 q 350 -23 504 -23 q 121 89 205 -23 l 121 -278 l 0 -278 l 0 738 l 121 738 l 121 633 q 220 726 159 691 q 351 761 280 761 q 598 636 504 761 q 685 364 685 522 m 557 371 q 501 560 557 481 q 330 651 437 651 q 162 559 223 651 q 108 366 108 479 q 162 177 108 254 q 333 87 224 87 q 502 178 441 87 q 557 371 557 258 "},"‡":{"x_min":0,"x_max":777,"ha":835,"o":"m 458 238 l 458 0 l 319 0 l 319 238 l 0 238 l 0 360 l 319 360 l 319 681 l 0 683 l 0 804 l 319 804 l 319 1015 l 458 1013 l 458 804 l 777 804 l 777 683 l 458 683 l 458 360 l 777 360 l 777 238 l 458 238 "},"ψ":{"x_min":0,"x_max":808,"ha":907,"o":"m 465 -278 l 341 -278 l 341 -15 q 87 102 180 -15 q 0 378 0 210 l 0 739 l 133 739 l 133 379 q 182 195 133 275 q 341 98 242 98 l 341 922 l 465 922 l 465 98 q 623 195 563 98 q 675 382 675 278 l 675 742 l 808 742 l 808 381 q 720 104 808 213 q 466 -13 627 -13 l 465 -278 "},"η":{"x_min":0.78125,"x_max":697,"ha":810,"o":"m 697 -278 l 572 -278 l 572 454 q 540 587 572 536 q 425 650 501 650 q 271 579 337 650 q 206 420 206 509 l 206 0 l 81 0 l 81 489 q 73 588 81 562 q 0 644 56 644 l 0 741 q 68 755 38 755 q 158 720 124 755 q 200 630 193 686 q 297 726 234 692 q 434 761 359 761 q 620 692 544 761 q 697 516 697 624 l 697 -278 "}},"cssFontWeight":"normal","ascender":1189,"underlinePosition":-100,"cssFontStyle":"normal","boundingBox":{"yMin":-334,"xMin":-111,"yMax":1189,"xMax":1672},"resolution":1000,"original_font_information":{"postscript_name":"Helvetiker-Regular","version_string":"Version 1.00 2004 initial release","vendor_url":"http://www.magenta.gr/","full_font_name":"Helvetiker","font_family_name":"Helvetiker","copyright":"Copyright (c) Μagenta ltd, 2004","description":"","trademark":"","designer":"","designer_url":"","unique_font_identifier":"Μagenta ltd:Helvetiker:22-10-104","license_url":"http://www.ellak.gr/fonts/MgOpen/license.html","license_description":"Copyright (c) 2004 by MAGENTA Ltd. All Rights Reserved.\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license (\"Fonts\") and associated documentation files (the \"Font Software\"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: \r\n\r\nThe above copyright and this permission notice shall be included in all copies of one or more of the Font Software typefaces.\r\n\r\nThe Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing the word \"MgOpen\", or if the modifications are accepted for inclusion in the Font Software itself by the each appointed Administrator.\r\n\r\nThis License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the \"MgOpen\" name.\r\n\r\nThe Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. \r\n\r\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL MAGENTA OR PERSONS OR BODIES IN CHARGE OF ADMINISTRATION AND MAINTENANCE OF THE FONT SOFTWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.","manufacturer_name":"Μagenta ltd","font_sub_family_name":"Regular"},"descender":-334,"familyName":"Helvetiker","lineHeight":1522,"underlineThickness":50} \ No newline at end of file diff --git a/src/js/indicators.js b/src/js/indicators.js new file mode 100644 index 0000000..0285784 --- /dev/null +++ b/src/js/indicators.js @@ -0,0 +1,107 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + +var modbus = require('./modbus.js'); + + +module.exports = { + template: '#indicators-template', + props: ['state'], + + + computed: { + modbus_status: function () {return modbus.status_to_string(this.state.mx)}, + + + sense_error: function () { + var error = ''; + + if (this.state.motor_voltage_sense_error) error += 'Motor voltage\n'; + if (this.state.motor_current_sense_error) error += 'Motor current\n'; + if (this.state.load1_sense_error) error += 'Load 1\n'; + if (this.state.load2_sense_error) error += 'Load 2\n'; + if (this.state.vdd_current_sense_error) error += 'Vdd current\n'; + + return error; + } + }, + + + methods: { + is_motor_enabled: function (motor) { + return typeof this.state[motor + 'me'] != 'undefined' && + this.state[motor + 'me']; + }, + + + get_min_pin: function (motor) { + switch (motor) { + case 0: return 3; + case 1: return 5; + case 2: return 9; + case 3: return 11; + } + }, + + + get_max_pin: function (motor) { + switch (motor) { + case 0: return 4; + case 1: return 8; + case 2: return 10; + case 3: return 12; + } + }, + + + motor_fault_class: function (motor, bit) { + if (typeof motor == 'undefined') { + var status = this.state['fa']; + if (typeof status == 'undefined') return 'fa-question'; + return 'fa-thumbs-' + (status ? 'down error' : 'up success') + } + + var flags = this.state[motor + 'df']; + if (typeof flags == 'undefined') return 'fa-question'; + return (flags & (1 << bit)) ? 'fa-thumbs-down error' : + 'fa-thumbs-up success'; + }, + + + motor_reset: function (motor) { + if (typeof motor == 'undefined') { + var cmd = ''; + for (var i = 0; i < 4; i++) + cmd += '\\$' + i + 'df=0\n'; + this.$dispatch('send', cmd); + + } else this.$dispatch('send', '\\$' + motor + 'df=0'); + } + } +} diff --git a/src/js/io-indicator.js b/src/js/io-indicator.js new file mode 100644 index 0000000..1803661 --- /dev/null +++ b/src/js/io-indicator.js @@ -0,0 +1,177 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +module.exports = { + template: "#io-indicator-template", + props: ['name', 'state'], + + + computed: { + klass: function () { + if (this.name == 'min-switch-0') return this.get_motor_min_class(0); + if (this.name == 'min-switch-1') return this.get_motor_min_class(1); + if (this.name == 'min-switch-2') return this.get_motor_min_class(2); + if (this.name == 'min-switch-3') return this.get_motor_min_class(3); + if (this.name == 'max-switch-0') return this.get_motor_max_class(0); + if (this.name == 'max-switch-1') return this.get_motor_max_class(1); + if (this.name == 'max-switch-2') return this.get_motor_max_class(2); + if (this.name == 'max-switch-3') return this.get_motor_max_class(3); + if (this.name == 'estop') return this.get_input_class('ew', 'et'); + if (this.name == 'probe') return this.get_input_class('pw', 'pt'); + if (this.name == 'load-1') return this.get_output_class('1'); + if (this.name == 'load-2') return this.get_output_class('2'); + if (this.name == 'fault') return this.get_output_class('f'); + if (this.name == 'tool-enable-mode') return this.get_output_class('e'); + if (this.name == 'tool-direction-mode') return this.get_output_class('d'); + }, + + + tooltip: function () { + if (this.name == 'min-switch-0') return this.get_motor_min_tooltip(0); + if (this.name == 'min-switch-1') return this.get_motor_min_tooltip(1); + if (this.name == 'min-switch-2') return this.get_motor_min_tooltip(2); + if (this.name == 'min-switch-3') return this.get_motor_min_tooltip(3); + if (this.name == 'max-switch-0') return this.get_motor_max_tooltip(0); + if (this.name == 'max-switch-1') return this.get_motor_max_tooltip(1); + if (this.name == 'max-switch-2') return this.get_motor_max_tooltip(2); + if (this.name == 'max-switch-3') return this.get_motor_max_tooltip(3); + if (this.name == 'estop') return this.get_input_tooltip('ew', 'et'); + if (this.name == 'probe') return this.get_input_tooltip('pw', 'pt'); + if (this.name == 'load-1') return this.get_output_tooltip('1'); + if (this.name == 'load-2') return this.get_output_tooltip('2'); + if (this.name == 'fault') return this.get_output_tooltip('f'); + if (this.name == 'tool-direction-mode') + return this.get_output_tooltip('d'); + if (this.name == 'tool-enable-mode') + return this.get_output_tooltip('e'); + } + }, + + + methods: { + get_io_state_class: function (active, state) { + if (typeof active == 'undefined' || typeof state == 'undefined') + return 'fa-exclamation-triangle warn'; + + if (state == 2) return 'fa-circle-o'; + + return (state ? 'fa-plus-circle' : 'fa-minus-circle') + ' ' + + (active ? 'active' : 'inactive'); + }, + + + get_input_active: function (stateCode, typeCode) { + var type = this.state[typeCode]; + var state = this.state[stateCode]; + + if (type == 1) return !state; // Normally open + else if (type == 2) return state; // Normally closed + + return false + }, + + + get_input_class: function (stateCode, typeCode) { + return this.get_io_state_class(this.get_input_active(stateCode, typeCode), + this.state[stateCode]); + }, + + + get_output_class: function (output) { + return this.get_io_state_class(this.state[output + 'oa'], + this.state[output + 'os']); + }, + + + get_motor_min_class: function (motor) { + return this.get_input_class(motor + 'lw', motor + 'ls'); + }, + + + get_motor_max_class: function (motor) { + return this.get_input_class(motor + 'xw', motor + 'xs'); + }, + + + get_tooltip: function (mode, active, state) { + if (typeof mode == 'undefined' || typeof active == 'undefined' || + typeof state == 'undefined') return 'Invalid'; + + if (state == 0) state = 'Lo/Gnd'; + else if (state == 1) state = 'Hi/+3.3v'; + else if (state == 2) state = 'Tristated'; + else return 'Invalid'; + + return 'Mode: ' + mode + '\nActive: ' + (active ? 'True' : 'False') + + '\nLevel: ' + state; + }, + + + get_input_tooltip: function (stateCode, typeCode) { + var type = this.state[typeCode]; + if (type == 0) return 'Disabled'; + else if (type == 1) type = 'Normally open'; + else if (type == 2) type = 'Normally closed'; + + var active = this.get_input_active(stateCode, typeCode); + var state = this.state[stateCode]; + + return this.get_tooltip(type, active, state); + }, + + + get_output_tooltip: function (output) { + var mode = this.state[output + 'om']; + if (mode == 0) return 'Disabled'; + else if (mode == 1) mode = 'Lo/Hi'; + else if (mode == 2) mode = 'Hi/Lo'; + else if (mode == 3) mode = 'Tri/Lo'; + else if (mode == 4) mode = 'Tri/Hi'; + else if (mode == 5) mode = 'Lo/Tri'; + else if (mode == 6) mode = 'Hi/Tri'; + else mode = undefined; + + var active = this.state[output + 'oa']; + var state = this.state[output + 'os']; + + return this.get_tooltip(mode, active, state); + }, + + + get_motor_min_tooltip: function (motor) { + return this.get_input_tooltip(motor + 'lw', motor + 'ls'); + }, + + + get_motor_max_tooltip: function (motor) { + return this.get_input_tooltip(motor + 'xw', motor + 'xs'); + } + } +} diff --git a/src/js/io-view.js b/src/js/io-view.js new file mode 100644 index 0000000..1cbdc49 --- /dev/null +++ b/src/js/io-view.js @@ -0,0 +1,42 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +module.exports = { + template: '#io-view-template', + props: ['config', 'template', 'state'], + + + events: { + 'input-changed': function() { + this.$dispatch('config-changed'); + return false; + } + } +} diff --git a/src/js/main.js b/src/js/main.js new file mode 100644 index 0000000..d369409 --- /dev/null +++ b/src/js/main.js @@ -0,0 +1,147 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict'; + + +function cookie_get(name) { + var decodedCookie = decodeURIComponent(document.cookie); + var ca = decodedCookie.split(';'); + name = name + '='; + + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1); + if (!c.indexOf(name)) return c.substring(name.length, c.length); + } +} + + +function cookie_set(name, value, days) { + var d = new Date(); + d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000); + var expires = 'expires=' + d.toUTCString(); + document.cookie = name + '=' + value + ';' + expires + ';path=/'; +} + + +var uuid_chars = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+'; + + +function uuid(length) { + if (typeof length == 'undefined') length = 52; + + var s = ''; + for (var i = 0; i < length; i++) + s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)]; + + return s +} + + +$(function() { + if (typeof cookie_get('client-id') == 'undefined') + cookie_set('client-id', uuid(), 10000); + + // Vue debugging + Vue.config.debug = true; + //Vue.util.warn = function (msg) {console.debug('[Vue warn]: ' + msg)} + + // Register global components + Vue.component('templated-input', require('./templated-input')); + Vue.component('message', require('./message')); + Vue.component('indicators', require('./indicators')); + Vue.component('io-indicator', require('./io-indicator')); + Vue.component('console', require('./console')); + Vue.component('unit-value', require('./unit-value')); + + Vue.filter('number', function (value) { + if (isNaN(value)) return 'NaN'; + return value.toLocaleString(); + }); + + Vue.filter('percent', function (value, precision) { + if (typeof value == 'undefined') return ''; + if (typeof precision == 'undefined') precision = 2; + return (value * 100.0).toFixed(precision) + '%'; + }); + + Vue.filter('non_zero_percent', function (value, precision) { + if (!value) return ''; + if (typeof precision == 'undefined') precision = 2; + return (value * 100.0).toFixed(precision) + '%'; + }); + + Vue.filter('fixed', function (value, precision) { + if (typeof value == 'undefined') return '0'; + return parseFloat(value).toFixed(precision) + }); + + Vue.filter('upper', function (value) { + if (typeof value == 'undefined') return ''; + return value.toUpperCase() + }); + + Vue.filter('time', function (value, precision) { + if (isNaN(value)) return ''; + if (isNaN(precision)) precision = 0; + + var MIN = 60; + var HR = MIN * 60; + var DAY = HR * 24; + var parts = []; + + if (DAY <= value) { + parts.push(Math.floor(value / DAY)); + value %= DAY; + } + + if (HR <= value) { + parts.push(Math.floor(value / HR)); + value %= HR; + } + + if (MIN <= value) { + parts.push(Math.floor(value / MIN)); + value %= MIN; + + } else parts.push(0); + + parts.push(value); + + for (var i = 0; i < parts.length; i++) { + parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0); + if (i && parts[i] < 10) parts[i] = '0' + parts[i]; + } + + return parts.join(':'); + }); + + // Vue app + require('./app'); +}); diff --git a/src/js/message.js b/src/js/message.js new file mode 100644 index 0000000..a720511 --- /dev/null +++ b/src/js/message.js @@ -0,0 +1,41 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +module.exports = { + template: '#message-template', + + props: { + show: { + type: Boolean, + required: true, + twoWay: true + } + } +} diff --git a/src/js/modbus-reg.js b/src/js/modbus-reg.js new file mode 100644 index 0000000..224f22e --- /dev/null +++ b/src/js/modbus-reg.js @@ -0,0 +1,48 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +module.exports = { + replace: true, + template: '#modbus-reg-view-template', + props: ['index', 'model', 'template', 'enable'], + + + computed: { + has_user_value: function () { + var type = this.model['reg-type']; + return type.indexOf('write') != -1 || type.indexOf('fixed') != -1; + } + }, + + + methods: { + change: function () {this.$dispatch('input-changed')} + } +} diff --git a/src/js/modbus.js b/src/js/modbus.js new file mode 100644 index 0000000..83a7504 --- /dev/null +++ b/src/js/modbus.js @@ -0,0 +1,51 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +// Must match modbus.c +var exports = { + DISCONNECTED: 0, + OK: 1, + CRC: 2, + INVALID: 3, + TIMEDOUT: 4 +}; + + +exports.status_to_string = + function (status) { + if (status == exports.OK) return 'Ok'; + if (status == exports.CRC) return 'CRC error'; + if (status == exports.INVALID) return 'Invalid response'; + if (status == exports.TIMEDOUT) return 'Timedout'; + return 'Disconnected'; + } + + +module.exports = exports; diff --git a/src/js/motor-view.js b/src/js/motor-view.js new file mode 100644 index 0000000..49bbf08 --- /dev/null +++ b/src/js/motor-view.js @@ -0,0 +1,136 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +module.exports = { + template: '#motor-view-template', + props: ['index', 'config', 'template', 'state'], + + + computed: { + metric: function () {return this.$root.metric()}, + + + is_slave: function () { + for (var i = 0; i < this.index; i++) + if (this.motor.axis == this.config.motors[i].axis) + return true; + + return false; + }, + + + motor: function () {return this.config.motors[this.index]}, + + + invalidMaxVelocity: function () { + return this.maxMaxVelocity < this.motor['max-velocity']; + }, + + + maxMaxVelocity: function () { + return 1 * (15 * this.umPerStep / this.motor['microsteps']).toFixed(3); + }, + + + ustepPerSec: function () { + return this.rpm * this.stepsPerRev * this.motor['microsteps'] / 60; + }, + + + rpm: function () { + return 1000 * this.motor['max-velocity'] / this.motor['travel-per-rev']; + }, + + + gForce: function () {return this.motor['max-accel'] * 0.0283254504}, + gForcePerMin: function () {return this.motor['max-jerk'] * 0.0283254504}, + stepsPerRev: function () {return 360 / this.motor['step-angle']}, + + + umPerStep: function () { + return this.motor['travel-per-rev'] * this.motor['step-angle'] / 0.36 + }, + + + milPerStep: function () {return this.umPerStep / 25.4}, + + invalidStallVelocity: function() { + if(!this.motor['homing-mode'].startsWith('stall-')) return false; + return this.maxStallVelocity < this.motor['search-velocity']; + }, + + stallRPM: function() { + var v = this.motor['search-velocity']; + return 1000 * v / this.motor['travel-per-rev']; + }, + + maxStallVelocity: function() { + var maxRate = 900000/this.motor['stall-sample-time']; + var ustep = this.motor['stall-microstep']; + var angle = this.motor['step-angle']; + var travel = this.motor['travel-per-rev']; + var maxStall = maxRate * 60/ 360 /1000 * angle/ ustep * travel; + + return 1 * maxStall.toFixed(3); + }, + + stallUStepPerSec: function() { + var ustep = this.motor['stall-microstep']; + return this.stallRPM * this.stepsPerRev * ustep / 60; + } + + }, + + + events: { + 'input-changed': function() { + Vue.nextTick(function () { + // Limit max-velocity + if (this.invalidMaxVelocity) + this.$set('motor["max-velocity"]', this.maxMaxVelocity); + + //Limit stall-velocity + if(this.invalidStallVelocity) + this.$set('motor["search-velocity"]', this.maxStallVelocity); + + this.$dispatch('config-changed'); + }.bind(this)) + + return false; + } + }, + + methods: { + show: function(name, templ) { + if(templ.hmodes == undefined) return true; + return templ.hmodes.indexOf(this.motor['homing-mode']) != -1; + } + } +} diff --git a/src/js/orbit.js b/src/js/orbit.js new file mode 100644 index 0000000..45c5768 --- /dev/null +++ b/src/js/orbit.js @@ -0,0 +1,682 @@ +/** + * @author qiao / https://github.com/qiao + * @author mrdoob / http://mrdoob.com + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author erich666 / http://erichaines.com + * @author jcoffland / https://buildbotics.com/ + */ + +'use strict' + +// This set of controls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up +// (+Y by default). +// +// Orbit - left mouse / touch: one-finger move +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - right mouse, or arrow keys / touch: two-finger move + + +var OrbitControls = function (object, domElement) { + this.object = object; + this.domElement = domElement != undefined ? domElement : document; + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new THREE.Vector3(); + + // How far you can zoom in and out (OrthographicCamera only) + this.minZoom = 0; + this.maxZoom = Infinity; + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, must be a sub-interval of the interval [- Math.PI, Math.PI]. + this.minAzimuthAngle = -Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.25; + + // This option enables dollying in and out; + // left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.panSpeed = 1.0; + this.screenSpacePanning = false; // if true, pan in screen-space + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + + // Set to false to disable use of the keys + this.enableKeys = true; + + // The four arrow keys + this.keys = {LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40}; + + // Mouse buttons + this.mouseButtons = { + ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT + }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // public methods + this.getPolarAngle = function () {return spherical.phi} + this.getAzimuthalAngle = function () {return spherical.theta} + + + this.saveState = function () { + scope.target0.copy(scope.target); + scope.position0.copy(scope.object.position); + scope.zoom0 = scope.object.zoom; + } + + + this.reset = function () { + scope.target.copy(scope.target0); + scope.object.position.copy(scope.position0); + scope.object.zoom = scope.zoom0; + scope.object.updateProjectionMatrix(); + + scope.dispatchEvent(changeEvent); + scope.update(); + + state = STATE.NONE; + } + + + this.update = function () { + var offset = new THREE.Vector3(); + + // so camera.up is the orbit axis + var quat = new THREE.Quaternion() + .setFromUnitVectors(object.up, new THREE.Vector3(0, 1, 0)); + var quatInverse = quat.clone().inverse(); + + var lastPosition = new THREE.Vector3(); + var lastQuaternion = new THREE.Quaternion(); + + return function update() { + var position = scope.object.position; + + offset.copy(position).sub(scope.target); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion(quat); + + // angle from z-axis around y-axis + spherical.setFromVector3(offset); + + if (scope.autoRotate && state == STATE.NONE) + rotateLeft(getAutoRotationAngle()); + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + // restrict theta to be between desired limits + spherical.theta = + Math.max(scope.minAzimuthAngle, + Math.min(scope.maxAzimuthAngle, spherical.theta)); + + // restrict phi to be between desired limits + spherical.phi = + Math.max(scope.minPolarAngle, + Math.min(scope.maxPolarAngle, spherical.phi)); + + spherical.makeSafe(); + spherical.radius *= scale; + + // restrict radius to be between desired limits + spherical.radius = + Math.max(10, Math.min(scope.object.far * 0.8, spherical.radius)); + + // move target to panned location + scope.target.add(panOffset); + + offset.setFromSpherical(spherical); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion(quatInverse); + + position.copy(scope.target).add(offset); + scope.object.lookAt(scope.target); + + if (scope.enableDamping) { + sphericalDelta.theta *= (1 - scope.dampingFactor); + sphericalDelta.phi *= (1 - scope.dampingFactor); + panOffset.multiplyScalar(1 - scope.dampingFactor); + + } else { + sphericalDelta.set(0, 0, 0); + panOffset.set(0, 0, 0); + } + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + if (zoomChanged || scale != 1 || + lastPosition.distanceToSquared(scope.object.position) > EPS || + 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) { + + scope.dispatchEvent(changeEvent); + + lastPosition.copy(scope.object.position); + lastQuaternion.copy(scope.object.quaternion); + zoomChanged = false; + scale = 1; + + return true; + } + + return false; + } + }() + + + this.dispose = function () { + scope.domElement.removeEventListener('contextmenu', onContextMenu, false); + scope.domElement.removeEventListener('mousedown', onMouseDown, false); + scope.domElement.removeEventListener('wheel', onMouseWheel, false); + scope.domElement.removeEventListener('touchstart', onTouchStart, false); + scope.domElement.removeEventListener('touchend', onTouchEnd, false); + scope.domElement.removeEventListener('touchmove', onTouchMove, false); + document.removeEventListener('mousemove', onMouseMove, false); + document.removeEventListener('mouseup', onMouseUp, false); + window.removeEventListener('keydown', onKeyDown, false); + } + + + // internals + var scope = this; + + var changeEvent = {type: 'change'}; + var startEvent = {type: 'start'}; + var endEvent = {type: 'end'}; + + var STATE = { + NONE: -1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4 + }; + + var state = STATE.NONE; + var EPS = 0.000001; + + // current position in spherical coordinates + var spherical = new THREE.Spherical(); + var sphericalDelta = new THREE.Spherical(); + + var scale = 1; + var panOffset = new THREE.Vector3(); + var zoomChanged = false; + + var rotateStart = new THREE.Vector2(); + var rotateEnd = new THREE.Vector2(); + var rotateDelta = new THREE.Vector2(); + + var panStart = new THREE.Vector2(); + var panEnd = new THREE.Vector2(); + var panDelta = new THREE.Vector2(); + + var dollyStart = new THREE.Vector2(); + var dollyEnd = new THREE.Vector2(); + var dollyDelta = new THREE.Vector2(); + + + function getAutoRotationAngle() { + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + } + + + function getZoomScale() {return Math.pow(0.95, scope.zoomSpeed)} + function rotateLeft(angle) {sphericalDelta.theta -= angle} + function rotateUp(angle) {sphericalDelta.phi -= angle} + + + var panLeft = function () { + var v = new THREE.Vector3(); + + return function panLeft(distance, objectMatrix) { + v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix + v.multiplyScalar(-distance); + panOffset.add(v); + } + }() + + + var panUp = function () { + var v = new THREE.Vector3(); + + return function panUp(distance, objectMatrix) { + if (scope.screenSpacePanning) v.setFromMatrixColumn(objectMatrix, 1); + else { + v.setFromMatrixColumn(objectMatrix, 0); + v.crossVectors(scope.object.up, v); + } + + v.multiplyScalar(distance); + panOffset.add(v); + } + }() + + + function unknownCamera() { + console.warn('WARNING: OrbitControls.js encountered an unknown camera ' + + 'type - pan & zoom disabled.'); + scope.enablePan = false; + scope.enableZoom = false; + } + + + // deltaX and deltaY are in pixels; right and down are positive + var pan = function () { + var offset = new THREE.Vector3(); + + return function pan(deltaX, deltaY) { + var element = scope.domElement === document ? + scope.domElement.body : scope.domElement; + + if (scope.object.isPerspectiveCamera) { + // perspective + offset.copy(scope.object.position).sub(scope.target); + var targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan((scope.object.fov / 2) * Math.PI / 180.0); + + // we use only clientHeight here so aspect ratio does not distort speed + panLeft(2 * deltaX * targetDistance / element.clientHeight, + scope.object.matrix); + panUp(2 * deltaY * targetDistance / element.clientHeight, + scope.object.matrix); + + } else if (scope.object.isOrthographicCamera) { + // orthographic + panLeft(deltaX * (scope.object.right - scope.object.left) / + scope.object.zoom / element.clientWidth, scope.object.matrix); + panUp(deltaY * (scope.object.top - scope.object.bottom) / + scope.object.zoom / element.clientHeight, scope.object.matrix); + + } else unknownCamera(); + } + }() + + + function dollyIn(dollyScale) { + if (scope.object.isPerspectiveCamera) scale /= dollyScale; + + else if (scope.object.isOrthographicCamera) { + scope.object.zoom = + Math.max(scope.minZoom, + Math.min(scope.maxZoom, scope.object.zoom * dollyScale)); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else unknownCamera(); + } + + + function dollyOut(dollyScale) { + if (scope.object.isPerspectiveCamera) scale *= dollyScale; + + else if (scope.object.isOrthographicCamera) { + scope.object.zoom = + Math.max(scope.minZoom, + Math.min(scope.maxZoom, scope.object.zoom / dollyScale)); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else unknownCamera(); + } + + + // event callbacks - update the object state + function handleMouseDownRotate(event) { + rotateStart.set(event.clientX, event.clientY); + } + + + function handleMouseDownDolly(event) { + dollyStart.set(event.clientX, event.clientY); + } + + + function handleMouseDownPan(event) { + panStart.set(event.clientX, event.clientY); + } + + + function handleMouseMoveRotate(event) { + rotateEnd.set(event.clientX, event.clientY); + rotateDelta.subVectors(rotateEnd, rotateStart) + .multiplyScalar(scope.rotateSpeed); + + var element = scope.domElement === document ? + scope.domElement.body : scope.domElement; + + // yes, height + rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); + rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight); + + rotateStart.copy(rotateEnd); + + scope.update(); + } + + + function handleMouseMoveDolly(event) { + dollyEnd.set(event.clientX, event.clientY); + dollyDelta.subVectors(dollyEnd, dollyStart); + + if (dollyDelta.y > 0) dollyIn(getZoomScale()); + else if (dollyDelta.y < 0) dollyOut(getZoomScale()); + + dollyStart.copy(dollyEnd); + scope.update(); + } + + + function handleMouseMovePan(event) { + panEnd.set(event.clientX, event.clientY); + panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); + pan(panDelta.x, panDelta.y); + panStart.copy(panEnd); + scope.update(); + } + + + function handleMouseUp(event) {} + + + function handleMouseWheel(event) { + if (event.deltaY < 0) dollyOut(getZoomScale()); + else if (event.deltaY > 0) dollyIn(getZoomScale()); + + scope.update(); + } + + + function handleKeyDown(event) { + switch (event.keyCode) { + case scope.keys.UP: + pan(0, scope.keyPanSpeed); + scope.update(); + break; + + case scope.keys.BOTTOM: + pan(0, -scope.keyPanSpeed); + scope.update(); + break; + + case scope.keys.LEFT: + pan(scope.keyPanSpeed, 0); + scope.update(); + break; + + case scope.keys.RIGHT: + pan(-scope.keyPanSpeed, 0); + scope.update(); + break; + } + } + + + function handleTouchStartRotate(event) { + rotateStart.set(event.touches[0].pageX, event.touches[0].pageY); + } + + + function handleTouchStartDollyPan(event) { + if (scope.enableZoom) { + var dx = event.touches[0].pageX - event.touches[1].pageX; + var dy = event.touches[0].pageY - event.touches[1].pageY; + var distance = Math.sqrt(dx * dx + dy * dy); + + dollyStart.set(0, distance); + } + + if (scope.enablePan) { + var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); + var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); + panStart.set(x, y); + } + } + + + function handleTouchMoveRotate(event) { + rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY); + rotateDelta.subVectors(rotateEnd, rotateStart) + .multiplyScalar(scope.rotateSpeed); + + var element = scope.domElement === document ? + scope.domElement.body : scope.domElement; + + // yes, height + rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); + rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight); + rotateStart.copy(rotateEnd); + scope.update(); + } + + + function handleTouchMoveDollyPan(event) { + if (scope.enableZoom) { + var dx = event.touches[0].pageX - event.touches[1].pageX; + var dy = event.touches[0].pageY - event.touches[1].pageY; + var distance = Math.sqrt(dx * dx + dy * dy); + + dollyEnd.set(0, distance); + dollyDelta.set(0, Math.pow(dollyEnd.y / dollyStart.y, scope.zoomSpeed)); + dollyIn(dollyDelta.y); + dollyStart.copy(dollyEnd); + } + + + if (scope.enablePan) { + var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); + var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); + + panEnd.set(x, y); + panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); + pan(panDelta.x, panDelta.y); + panStart.copy(panEnd); + } + + scope.update(); + } + + + function handleTouchEnd(event) {} + + + // event handlers - listen for events and reset state + function onMouseDown(event) { + if (!scope.enabled) return; + + event.preventDefault(); + + switch (event.button) { + case scope.mouseButtons.ORBIT: + if (!scope.enableRotate) return; + handleMouseDownRotate(event); + state = STATE.ROTATE; + break; + + case scope.mouseButtons.ZOOM: + if (!scope.enableZoom) return; + handleMouseDownDolly(event); + state = STATE.DOLLY; + break; + + case scope.mouseButtons.PAN: + if (!scope.enablePan) return; + handleMouseDownPan(event); + state = STATE.PAN; + break; + } + + if (state != STATE.NONE) { + document.addEventListener('mousemove', onMouseMove, false); + document.addEventListener('mouseup', onMouseUp, false); + scope.dispatchEvent(startEvent); + } + } + + + function onMouseMove(event) { + if (!scope.enabled) return; + + event.preventDefault(); + + switch (state) { + case STATE.ROTATE: + if (!scope.enableRotate) return; + handleMouseMoveRotate(event); + break; + + case STATE.DOLLY: + if (!scope.enableZoom) return; + handleMouseMoveDolly(event); + break; + + case STATE.PAN: + if (!scope.enablePan) return; + handleMouseMovePan(event); + break; + } + } + + + function onMouseUp(event) { + if (!scope.enabled) return; + + handleMouseUp(event); + document.removeEventListener('mousemove', onMouseMove, false); + document.removeEventListener('mouseup', onMouseUp, false); + scope.dispatchEvent(endEvent); + state = STATE.NONE; + } + + + function onMouseWheel(event) { + if (!scope.enabled || !scope.enableZoom || + (state != STATE.NONE && state != STATE.ROTATE)) return; + + event.preventDefault(); + event.stopPropagation(); + scope.dispatchEvent(startEvent); + handleMouseWheel(event); + scope.dispatchEvent(endEvent); + } + + + function onKeyDown(event) { + if (!scope.enabled || !scope.enableKeys || !scope.enablePan) return; + + handleKeyDown(event); + } + + + function onTouchStart(event) { + if (!scope.enabled) return; + + event.preventDefault(); + + switch (event.touches.length) { + case 1: // one-fingered touch: rotate + if (!scope.enableRotate) return; + handleTouchStartRotate(event); + state = STATE.TOUCH_ROTATE; + break; + + case 2: // two-fingered touch: dolly-pan + if (!scope.enableZoom && !scope.enablePan) return; + handleTouchStartDollyPan(event); + state = STATE.TOUCH_DOLLY_PAN; + break; + + default: state = STATE.NONE; + } + + if (state != STATE.NONE) scope.dispatchEvent(startEvent); + } + + + function onTouchMove(event) { + if (!scope.enabled) return; + + event.preventDefault(); + event.stopPropagation(); + + switch (event.touches.length) { + case 1: // one-fingered touch: rotate + if (!scope.enableRotate) return; + if (state != STATE.TOUCH_ROTATE) return; // is this needed? + + handleTouchMoveRotate(event); + break; + + case 2: // two-fingered touch: dolly-pan + if (!scope.enableZoom && !scope.enablePan) return; + if (state != STATE.TOUCH_DOLLY_PAN) return; // is this needed? + + handleTouchMoveDollyPan(event); + break; + + default: state = STATE.NONE; + } + } + + + function onTouchEnd(event) { + if (!scope.enabled) return; + + handleTouchEnd(event); + scope.dispatchEvent(endEvent); + state = STATE.NONE; + } + + + function onContextMenu(event) { + if (!scope.enabled) return; + event.preventDefault(); + } + + + scope.domElement.addEventListener('contextmenu', onContextMenu, false); + scope.domElement.addEventListener('mousedown', onMouseDown, false); + scope.domElement.addEventListener('wheel', onMouseWheel, false); + scope.domElement.addEventListener('touchstart', onTouchStart, false); + scope.domElement.addEventListener('touchend', onTouchEnd, false); + scope.domElement.addEventListener('touchmove', onTouchMove, false); + window .addEventListener('keydown', onKeyDown, false); + + this.update(); // force an update at start +} + + +OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype); +OrbitControls.prototype.constructor = OrbitControls; +module.exports = OrbitControls; diff --git a/src/js/path-viewer.js b/src/js/path-viewer.js new file mode 100644 index 0000000..3cd1d01 --- /dev/null +++ b/src/js/path-viewer.js @@ -0,0 +1,759 @@ +/******************************************************************************\ + + Copyright 2018. Buildbotics LLC + All Rights Reserved. + + For information regarding this software email: + Joseph Coffland + joseph@buildbotics.com + + This software is free software: you clan redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation, either version 2.1 of + the License, or (at your option) any later version. + + This software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the C! library. If not, see + . + +\******************************************************************************/ + +'use strict' + +var orbit = require('./orbit'); +var cookie = require('./cookie')('bbctrl-'); +var api = require('./api'); +var font = require('./helvetiker_regular.typeface.json') + + +function get(obj, name, defaultValue) { + return typeof obj[name] == 'undefined' ? defaultValue : obj[name]; +} + + +var surfaceModes = ['cut', 'wire', 'solid', 'off']; + + +module.exports = { + template: '#path-viewer-template', + props: ['toolpath'], + + + data: function () { + return { + enabled: false, + loading: false, + dirty: true, + snapView: cookie.get('snap-view', 'isometric'), + small: cookie.get_bool('small-path-view', true), + surfaceMode: 'cut', + showPath: cookie.get_bool('show-path', true), + showTool: cookie.get_bool('show-tool', true), + showBBox: cookie.get_bool('show-bbox', true), + showAxes: cookie.get_bool('show-axes', true), + showIntensity: cookie.get_bool('show-intensity', false) + } + }, + + + computed: { + target: function () {return $(this.$el).find('.path-viewer-content')[0]} + }, + + + watch: { + toolpath: function () {Vue.nextTick(this.update)}, + surfaceMode: function (mode) {this.update_surface_mode(mode)}, + + + small: function (enable) { + cookie.set_bool('small-path-view', enable); + Vue.nextTick(this.update_view) + }, + + + showPath: function (enable) { + cookie.set_bool('show-path', enable); + this.set_visible(this.pathView, enable) + }, + + + showTool: function (enable) { + cookie.set_bool('show-tool', enable); + this.set_visible(this.toolView, enable) + }, + + + showAxes: function (enable) { + cookie.set_bool('show-axes', enable); + this.set_visible(this.axesView, enable) + }, + + + showIntensity: function (enable) { + cookie.set_bool('show-intensity', enable); + Vue.nextTick(this.update) + }, + + + showBBox: function (enable) { + cookie.set_bool('show-bbox', enable); + this.set_visible(this.bboxView, enable); + this.set_visible(this.envelopeView, enable); + }, + + + x: function () {this.axis_changed()}, + y: function () {this.axis_changed()}, + z: function () {this.axis_changed()} + }, + + + ready: function () { + this.graphics(); + Vue.nextTick(this.update); + }, + + + methods: { + update: function () { + if (!this.state.selected) { + this.dirty = true; + this.scene = new THREE.Scene(); + + } else if (!this.toolpath.filename && !this.loading) { + this.loading = true; + this.dirty = true; + this.draw_loading(); + } + + if (!this.enabled || !this.toolpath.filename) return; + + function get(url) { + var d = $.Deferred(); + var xhr = new XMLHttpRequest(); + + xhr.open('GET', url + '?' + Math.random(), true); + xhr.responseType = 'arraybuffer'; + + xhr.onload = function (e) { + if (xhr.response) d.resolve(new Float32Array(xhr.response)); + else d.reject(); + }; + + xhr.send(); + + return d.promise(); + } + + var d1 = get('/api/path/' + this.toolpath.filename + '/positions'); + var d2 = get('/api/path/' + this.toolpath.filename + '/speeds'); + + $.when(d1, d2).done(function (positions, speeds) { + this.positions = positions + this.speeds = speeds; + this.loading = false; + + // Update scene + this.scene = new THREE.Scene(); + this.draw(this.scene); + this.snap(this.snapView); + + this.update_view(); + }.bind(this)) + }, + + + update_surface_mode: function (mode) { + if (!this.enabled) return; + + if (typeof this.surfaceMaterial != 'undefined') { + this.surfaceMaterial.wireframe = mode == 'wire'; + this.surfaceMaterial.needsUpdate = true; + } + + this.set_visible(this.surfaceMesh, mode == 'cut' || mode == 'wire'); + this.set_visible(this.workpieceMesh, mode == 'solid'); + }, + + + load_surface: function (surface) { + if (typeof surface == 'undefined') { + this.vertices = undefined; + this.normals = undefined; + return; + } + + this.vertices = surface.vertices; + + // Expand normals + this.normals = []; + for (var i = 0; i < surface.normals.length / 3; i++) + for (var j = 0; j < 3; j++) + for (var k = 0; k < 3; k++) + this.normals.push(surface.normals[i * 3 + k]); + }, + + + set_visible: function (target, visible) { + if (typeof target != 'undefined') target.visible = visible; + this.dirty = true; + }, + + + get_dims: function () { + var t = $(this.target); + var width = t.innerWidth(); + var height = t.innerHeight(); + return {width: width, height: height}; + }, + + + update_view: function () { + if (!this.enabled) return; + var dims = this.get_dims(); + + this.camera.aspect = dims.width / dims.height; + this.camera.updateProjectionMatrix(); + this.renderer.setSize(dims.width, dims.height); + + if (this.loading) { + this.controls.reset(); + this.camera.position.copy(new THREE.Vector3(0, 0, 600)); + this.camera.lookAt(new THREE.Vector3(0, 0, 0)); + } + + this.dirty = true; + }, + + + update_tool: function (tool) { + if (!this.enabled) return; + if (typeof tool == 'undefined') tool = this.toolView; + if (typeof tool == 'undefined') return; + tool.position.x = this.x.pos; + tool.position.y = this.y.pos; + tool.position.z = this.z.pos; + }, + + + update_envelope: function (envelope) { + if (!this.enabled || !this.axes.homed) return; + if (typeof envelope == 'undefined') envelope = this.envelopeView; + if (typeof envelope == 'undefined') return; + + var min = new THREE.Vector3(); + var max = new THREE.Vector3(); + + for (var axis of 'xyz') { + min[axis] = this[axis].min - this[axis].off; + max[axis] = this[axis].max - this[axis].off; + } + + var bounds = new THREE.Box3(min, max); + if (bounds.isEmpty()) envelope.geometry = this.create_empty_geom(); + else envelope.geometry = this.create_bbox_geom(bounds); + }, + + + axis_changed: function () { + this.update_tool(); + this.update_envelope(); + this.dirty = true; + }, + + + graphics: function () { + try { + // Renderer + this.renderer = new THREE.WebGLRenderer({antialias: true, alpha: true}); + this.renderer.setPixelRatio(window.devicePixelRatio); + this.renderer.setClearColor(0, 0); + this.target.appendChild(this.renderer.domElement); + + } catch (e) { + console.log('WebGL not supported: ', e); + return; + } + this.enabled = true; + + // Camera + this.camera = new THREE.PerspectiveCamera(45, 4 / 3, 1, 10000); + + // Lighting + this.ambient = new THREE.AmbientLight(0xffffff, 0.5); + + var keyLight = new THREE.DirectionalLight + (new THREE.Color('hsl(30, 100%, 75%)'), 0.75); + keyLight.position.set(-100, 0, 100); + + var fillLight = new THREE.DirectionalLight + (new THREE.Color('hsl(240, 100%, 75%)'), 0.25); + fillLight.position.set(100, 0, 100); + + var backLight = new THREE.DirectionalLight(0xffffff, 0.5); + backLight.position.set(100, 0, -100).normalize(); + + this.lights = new THREE.Group(); + this.lights.add(keyLight); + this.lights.add(fillLight); + this.lights.add(backLight); + + // Surface material + this.surfaceMaterial = this.create_surface_material(); + + // Controls + this.controls = new orbit(this.camera, this.renderer.domElement); + this.controls.enableDamping = true; + this.controls.dampingFactor = 0.2; + this.controls.rotateSpeed = 0.25; + this.controls.enableZoom = true; + + // Move lights with scene + this.controls.addEventListener('change', function (scope) { + return function () { + keyLight.position.copy(scope.camera.position); + fillLight.position.copy(scope.camera.position); + backLight.position.copy(scope.camera.position); + keyLight.lookAt(scope.controls.target); + fillLight.lookAt(scope.controls.target); + backLight.lookAt(scope.controls.target); + scope.dirty = true; + } + }(this)) + + // Events + window.addEventListener('resize', this.update_view, false); + + // Start it + this.render(); + }, + + + create_surface_material: function () { + return new THREE.MeshPhongMaterial({ + specular: 0x111111, + shininess: 10, + side: THREE.FrontSide, + color: 0x0c2d53 + }); + }, + + + draw_loading: function () { + this.scene = new THREE.Scene(); + + var geometry = new THREE.TextGeometry('Loading 3D View...', { + font: new THREE.Font(font), + size: 40, + height: 5, + curveSegments: 12, + bevelEnabled: true, + bevelThickness: 10, + bevelSize: 8, + bevelSegments: 5 + }); + geometry.computeBoundingBox(); + + var center = geometry.center(); + var mesh = new THREE.Mesh(geometry, this.surfaceMaterial); + + this.scene.add(mesh); + this.scene.add(this.ambient); + this.scene.add(this.lights); + this.update_view(); + }, + + + draw_workpiece: function (scene, material) { + if (typeof this.workpiece == 'undefined') return; + + var min = this.workpiece.min; + var max = this.workpiece.max; + + min = new THREE.Vector3(min[0], min[1], min[2]); + max = new THREE.Vector3(max[0], max[1], max[2]); + var dims = max.clone().sub(min); + + var geometry = new THREE.BoxGeometry(dims.x, dims.y, dims.z) + var mesh = new THREE.Mesh(geometry, material); + + var offset = dims.clone(); + offset.divideScalar(2); + offset.add(min); + + mesh.position.add(offset); + + geometry.computeBoundingBox(); + + scene.add(mesh); + + return mesh; + }, + + + draw_surface: function (scene, material) { + if (typeof this.vertices == 'undefined') return; + + var geometry = new THREE.BufferGeometry(); + + geometry.addAttribute + ('position', new THREE.Float32BufferAttribute(this.vertices, 3)); + geometry.addAttribute + ('normal', new THREE.Float32BufferAttribute(this.normals, 3)); + + geometry.computeBoundingSphere(); + geometry.computeBoundingBox(); + + return new THREE.Mesh(geometry, material); + }, + + + draw_tool: function (scene, bbox) { + // Tool size is relative to bounds + var size = bbox.getSize(new THREE.Vector3()); + var length = (size.x + size.y + size.z) / 24; + + if (length < 1) length = 1; + + var material = new THREE.MeshPhongMaterial({ + transparent: true, + opacity: 0.75, + specular: 0x161616, + shininess: 10, + color: 0xffa500 // Orange + }); + + var geometry = new THREE.CylinderGeometry(length / 2, 0, length, 128); + geometry.translate(0, length / 2, 0); + geometry.rotateX(0.5 * Math.PI); + + var mesh = new THREE.Mesh(geometry, material); + this.update_tool(mesh); + mesh.visible = this.showTool; + scene.add(mesh); + return mesh; + }, + + + draw_axis: function (axis, up, length, radius) { + var color; + + if (axis == 0) color = 0xff0000; // Red + else if (axis == 1) color = 0x00ff00; // Green + else if (axis == 2) color = 0x0000ff; // Blue + + var group = new THREE.Group(); + var material = new THREE.MeshPhongMaterial({ + specular: 0x161616, shininess: 10, color: color + }); + var geometry = new THREE.CylinderGeometry(radius, radius, length, 128); + geometry.translate(0, -length / 2, 0); + group.add(new THREE.Mesh(geometry, material)); + + geometry = new THREE.CylinderGeometry(1.5 * radius, 0, 2 * radius, 128); + geometry.translate(0, -length - radius, 0); + group.add(new THREE.Mesh(geometry, material)); + + if (axis == 0) group.rotateZ((up ? 0.5 : 1.5) * Math.PI); + else if (axis == 1) group.rotateX((up ? 0 : 1 ) * Math.PI); + else if (axis == 2) group.rotateX((up ? 1.5 : 0.5) * Math.PI); + + return group; + }, + + + draw_axes: function (scene, bbox) { + var size = bbox.getSize(new THREE.Vector3()); + var length = (size.x + size.y + size.z) / 3; + length /= 10; + + if (length < 1) length = 1; + + var radius = length / 20; + + var group = new THREE.Group(); + + for (var axis = 0; axis < 3; axis++) + for (var up = 0; up < 2; up++) + group.add(this.draw_axis(axis, up, length, radius)); + + group.visible = this.showAxes; + scene.add(group); + + return group; + }, + + + get_color: function (speed) { + if (isNaN(speed)) return [255, 0, 0]; // Rapid + + var intensity = speed / this.toolpath.maxSpeed; + if (typeof speed == 'undefined' || !this.showIntensity) intensity = 1; + return [0, 255 * intensity, 127 * (1 - intensity)]; + }, + + + draw_path: function (scene) { + var geometry = new THREE.BufferGeometry(); + var material = + new THREE.LineBasicMaterial({ + vertexColors: THREE.VertexColors, + linewidth: 1.5 + }); + + var positions = new THREE.Float32BufferAttribute(this.positions, 3); + geometry.addAttribute('position', positions); + + var colors = []; + for (var i = 0; i < this.speeds.length; i++) { + var color = this.get_color(this.speeds[i]); + Array.prototype.push.apply(colors, color); + } + + colors = new THREE.Uint8BufferAttribute(colors, 3, true); + geometry.addAttribute('color', colors); + + geometry.computeBoundingSphere(); + geometry.computeBoundingBox(); + + var line = new THREE.Line(geometry, material); + + line.visible = this.showPath; + scene.add(line); + + return line; + }, + + + create_empty_geom: function () { + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute('position', + new THREE.Float32BufferAttribute([], 3)); + return geometry; + }, + + + create_bbox_geom: function (bbox) { + var vertices = []; + + if (!bbox.isEmpty()) { + // Top + vertices.push(bbox.min.x, bbox.min.y, bbox.min.z); + vertices.push(bbox.max.x, bbox.min.y, bbox.min.z); + vertices.push(bbox.max.x, bbox.min.y, bbox.min.z); + vertices.push(bbox.max.x, bbox.min.y, bbox.max.z); + vertices.push(bbox.max.x, bbox.min.y, bbox.max.z); + vertices.push(bbox.min.x, bbox.min.y, bbox.max.z); + vertices.push(bbox.min.x, bbox.min.y, bbox.max.z); + vertices.push(bbox.min.x, bbox.min.y, bbox.min.z); + + // Bottom + vertices.push(bbox.min.x, bbox.max.y, bbox.min.z); + vertices.push(bbox.max.x, bbox.max.y, bbox.min.z); + vertices.push(bbox.max.x, bbox.max.y, bbox.min.z); + vertices.push(bbox.max.x, bbox.max.y, bbox.max.z); + vertices.push(bbox.max.x, bbox.max.y, bbox.max.z); + vertices.push(bbox.min.x, bbox.max.y, bbox.max.z); + vertices.push(bbox.min.x, bbox.max.y, bbox.max.z); + vertices.push(bbox.min.x, bbox.max.y, bbox.min.z); + + // Sides + vertices.push(bbox.min.x, bbox.min.y, bbox.min.z); + vertices.push(bbox.min.x, bbox.max.y, bbox.min.z); + vertices.push(bbox.max.x, bbox.min.y, bbox.min.z); + vertices.push(bbox.max.x, bbox.max.y, bbox.min.z); + vertices.push(bbox.max.x, bbox.min.y, bbox.max.z); + vertices.push(bbox.max.x, bbox.max.y, bbox.max.z); + vertices.push(bbox.min.x, bbox.min.y, bbox.max.z); + vertices.push(bbox.min.x, bbox.max.y, bbox.max.z); + } + + var geometry = new THREE.BufferGeometry(); + + geometry.addAttribute('position', + new THREE.Float32BufferAttribute(vertices, 3)); + + return geometry; + }, + + + draw_bbox: function (scene, bbox) { + var geometry = this.create_bbox_geom(bbox); + var material = new THREE.LineBasicMaterial({color: 0xffffff}); + var line = new THREE.LineSegments(geometry, material); + + line.visible = this.showBBox; + + scene.add(line); + + return line; + }, + + + draw_envelope: function (scene) { + var geometry = this.create_empty_geom(); + var material = new THREE.LineBasicMaterial({color: 0x00f7ff}); + var line = new THREE.LineSegments(geometry, material); + + line.visible = this.showBBox; + + scene.add(line); + this.update_envelope(line); + + return line; + }, + + + draw: function (scene) { + // Lights + scene.add(this.ambient); + scene.add(this.lights); + + // Model + this.pathView = this.draw_path(scene); + this.surfaceMesh = this.draw_surface(scene, this.surfaceMaterial); + this.workpieceMesh = this.draw_workpiece(scene, this.surfaceMaterial); + this.update_surface_mode(this.surfaceMode); + + // Compute bounding box + var bbox = this.get_model_bounds(); + + // Tool, axes & bounds + this.toolView = this.draw_tool(scene, bbox); + this.axesView = this.draw_axes(scene, bbox); + this.bboxView = this.draw_bbox(scene, bbox); + this.envelopeView = this.draw_envelope(scene); + }, + + + render: function () { + window.requestAnimationFrame(this.render); + if (typeof this.scene == 'undefined') return; + + if (this.controls.update() || this.dirty) { + this.dirty = false; + this.renderer.render(this.scene, this.camera); + } + }, + + + get_model_bounds: function () { + var bbox = new THREE.Box3(new THREE.Vector3(0, 0, 0), + new THREE.Vector3(0.00001, 0.00001, 0.00001)); + + function add(o) { + if (typeof o != 'undefined') { + var oBBox = new THREE.Box3(); + oBBox.setFromObject(o); + bbox.union(oBBox); + } + } + + add(this.pathView); + add(this.surfaceMesh); + add(this.workpieceMesh); + + return bbox; + }, + + + snap: function (view) { + if (this.loading) return; + if (view != this.snapView) { + this.snapView = view; + cookie.set('snap-view', view); + } + + var bbox = this.get_model_bounds(); + this.controls.reset(); + bbox.getCenter(this.controls.target); + this.update_view(); + + // Compute new camera position + var center = bbox.getCenter(new THREE.Vector3()); + var offset = new THREE.Vector3(); + + if (view == 'isometric') {offset.y -= 1; offset.z += 1;} + if (view == 'front') offset.y -= 1; + if (view == 'back') offset.y += 1; + if (view == 'left') offset.x -= 1; + if (view == 'right') offset.x += 1; + if (view == 'top') offset.z += 1; + if (view == 'bottom') offset.z -= 1; + offset.normalize(); + + // Initial camera position + var position = new THREE.Vector3().copy(center).add(offset); + this.camera.position.copy(position); + this.camera.lookAt(center); // Get correct camera orientation + + var theta = this.camera.fov / 180 * Math.PI; // View angle + var cameraLine = new THREE.Line3(center, position); + var cameraUp = new THREE.Vector3().copy(this.camera.up) + .applyQuaternion(this.camera.quaternion); + var cameraLeft = + new THREE.Vector3().copy(offset).cross(cameraUp).normalize(); + + var corners = [ + new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.min.z), + new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.max.z), + new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.min.z), + new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.max.z), + new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.min.z), + new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.max.z), + new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.min.z), + new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.max.z), + ] + + var dist = this.camera.near; // Min camera dist + + for (var i = 0; i < corners.length; i++) { + // Project on to camera line + var p1 = cameraLine + .closestPointToPoint(corners[i], false, new THREE.Vector3()); + + // Compute distance from projection to center + var d = p1.distanceTo(center); + if (cameraLine.closestPointToPointParameter(p1, false) < 0) d = -d; + + // Compute up line + var up = + new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraUp)); + + // Project on to up line + var p2 = up.closestPointToPoint(corners[i], false, new THREE.Vector3()); + + // Compute length + var l = p1.distanceTo(p2); + + // Update min camera distance + dist = Math.max(dist, d + l / Math.tan(theta / 2)); + + // Compute left line + var left = + new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraLeft)); + + // Project on to left line + var p3 = + left.closestPointToPoint(corners[i], false, new THREE.Vector3()); + + // Compute length + l = p1.distanceTo(p3); + + // Update min camera distance + dist = Math.max(dist, d + l / Math.tan(theta / 2) / this.camera.aspect); + } + + this.camera.position.copy(offset.multiplyScalar(dist * 1.2).add(center)); + } + }, + + + mixins: [require('./axis-vars')] +} diff --git a/src/js/settings-view.js b/src/js/settings-view.js new file mode 100644 index 0000000..599aea8 --- /dev/null +++ b/src/js/settings-view.js @@ -0,0 +1,42 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +module.exports = { + template: '#settings-view-template', + props: ['config', 'template'], + + + events: { + 'input-changed': function() { + this.$dispatch('config-changed'); + return false; + } + } +} diff --git a/src/js/sock.js b/src/js/sock.js new file mode 100644 index 0000000..5c274cd --- /dev/null +++ b/src/js/sock.js @@ -0,0 +1,127 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +var Sock = function (url, retry, timeout) { + if (!(this instanceof Sock)) return new Sock(url, retry); + + if (typeof retry == 'undefined') retry = 2000; + if (typeof timeout == 'undefined') timeout = 16000; + + this.url = url; + this.retry = retry; + this.timeout = timeout; + this.divisions = 4; + this.count = 0; + + this.connect(); +} + + +Sock.prototype.onmessage = function () {} +Sock.prototype.onopen = function () {} +Sock.prototype.onclose = function () {} + + +Sock.prototype.connect = function () { + console.debug('connecting to', this.url); + this.close(); + + this._sock = new SockJS(this.url); + + this._sock.onmessage = function (e) { + console.debug('msg:', e.data); + this.heartbeat('msg'); + this.onmessage(e); + }.bind(this); + + + this._sock.onopen = function () { + console.debug('connected'); + this.heartbeat('open'); + this.onopen(); + }.bind(this); + + + this._sock.onclose = function () { + console.debug('disconnected'); + this._cancel_timeout(); + + this.onclose(); + if (typeof this._sock != 'undefined') + setTimeout(this.connect.bind(this), this.retry); + }.bind(this); +} + + +Sock.prototype._timedout = function () { + // Divide timeout so slow browser doesn't trigger timeouts when the + // connection is good. + if (this.divisions <= ++this.count) { + console.debug('connection timedout'); + this._timeout = undefined; + this._sock.close(); + + } else this._set_timeout(); +} + + +Sock.prototype._cancel_timeout = function () { + clearTimeout(this._timeout); + this._timeout = undefined; + this.count = 0; +} + + +Sock.prototype._set_timeout = function () { + this._timeout = setTimeout(this._timedout.bind(this), + this.timeout / this.divisions); +} + + +Sock.prototype.heartbeat = function (msg) { + //console.debug('heartbeat ' + new Date().toLocaleTimeString() + ' ' + msg); + this._cancel_timeout(); + this._set_timeout(); +} + + +Sock.prototype.close = function () { + if (typeof this._sock != 'undefined') { + var sock = this._sock; + this._sock = undefined; + sock.close(); + } +} + + +Sock.prototype.send = function (msg) {this._sock.send(msg)} + + +module.exports = Sock diff --git a/src/js/templated-input.js b/src/js/templated-input.js new file mode 100644 index 0000000..32d80eb --- /dev/null +++ b/src/js/templated-input.js @@ -0,0 +1,89 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +module.exports = { + replace: true, + template: '#templated-input-template', + props: ['name', 'model', 'template'], + + + data: function () {return {view: ''}}, + + + computed: { + metric: function () {return this.$root.metric()}, + + + _view: function () { + if (this.template.scale) { + if (this.metric) return 1 * this.model.toFixed(3); + + return 1 * (this.model / this.template.scale).toFixed(4); + } + + return this.model; + }, + + + units: function () { + return (this.metric || !this.template.iunit) ? + this.template.unit : this.template.iunit; + }, + + + title: function () { + var s = 'Default ' + this.template.default + ' ' + + (this.template.unit || ''); + if (typeof this.template.help != 'undefined') + s = this.template.help + '\n' + s; + return s; + } + }, + + + watch: { + _view: function () {this.view = this._view}, + + + view: function () { + if (this.template.scale && !this.metric) + this.model = this.view * this.template.scale; + else this.model = this.view; + } + }, + + + ready: function () {this.view = this._view}, + + + methods: { + change: function () {this.$dispatch('input-changed')} + } +} diff --git a/src/js/tool-view.js b/src/js/tool-view.js new file mode 100644 index 0000000..958205d --- /dev/null +++ b/src/js/tool-view.js @@ -0,0 +1,152 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict'; + +var api = require('./api'); +var modbus = require('./modbus.js'); + + +module.exports = { + template: '#tool-view-template', + props: ['config', 'template', 'state'], + + + data: function () { + return { + address: 0, + value: 0 + } + }, + + + components: {'modbus-reg': require('./modbus-reg.js')}, + + + watch: { + 'state.mr': function () {this.value = this.state.mr} + }, + + + events: { + 'input-changed': function() { + this.$dispatch('config-changed'); + return false; + } + }, + + + ready: function () {this.value = this.state.mr}, + + + computed: { + regs_tmpl: function () {return this.template['modbus-spindle'].regs}, + tool_type: function () {return this.config.tool['tool-type'].toUpperCase()}, + + + is_modbus: function () { + return this.tool_type != 'DISABLED' && this.tool_type != 'PWM SPINDLE' + }, + + + modbus_status: function () {return modbus.status_to_string(this.state.mx)} + }, + + + methods: { + get_reg_type: function (reg) { + return this.regs_tmpl.template['reg-type'].values[this.state[reg + 'vt']] + }, + + + get_reg_addr: function (reg) {return this.state[reg + 'va']}, + get_reg_value: function (reg) {return this.state[reg + 'vv']}, + + + get_reg_fails: function (reg) { + var fails = this.state[reg + 'vr'] + return fails == 255 ? 'Max' : fails; + }, + + + show_modbus_field: function (key) { + return key != 'regs' && + (key != 'multi-write' || this.tool_type == 'CUSTOM MODBUS VFD'); + }, + + + read: function (e) { + e.preventDefault(); + api.put('modbus/read', {address: this.address}); + }, + + + write: function (e) { + e.preventDefault(); + api.put('modbus/write', {address: this.address, value: this.value}); + }, + + + customize: function (e) { + e.preventDefault(); + this.config.tool['tool-type'] = 'Custom Modbus VFD'; + + var regs = this.config['modbus-spindle'].regs; + for (var i = 0; i < regs.length; i++) { + var reg = this.regs_tmpl.index[i]; + regs[i]['reg-type'] = this.get_reg_type(reg); + regs[i]['reg-addr'] = this.get_reg_addr(reg); + regs[i]['reg-value'] = this.get_reg_value(reg); + } + + this.$dispatch('config-changed'); + }, + + + clear: function (e) { + e.preventDefault(); + this.config.tool['tool-type'] = 'Custom Modbus VFD'; + + var regs = this.config['modbus-spindle'].regs; + for (var i = 0; i < regs.length; i++) { + regs[i]['reg-type'] = 'disabled'; + regs[i]['reg-addr'] = 0; + regs[i]['reg-value'] = 0; + } + + this.$dispatch('config-changed'); + }, + + + reset_failures: function (e) { + e.preventDefault(); + var regs = this.config['modbus-spindle'].regs; + for (var reg = 0; reg < regs.length; reg++) + this.$dispatch('send', '\$' + reg + 'vr=0'); + } + } +} diff --git a/src/js/unit-value.js b/src/js/unit-value.js new file mode 100644 index 0000000..d4d6ee0 --- /dev/null +++ b/src/js/unit-value.js @@ -0,0 +1,58 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +'use strict' + + +module.exports = { + replace: true, + template: '{{text}}{{metric ? unit : iunit}}', + props: ['value', 'precision', 'unit', 'iunit', 'scale'], + + + computed: { + metric: function () {return !this.$root.state.imperial}, + + + text: function () { + var value = this.value; + if (typeof value == 'undefined') return ''; + + if (!this.metric) value /= this.scale; + + return (1 * value.toFixed(this.precision)).toLocaleString(); + } + }, + + + ready: function () { + if (typeof this.precision == 'undefined') this.precision = 0; + if (typeof this.unit == 'undefined') this.unit = 'mm'; + if (typeof this.iunit == 'undefined') this.iunit = 'in'; + if (typeof this.scale == 'undefined') this.scale = 25.4; + } +} diff --git a/src/pug/index.pug b/src/pug/index.pug new file mode 100644 index 0000000..8fdb348 --- /dev/null +++ b/src/pug/index.pug @@ -0,0 +1,213 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +doctype html +html(lang="en") + head + meta(charset="utf-8") + meta(name="viewport", content="width=device-width, initial-scale=1.0") + + title Onefinity CNC - Web interface + + style: include ../static/css/pure-min.css + style: include ../static/css/side-menu.css + + style: include ../static/css/font-awesome.min.css + style: include ../static/css/Audiowide.css + style: include ../static/css/clusterize.css + style: include:stylus ../stylus/style.styl + + + body(v-cloak) + #overlay(v-if="status != 'connected'") + span {{status}} + #layout + a#menuLink.menu-link(href="#menu"): span + + #menu + button.save.pure-button.button-success(:disabled="!modified", + @click="save") Save + + .pure-menu + ul.pure-menu-list + li.pure-menu-heading + a.pure-menu-link(href="#control") Control + + li.pure-menu-heading + a.pure-menu-link(href="#settings") Settings + + li.pure-menu-heading + a.pure-menu-link(href="#motor:0") Motors + + li.pure-menu-item(v-for="motor in config.motors") + a.pure-menu-link(:href="'#motor:' + $index") Motor {{$index}} + + li.pure-menu-heading + a.pure-menu-link(href="#tool") Tool + + li.pure-menu-heading + a.pure-menu-link(href="#io") I/O + + li.pure-menu-heading + a.pure-menu-link(href="#admin-general") Admin + + li.pure-menu-item + a.pure-menu-link(href="#admin-general") General + + li.pure-menu-item + a.pure-menu-link(href="#admin-network") Network + + li.pure-menu-heading + a.pure-menu-link(href="#cheat-sheet") Cheat Sheet + + li.pure-menu-heading + a.pure-menu-link(href="#help") Help + + #main + .header + .header-content + .banner + img(src="/images/onefinity_logo.png") + .title + .fa.fa-thermometer-full(class="error", + v-if="80 <= state.rpi_temp", + title="Raspberry Pi temperature too high.") + .subtitle + | CNC Controller #[b {{state.demo ? 'Demo ' : ''}}] + | v{{config.version}} + a.upgrade-version(v-if="show_upgrade()", href="#admin-general") + | Upgrade to v{{latestVersion}} + .fa.fa-check(v-if="!show_upgrade() && latestVersion", + title="Firmware up to date") + .p {{get_ip_address()}} {{get_ssid()}} + + .estop(:class="{active: state.es}") + estop(@click="estop") + + .video(title="Plug camera into USB.\n" + + "Left click to toggle video size.\n" + + "Right click to toggle crosshair.", @click="toggle_video", + @contextmenu="toggle_crosshair", :class="video_size") + .crosshair(v-if="crosshair") + .vertical + .horizontal + .box + img(src="/api/video") + + .clear + + .content(class="{{currentView}}-view") + component(:is="currentView + '-view'", :index="index", + :config="config", :template="template", :state="state", keep-alive) + + message.error-message(:show.sync="errorShow") + div(slot="header") + .estop(:class="{active: state.es}"): estop(@click="estop") + h3 ERROR: {{errorMessage}} + + div(slot="body") + console + + button.pure-button(@click="block_error_dialog") + .fa.fa-ban + |  Stop + label showing errors for + input(style="width: 50px", v-model="errorTimeout", number) + label seconds. + + div(slot="footer") + button.pure-button.pure-button-primary(@click="errorShow = false") Ok + + message(:show.sync="confirmUpgrade") + h3(slot="header") Upgrade Firmware? + div(slot="body") + p + | Are you sure you want to upgrade the firmware to version + | {{latestVersion}}? + + p.pure-control-group + label(for="pass") Password + input(name="pass", v-model="password", type="password", + @keyup.enter="upgrade_confirmed") + + div(slot="footer") + button.pure-button(@click="confirmUpgrade=false") Cancel + button.pure-button.pure-button-primary(@click="upgrade_confirmed") + | Upgrade + + message(:show.sync="confirmUpload") + h3(slot="header") Upload Firmware? + div(slot="body") + p Are you sure you want to upload firmware #[em {{firmwareName}}]? + + p.pure-control-group + label(for="pass") Password + input(name="pass", v-model="password", type="password", + @keyup.enter="upload_confirmed") + + div(slot="footer") + button.pure-button(@click="confirmUpload=false") Cancel + button.pure-button.pure-button-primary(@click="upload_confirmed") + | Upload + + message(:show.sync="firmwareUpgrading") + h3(slot="header") Firmware upgrading + div(slot="body") + h3 Please wait... + p Loss of power during an upgrade may damage the controller. + div(slot="footer") + + message(v-if="popupMessages.length", :show="true") + h3(slot="header") GCode message + + div(slot="body") + ul + li(v-for="msg in popupMessages", track-by="$index") {{msg}} + + div(slot="footer") + button.pure-button.button-success(v-if="state.xx != 'HOLDING'", + @click="close_messages('ok')") OK + + div(v-if="state.xx == 'HOLDING'") + button.pure-button(@click="close_messages('stop')") + | Stop + .fa.fa-stop + + button.pure-button(@click="close_messages('continue')") + | Continue + .fa.fa-play + + #templates: include ../../build/templates.pug + iframe#download-target(style="display:none") + + script: include ../static/js/jquery-1.11.3.min.js + script: include ../static/js/vue.js + script: include ../static/js/sockjs.min.js + script: include ../static/js/clusterize.min.js + script: include ../static/js/three.min.js + script: include:browserify ../js/main.js + script: include ../static/js/ui.js diff --git a/src/pug/templates/admin-general-view.pug b/src/pug/templates/admin-general-view.pug new file mode 100644 index 0000000..00a9818 --- /dev/null +++ b/src/pug/templates/admin-general-view.pug @@ -0,0 +1,68 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#admin-general-view-template(type="text/x-template") + #admin-general + h2 Firmware + button.pure-button.pure-button-primary(@click="check") Check + //button.pure-button.pure-button-primary(@click="upgrade") Upgrade + label.pure-button.pure-button-primary(@click="upload_firmware") Upload + form.upload-firmware.file-upload + input(type="file", accept=".bz2", @change="upload") + + p + input(type="checkbox", v-model="autoCheckUpgrade", + @change="change_auto_check_upgrade") + label(for="auto-check-upgrade")   Automatically check for upgrades + + h2 Configuration + button.pure-button.pure-button-primary(@click="backup") Backup + + label.pure-button.pure-button-primary(@click="restore_config") Restore + form.restore-config.file-upload + input(type="file", accept=".json", @change="restore") + message(:show.sync="configRestored") + h3(slot="header") Success + p(slot="body") Configuration restored. + + button.pure-button.pure-button-primary(@click="confirmReset = true") Reset + message(:show.sync="confirmReset") + h3(slot="header") Reset to default configuration? + p(slot="body") Non-network configuration changes will be lost. + div(slot="footer") + button.pure-button(@click="confirmReset = false") Cancel + button.pure-button.button-success(@click="reset") OK + + message(:show.sync="configReset") + h3(slot="header") Success + p(slot="body") Configuration reset. + + h2 Debugging + a(href="/api/log", target="_blank") + button.pure-button.pure-button-primary View Log + a(href="/api/bugreport") + button.pure-button.pure-button-primary Bug Report diff --git a/src/pug/templates/admin-network-view.pug b/src/pug/templates/admin-network-view.pug new file mode 100644 index 0000000..a1c328a --- /dev/null +++ b/src/pug/templates/admin-network-view.pug @@ -0,0 +1,130 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#admin-network-view-template(type="text/x-template") + #admin-network + h2 Hostname + .pure-form.pure-form-aligned + .pure-control-group + label(for="hostname") Hostname + input(name="hostname", v-model="hostname", @keyup.enter="set_hostname") + button.pure-button.pure-button-primary(@click="set_hostname") Set + + message(:show.sync="hostnameSet") + h3(slot="header") Hostname Set + div(slot="body") + p Hostname was successfuly set to #[strong {{hostname}}]. + p Rebooting to apply changes. + p Redirecting to new hostname in {{redirectTimeout}} seconds. + div(slot="footer") + + h2 Remote SSH User + .pure-form.pure-form-aligned + .pure-control-group + label(for="username") Username + input(name="username", v-model="username", @keyup.enter="set_username") + button.pure-button.pure-button-primary(@click="set_username") Set + + .pure-form.pure-form-aligned + .pure-control-group + label(for="current") Current Password + input(name="current", v-model="current", type="password") + .pure-control-group + label(for="pass1") New Password + input(name="pass1", v-model="password", type="password") + .pure-control-group + label(for="pass2") New Password + input(name="pass2", v-model="password2", type="password") + button.pure-button.pure-button-primary(@click="set_password") Set + + message(:show.sync="passwordSet") + h3(slot="header") Password Set + p(slot="body") + + message(:show.sync="usernameSet") + h3(slot="header") Username Set + p(slot="body") + + h2 Wifi Setup + .pure-form.pure-form-aligned + .pure-control-group + label(for="wifi_mode") Mode + select(name="wifi_mode", v-model="wifi_mode", + title="Select client or access point mode") + option(value="disabled") Disabled + option(value="client") Client + option(value="ap") Access Point + button.pure-button.pure-button-primary(@click="wifiConfirm = true", + v-if="wifi_mode == 'disabled'") Set + + .pure-control-group(v-if="wifi_mode == 'ap'") + label(for="wifi_ch") Channel + select(name="wifi_ch", v-model="wifi_ch") + each ch in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + option(value=ch)= ch + + .pure-control-group(v-if="wifi_mode != 'disabled'") + label(for="ssid") Network (SSID) + input(name="ssid", v-model="wifi_ssid") + + .pure-control-group(v-if="wifi_mode != 'disabled'") + label(for="wifi_pass") Password + input(name="wifi_pass", v-model="wifi_pass", type="password") + button.pure-button.pure-button-primary(@click="wifiConfirm = true") Set + + p(v-if="wifi_mode != 'disabled'"). + WARNING: WiFi may be unreliable in an electrically noisy environment + such as a machine shop. + + message.wifi-confirm(:show.sync="wifiConfirm") + h3(slot="header") Configure Wifi and reboot? + div(slot="body") + p + | After configuring the Wifi settings the controller will + | automatically reboot. + table + tr + th Mode + td  {{wifi_mode}} + tr(v-if="wifi_mode == 'ap'") + th Channel + td  {{wifi_ch}} + tr(v-if="wifi_mode != 'disabled'") + th SSID + td  {{wifi_ssid}} + tr(v-if="wifi_mode != 'disabled'") + th Auth + td  {{wifi_pass ? 'WPA2' : 'Open'}} + + div(slot="footer") + button.pure-button(@click="wifiConfirm = false") Cancel + button.pure-button.button-success(@click="config_wifi") OK + + message(:show.sync="rebooting") + h3(slot="header") Rebooting + p(slot="body") Please wait... + div(slot="footer") diff --git a/src/pug/templates/axis-control.pug b/src/pug/templates/axis-control.pug new file mode 100644 index 0000000..77764b9 --- /dev/null +++ b/src/pug/templates/axis-control.pug @@ -0,0 +1,232 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#axis-control-template(type="text/x-template") + svg(xmlns="http://www.w3.org/2000/svg", + xmlns:xlink="http://www.w3.org/1999/xlink", + width="100%", viewBox="0 0 300 300") + defs + lineargradient#red + stop(offset="0", stop-color="#d26969") + stop(offset="1", stop-color="#ff7f7f") + + lineargradient#green + stop(offset="0", stop-color="#69d269") + stop(offset="1", stop-color="#7fff7f") + + lineargradient#blue + stop(offset="0", stop-color="#6969d2") + stop(offset="1", stop-color="#7f7fff") + + lineargradient#orange + stop(offset="0", stop-color="#d29d69") + stop(offset="1", stop-color="#ffbf7f") + + lineargradient#cyan + stop(offset="0", stop-color="#69d2d2") + stop(offset="1", stop-color="#7fffff") + + lineargradient#purple + stop(offset="0", stop-color="#d269d2") + stop(offset="1", stop-color="#ff7fff") + + + each color in 'red green blue orange cyan purple'.split(' ') + lineargradient(xlink:href="#" + color, id=color + "-1", + gradientunits="userSpaceOnUse", gradienttransform="rotate(180 7 5)", + x1="0", y1="0", x2="15", y2="10") + + lineargradient(xlink:href="#" + color, id=color + "-2", + gradientunits="userSpaceOnUse", x1="10", y1="10", x2="40", y2="40") + + + filter#shadow(x="-50%" y="-50%" width="200%" height="200%") + feOffset(in="SourceAlpha", dx="3", dy="3") + feComponentTransfer + feFuncR(type="discrete", tableValues="0.05") + feFuncG(type="discrete", tableValues="0.05") + feFuncB(type="discrete", tableValues="0.05") + feGaussianBlur(result="shadow", stdDeviation="5") + feBlend(in="SourceGraphic", in2="shadow", mode="normal") + + path#pie-1(d="M107,0 83,0 0,83 0,107A107,107 0 0 0 107,0Z") + path#pie-2(d="M83,0 59,0 0,59 0,83A83,83 0 0 0 83,0Z") + path#pie-3(d="M59,0 35,0 0,35 0,59A59,59 0 0 0 59,0Z") + path#pie-4(d="M35,0 0,0 0,35A35,35 0 0 0 35,0Z") + + path#arrow(d="M-16,9 0,9 0,17 17,0 0,-17 0,-9 -16,-9 -16,9Z") + + circle#centrebutton(cx="0",cy="0",r="15",fill="green") + + + g(transform="scale(1.0, 1.0)") + // 100% ring + g.ring(fill="#9f9f9f", filter="url(#shadow)") + use.button(xlink:href="#pie-1", v-if="enabled[0]", + transform="translate(134 121) rotate(-45)", + @mousedown="jog(0, 3, 1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 3, 1)", @touchend.prevent="release(0)") + + use.button(xlink:href="#pie-1", v-if="enabled[0]", + transform="translate(115 121) rotate(135)", + @mousedown="jog(0, 3, -1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 3, -1)", @touchend.prevent="release(0)") + + g.button(v-if="enabled[1]", + @mousedown="jog(1, 3, 1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 3, 1)", @touchend.prevent="release(1)") + use.button(xlink:href="#pie-1", + transform="translate(124 111) rotate(-135)") + text(x="125", y="21", transform="rotate(22 125 125)") {{text(3)}} + text(x="125", y="21", transform="rotate(-22 125 125)") {{text(3)}} + use.button(xlink:href="#pie-1", fill="transparent", + transform="translate(124 111) rotate(-135)") + + use.button(xlink:href="#pie-1", v-if="enabled[1]", + transform="translate(124 130) rotate(45)", + @mousedown="jog(1, 3, -1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 3, -1)", @touchend.prevent="release(1)") + + // 50% ring + g.ring(fill="#c5c5c5", filter="url(#shadow)") + use.button(xlink:href="#pie-2", v-if="enabled[0]", + transform="translate(134 121) rotate(-45)", + @mousedown="jog(0, 2, 1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 2, 1)", @touchend.prevent="release(0)") + + use.button(xlink:href="#pie-2", v-if="enabled[0]", + transform="translate(115 121) rotate(135)", + @mousedown="jog(0, 2, -1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 2, -1)", @touchend.prevent="release(0)") + + g.button(v-if="enabled[1]", + @mousedown="jog(1, 2, 1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 2, 1)", @touchend.prevent="release(1)") + use.button(xlink:href="#pie-2", + transform="translate(124 111) rotate(-135)") + text(x="125", y="48") {{text(2)}} + use.button(xlink:href="#pie-2", fill="transparent", + transform="translate(124 111) rotate(-135)") + + use.button(xlink:href="#pie-2", v-if="enabled[1]", + transform="translate(124 130) rotate(45)", + @mousedown="jog(1, 2, -1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 2, -1)", @touchend.prevent="release(1)") + + + // 25% ring + g.ring(fill="#e2e2e2", filter="url(#shadow)") + use.button(xlink:href="#pie-3", v-if="enabled[0]", + transform="translate(134 121) rotate(-45)", + @mousedown="jog(0, 1, 1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 1, 1)", @touchend.prevent="release(0)") + + use.button(xlink:href="#pie-3", v-if="enabled[0]", + transform="translate(115 121) rotate(135)", + @mousedown="jog(0, 1, -1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 1, -1)", @touchend.prevent="release(0)") + + g.button(v-if="enabled[1]", + @mousedown="jog(1, 1, 1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 1, 1)", @touchend.prevent="release(1)") + use.button(xlink:href="#pie-3", + transform="translate(124 111) rotate(-135)") + text(x="125", y="69") {{text(1)}} + use.button(xlink:href="#pie-3", fill="transparent", + transform="translate(124 111) rotate(-135)") + + use.button(xlink:href="#pie-3", v-if="enabled[1]", + transform="translate(124 130) rotate(45)", + @mousedown="jog(1, 1, -1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 1, -1)", @touchend.prevent="release(1)") + + + // 10% ring + g.ring(fill="#f7f7f7", filter="url(#shadow)") + use.button(xlink:href="#pie-4", v-if="enabled[0]", + transform="translate(134 121) rotate(-45)", + @mousedown="jog(0, 0, 1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 0, 1)", @touchend.prevent="release(0)") + + use.button(xlink:href="#pie-4", v-if="enabled[0]", + transform="translate(115 121) rotate(135)", + @mousedown="jog(0, 0, -1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 0, -1)", @touchend.prevent="release(0)") + + g.button(v-if="enabled[1]", + @mousedown="jog(1, 0, 1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 0, 1)", @touchend.prevent="release(1)") + use.button(xlink:href="#pie-4", + transform="translate(124 111) rotate(-135)") + text(x="125", y="91") {{text(0)}} + use.button(xlink:href="#pie-4", fill="transparent", + transform="translate(124 111) rotate(-135)") + + use.button(xlink:href="#pie-4", v-if="enabled[1]", + transform="translate(124 130) rotate(45)", + @mousedown="jog(1, 0, -1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 0, -1)", @touchend.prevent="release(1)") + + + // +A + g.button.arrow(v-if="enabled[0]", transform="translate(230 120)", + @mousedown="jog(0, 3, 1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 3, 1)", @touchend.prevent="release(0)") + use(xlink:href="#arrow", fill="url(#{{colors[0]}}-1)") + text(x="-12", y="5", font-size="14", textLength="21") +{{axes[0]}} + + + // -A + g.button.arrow(v-if="enabled[0]", transform="translate(20 120)", + @mousedown="jog(0, 3, -1)", @mouseup="release(0)", + @touchstart.prevent="jog(0, 3, -1)", @touchend.prevent="release(0)") + use(xlink:href="#arrow", fill="url(#{{colors[0]}}-1)", + transform="rotate(180)") + text(x="-8", y="5", font-size="14", textLength="16") -{{axes[0]}} + + + // +B + g.button.arrow(v-if="enabled[1]", transform="translate(125, 18)", + @mousedown="jog(1, 3, 1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 3, 1)", @touchend.prevent="release(1)") + use(xlink:href="#arrow", fill="url(#{{colors[1]}}-1)", + transform="rotate(-90)") + text(x="-8", y="5", font-size="12", textLength="16") +{{axes[1]}} + + + // -B + g.button.arrow(v-if="enabled[1]", transform="translate(125, 225)", + @mousedown="jog(1, 3, -1)", @mouseup="release(1)", + @touchstart.prevent="jog(1, 3, -1)", @touchend.prevent="release(1)") + use(xlink:href="#arrow", fill="url(#{{colors[1]}}-1)", + transform="rotate(90)") + text(x="-7", y="5", font-size="12", textLength="14") -{{axes[1]}} + + // Return to zero button + g.button.cicle(@mouseup="back2zero(0,1)",transform="translate(124,121)") + use(xlink:href="#centrebutton") + text(x="-12",y="3",font-size="9") {{axes[0]}}0{{axes[1]}}0 diff --git a/src/pug/templates/cheat-sheet-view.pug b/src/pug/templates/cheat-sheet-view.pug new file mode 100644 index 0000000..4f63351 --- /dev/null +++ b/src/pug/templates/cheat-sheet-view.pug @@ -0,0 +1,591 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#cheat-sheet-view-template(type="text/x-template") + // Modified from http://linuxcnc.org/docs/html/gcode.html + - var base = 'http://linuxcnc.org/docs/html/gcode'; + - var camotics_base = 'https://camotics.org/gcode.html'; + + .cheat-sheet + h2 GCode Cheat Sheet + + table + tr + th Code + th Parameters + th Description + + tr.spacer-row: th + tr.header-row + th(colspan='3') Motion + tr + td + a(href=`${base}/g-code.html#gcode:g0`) G0 + td + td Rapid Move + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g1`) G1 + td + td Linear Move + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g2-g3`) G2, G3 + td I J K or R, P + td Arc Move + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g4`) G4 + td P + td Dwell + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g5`) G5 + td I J P Q + td Cubic Spline + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g5.1`) G5.1 + td I J + td Quadratic Spline + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g5.2-g5.3`) G5.2 + td P L + td NURBS + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g33.1`) G33.1 + td K + td Rigid Tapping + + tr.spacer-row: th + tr.header-row + th(colspan='3') Homing & Probing + tr + td + a(target="_blank", href=`${camotics_base}#gcodes-g28_2-28_3`) + | G28.2, G28.3 + td + td (Un)set Axis Homed State + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g38`) G38.2 - G38.5 + td + td Straight Probe + tr + td + a(target="_blank", href=`${camotics_base}#gcodes-g38_6-38_9`) + | G38.6 - G38.9 + td + td Seek Switch + + tr.spacer-row: th + tr.header-row + th(colspan='3') Tool Control + tr + td + a(href=`${base}/other-code.html#sec:select-tool`) T + td + td Select Tool + tr + td + a(target="_blank", href=`${base}/m-code.html#mcode:m6`) M6 + td T + td Tool Change + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m61`) M61 + td Q + td Set Current Tool + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g10-l1`) G10 L1 + td P Q R + td Set Tool Table + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g10-l10`) G10 L10 + td P + td Set Tool Table + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g10-l11`) G10 L11 + td P + td Set Tool Table + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g43`) G43 + td H + td Tool Length Offset + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g43.1`) G43.1 + td + td Dynamic Tool Length Offset + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g43.2`) G43.2 + td H + td Apply additional Tool Length Offset + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g49`) G49 + td + td Cancel Tool Length Compensation + + tr.spacer-row: th + tr.header-row + th(colspan='3') Feed Control + tr + td + a(href=`${base}/other-code.html#sec:set-feed-rate`) F + td + td Set Feed Rate + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g93-g94-g95`) + | G93, G94, G95 + td + td Feed Rate Mode + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m52`) M52 + td P0 (off) or P1 (on) + td Adaptive Feed Control + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m53`) M53 + td P0 (off) or P1 (on) + td Feed Stop Control + + tr.spacer-row: th + tr.header-row + th(colspan='3') Spindle Control + tr + td + a(href=`${base}/other-code.html#sec:set-spindle-speed`) S + td + td Set Spindle Speed + tr + td + a(target="_blank", href=`${base}/m-code.html#mcode:m3-m4-m5`) + | M3, M4, M5 + td S + td Spindle Control + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m19`) M19 + td + td Orient Spindle + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g96-g97`) G96, G97 + td S D + td Spindle Control Mode + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g33`) G33 + td K + td Spindle Synchronized Motion + + tr.spacer-row: th + tr.header-row + th(colspan='3') Coolant + tr + td + a(target="_blank", href=`${base}/m-code.html#mcode:m7-m8-m9`) + | M7, M8, M9 + td + td Coolant Control + + tr.spacer-row: th + tr.header-row + th(colspan='3') Stopping + tr + td + a(target="_blank", href=`${base}/m-code.html#mcode:m0-m1`) M0, M1 + td + td Program Pause + tr + td + a(target="_blank", href=`${base}/m-code.html#mcode:m2-m30`) M2, M30 + td + td Program End + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m60`) M60 + td + td Pallet Change Pause + + tr.spacer-row: th + tr.header-row + th(colspan='3') Units + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g20-g21`) G20, G21 + td + td Units (inch, mm) + + tr.spacer-row: th + tr.header-row + th(colspan='3') Distance Mode + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g90-g91`) G90, G91 + td + td Distance Mode + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g90.1-g91.1`) + | G90.1, G91.1 + td + td Arc Distance Mode + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g7`) G7 + td + td Lathe Diameter Mode + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g8`) G8 + td + td Lathe Radius Mode + + tr.spacer-row.unimplemented(v-if="showUnimplemented"): th + tr.header-row.unimplemented(v-if="showUnimplemented") + th(colspan='3') Cutter Radius Compensation + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g40`) G40 + td + td Compensation Off + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g41-g42`) G41,G42 + td D + td Cutter Compensation + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g41.1-g42.1`) + | G41.1, G42.1 + td D L + td Dynamic Cutter Compensation + + tr.spacer-row: th + tr.header-row + th(colspan='3') Path Control Mode + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g61-g61.1`) + | G61 G61.1 + td + td Exact Path Mode + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g64`) G64 + td P Q + td Path Blending (Partial support) + + tr.spacer-row.unimplemented(v-if="showUnimplemented"): th + tr.header-row.unimplemented(v-if="showUnimplemented") + th(colspan='3') Overrides + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m48-m49`) M48, M49 + td + td Speed and Feed Override Control + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m50`) M50 + td P0 (off) or P1 (on) + td Feed Override Control + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m51`) M51 + td P0 (off) or P1 (on) + td Spindle Speed Override Control + + tr.spacer-row: th + tr.header-row + th(colspan='3') Coordinate Systems, Offsets & Planes + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g54-g59.3`) + | G54-G59.3 + td + td Select Coordinate System + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g10-l2`) G10 L2 + td P R + td Set Coordinate System + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g10-l20`) G10 L20 + td P + td Set Coordinate System + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g52`) G52 + td + td Local Coordinate System Offset + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g53`) G53 + td + td Move in Machine Coordinates + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g92`) G92 + td + td Coordinate System Offset + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g92.1-g92.2`) + | G92.1, G92.2 + td + td Reset G92 Offsets + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g92.3`) G92.3 + td + td Restore G92 Offsets + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g28-g28.1`) + | G28, G28.1 + td + td Go/Set Predefined Position + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g30-g30.1`) + | G30, G30.1 + td + td Go/Set Predefined Position + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g17-g19.1`) + | G17 - G19.1 + td (affects G2, G3, G81…G89, G40…G42) + td Plane Select + + tr.spacer-row: th + tr.header-row + th(colspan='3') Flow-control Codes + tr + td + a(target="_blank", href=`${base}/o-code.html#ocode:subroutines`) + | o sub/endsub/call + td + td Subroutines, sub/endsub call + tr + td + a(target="_blank", href=`${base}/o-code.html#ocode:looping`) o while + td + td Looping, while/endwhile do/while + tr + td + a(target="_blank", href=`${base}/o-code.html#ocode:conditional`) o if + td + td Conditional, if/else/endif + tr + td + a(target="_blank", href=`${base}/o-code.html#ocode:repeat`) o repeat + td + td Repeat a loop of code + tr + td + a(target="_blank", href=`${base}/o-code.html#ocode:indirection`) [] + td + td Indirection + tr + td + a(target="_blank", href=`${base}/o-code.html#ocode:calling-files`) + | o call + td + td Call named or numbered file + + tr.spacer-row: th + tr.header-row + th(colspan='3') Modal State + tr + td + a(target="_blank", href=`${base}/m-code.html#mcode:m70`) M70 + td + td Save modal state + tr + td + a(target="_blank", href=`${base}/m-code.html#mcode:m71`) M71 + td + td Invalidate stored state + tr + td + a(target="_blank", href=`${base}/m-code.html#mcode:m72`) M72 + td + td Restore modal state + tr + td + a(target="_blank", href=`${base}/m-code.html#mcode:m73`) M73 + td + td Save and Auto-restore modal state + + tr.spacer-row.unimplemented(v-if="showUnimplemented"): th + tr.header-row.unimplemented(v-if="showUnimplemented") + th(colspan='3') Input/Output + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m62-m65`) M62 - M65 + td P + td Digital Output Control + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m66`) M66 + td P E L Q + td Wait on Input + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m67`) M67 + td T + td Analog Output,Synchronized + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m68`) M68 + td T + td Analog Output, Immediate + + tr.spacer-row.unimplemented(v-if="showUnimplemented"): th + tr.header-row.unimplemented(v-if="showUnimplemented") + th(colspan='3') User Defined Commands + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/m-code.html#mcode:m100-m199`) + | M101 - M199 + td P Q + td User Defined Commands + + tr.spacer-row: th + tr.header-row + th(colspan='3') Canned cycles + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g80`) G80 + td + td Cancel Canned Cycle + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g81`) G81 + td R L (P) + td Drilling Cycle + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g82`) G82 + td R L (P) + td Drilling Cycle, Dwell + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g83`) G83 + td R L Q + td Drilling Cycle, Peck + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g73`) G73 + td R L Q + td Drilling Cycle, Chip Breaking + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g85`) G85 + td R L (P) + td Boring Cycle, Feed Out + tr + td + a(target="_blank", href=`${base}/g-code.html#gcode:g89`) G89 + td R L (P) + td Boring Cycle, Dwell, Feed Out + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g76`) G76 + td P Z I J R K Q H L E + td Threading Cycle + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/g-code.html#gcode:g98-g99`) G98, G99 + td + td Canned Cycle Return Level + + tr.spacer-row: th + tr.header-row + th(colspan='3') Comments & Messages + tr + td + a(target="_blank", href=`${base}/overview.html#gcode:comments`) ; (…) + td + td Comments + tr + td + a(target="_blank", href=`${base}/overview.html#gcode:messages`) + | (MSG,…) + td + td Messages + tr + td + a(target="_blank", href=`${base}/overview.html#gcode:debug`) (DEBUG,…) + td + td Debug Messages + tr.unimplemented(v-if="showUnimplemented") + td + a(target="_blank", href=`${base}/overview.html#gcode:print`) (PRINT,…) + td + td Print Messages + tr + td + a(target="_blank", href=`${base}/overview.html#_logging`) (LOG,…) + td + td Logging Messages + + div + input(type="checkbox", v-model="showUnimplemented") + label Show unsupported codes + + h2 Further GCode Programming Documentation + + p + | The Buildbotics controller implements a subset of LinuxCNC GCode. + | Supported commands are listed above. You can find further help with + | #[a(href="http://wikipedia.com/wiki/G-code", target="_blank") GCode] + | programming on the LinuxCNC website: + + ul + li: a(href="http://linuxcnc.org/docs/html/gcode/overview.html", + target="_blank") + | G Code overview + li: a(href="http://linuxcnc.org/docs/html/gcode/g-code.html", + target="_blank") + | G Code reference + li: a(href="http://linuxcnc.org/docs/html/gcode/m-code.html", + target="_blank") + | M Code reference diff --git a/src/pug/templates/console.pug b/src/pug/templates/console.pug new file mode 100644 index 0000000..bc8df6c --- /dev/null +++ b/src/pug/templates/console.pug @@ -0,0 +1,49 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#console-template(type="text/x-template") + .console + .toolbar + button.pure-button(title="Clear console.", @click="clear") + .fa.fa-trash + |  Clear + + .console-wrapper + table + tr + th Level + th Source + th Location + th Repeat + th Message + + tr(v-for="msg in messages", class="log-{{msg.level || 'info'}}") + td {{msg.level || 'info'}} + td {{msg.source || ''}} + td {{msg.where || ''}} + td {{msg.repeat}} + td.message {{msg.msg}} diff --git a/src/pug/templates/control-view.pug b/src/pug/templates/control-view.pug new file mode 100644 index 0000000..26696ae --- /dev/null +++ b/src/pug/templates/control-view.pug @@ -0,0 +1,390 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#control-view-template(type="text/x-template") + #control + + table(width="99%") + tr + td(style="white-space: nowrap;") + table(table-layout="fixed") + colgroup + col(style="width:100px") + col(style="width:100px") + col(style="width:100px") + col(style="width:5px") + col(style="width:100px") + tr + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="jog_fn(-1,1,0,0)") ⬁ + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="jog_fn(0,1,0,0)") Y+ + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="jog_fn(1,1,0,0)") ⬀ + td + td(style="height:100px",align="center") + button(style="height:100px;width:100px",,@click="jog_fn(0,0,1,0)") Z+ + tr + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="jog_fn(-1,0,0,0)") X- + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="goto_zero(1,1,0,0)") + .fa.fa-bullseye + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="jog_fn(1,0,0,0)") X+ + td + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="goto_zero(0,0,1,0)") Z0 + tr + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="jog_fn(-1,-1,0,0)") ⬃ + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="jog_fn(0,-1,0,0)") Y- + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="jog_fn(1,-1,0,0)") ⬂ + td + td(style="height:100px",align="center") + button(style="height:100px;width:100px",@click="jog_fn(0,0,-1,0)") Z- + td(width="99%") + table.axes + tr(:class="axes.klass") + th.name Axis + th.position Position + th.absolute Absolute + th.offset Offset + th.state State + th.actions + button.pure-button(:disabled="!can_set_axis", + title="Zero all axis offsets.", @click="zero()",style="height:60px;width:60px") ∅ + + button.pure-button(title="Home all axes.", @click="home()", + :disabled="!is_idle",style="height:60px;width:60px") + .fa.fa-home + + each axis in 'xyzabc' + tr.axis(:class=`${axis}.klass`, v-if=`${axis}.enabled`, + :title=`${axis}.title`) + th.name= axis + td.position: unit-value(:value=`${axis}.pos`, precision=4) + td.absolute: unit-value(:value=`${axis}.abs`, precision=3) + td.offset: unit-value(:value=`${axis}.off`, precision=3) + td.state + .fa(:class=`'fa-' + ${axis}.icon`) + | {{#{axis}.state}} + + th.actions + button.pure-button(:disabled="!can_set_axis", + title=`Set {{'${axis}' | upper}} axis position.`, + @click=`show_set_position('${axis}')`,style="height:60px;width:60px") + .fa.fa-cog + + button.pure-button(:disabled="!can_set_axis", + title=`Zero {{'${axis}' | upper}} axis offset.`, + @click=`zero('${axis}')`,style="height:60px;width:60px") ∅ + + button.pure-button(:disabled="!is_idle", @click=`home('${axis}')`, + title=`Home {{'${axis}' | upper}} axis.`,style="height:60px;width:60px") + .fa.fa-home + + message(:show.sync=`position_msg['${axis}']`) + h3(slot="header") Set {{'#{axis}' | upper}} axis position + + div(slot="body") + .pure-form + .pure-control-group + label Position + input(v-model="axis_position", + @keyup.enter=`set_position('${axis}', axis_position)`) + p + + div(slot="footer") + button.pure-button(@click=`position_msg['${axis}'] = false`) + | Cancel + + button.pure-button(v-if=`${axis}.homed`, + @click=`unhome('${axis}')`) Unhome + + button.pure-button.button-success( + @click=`set_position('${axis}', axis_position)`) Set + + + message(:show.sync=`manual_home['${axis}']`) + h3(slot="header") Manually home {{'#{axis}' | upper}} axis + + div(slot="body") + p Set axis absolute position. + + .pure-form + .pure-control-group + label Absolute + input(v-model="axis_position", + @keyup.enter=`set_home('${axis}', axis_position)`) + + p + + div(slot="footer") + button.pure-button(@click=`manual_home['${axis}'] = false`) + | Cancel + + button.pure-button.button-success( + title=`Home {{'${axis}' | upper}} axis.`, + @click=`set_home('${axis}', axis_position)`) Set + tr + td(style="white-space: nowrap;") + button(style="height:100px;width:100px;font-weight:normal",id="jog_button_fine",@click=`set_jog_incr('fine')`) 0.1 + button(style="height:100px;width:100px;font-weight:bold",id="jog_button_small",@click=`set_jog_incr('small')`) 1.0 + button(style="height:100px;width:100px;font-weight:normal",id="jog_button_medium",@click=`set_jog_incr('medium')`) 10 + button(style="height:100px;width:100px;font-weight:normal",id="jog_button_large",@click=`set_jog_incr('large')`) 100 + td(rowspan="2") + + table(width="100%") + tr + td(style="text-align:center") + table.info + tr + th State + td(:class="{attention: highlight_state}") {{mach_state}} + + tr + th Message + td.message(:class="{attention: highlight_state}") + | {{message.replace(/^#/, '')}} + + tr(title="Active machine units") + th Units + td.mach_units + select(v-model="mach_units", :disabled="!is_idle") + option(value="METRIC") METRIC + option(value="IMPERIAL") IMPERIAL + + tr(title="Active tool") + th Tool + td {{state.tool || 0}} + + td + table.info + tr( + title="Current velocity in {{metric ? 'meters' : 'inches'}} per minute") + th Velocity + td + unit-value(:value="state.v", precision="2", unit="", iunit="", + scale="0.0254") + | {{metric ? ' m/min' : ' IPM'}} + + tr(title="Programmed feed rate.") + th Feed + td + unit-value(:value="state.feed", precision="2", unit="", iunit="") + | {{metric ? ' mm/min' : ' IPM'}} + + tr(title="Programed and actual speed.") + th Speed + td + | {{state.speed || 0 | fixed 0}} + span(v-if="!isNaN(state.s)")  ({{state.s | fixed 0}}) + = ' RPM' + + tr(title="Load switch states.") + th Loads + td + span(:class="state['1oa'] ? 'load-on' : ''") + | 1:{{state['1oa'] ? 'On' : 'Off'}} + |   + span(:class="state['2oa'] ? 'load-on' : ''") + | 2:{{state['2oa'] ? 'On' : 'Off'}} + + td + table.info + tr + th Remaining + td(title="Total run time (days:hours:mins:secs)"). + #[span(v-if="plan_time_remaining") {{plan_time_remaining | time}} of] + {{toolpath.time | time}} + tr + th ETA + td.eta {{eta}} + tr + th Line + td + | {{0 <= state.line ? state.line : 0 | number}} + span(v-if="toolpath.lines") + |  of {{toolpath.lines | number}} + tr + th Progress + td.progress + label {{(progress || 0) | percent}} + .bar(:style="'width:' + (progress || 0) * 100 + '%'") + tr + td(style="white-space: nowrap;text-align:center") + button(style="height:100px;width:200px",@click="tool_msg = true",) Probe XYZ + + message(:show.sync=`tool_msg`) + h3(slot="header") Enter probe tool information + + div(slot="body") + .pure-form + .pure-control-group + label="{{metric ? 'Diameter (mm)' : 'Diameter (inches)'}}" + input(v-model="tool_diameter", + @keyup.enter=`set_tool_diameter(tool_diameter)`) + p + + div(slot="footer") + button.pure-button(@click=`tool_msg = false`) + | Cancel + + button.pure-button.button-success( + @click=`set_tool_diameter(tool_diameter)`) Set + + + button(style="height:100px;width:200px",@click="probe_z()") Probe Z + + .tabs + + input#tab1(type="radio", name="tabs",checked="" @click="tab = 'auto'") + label(for="tab1", title="Run GCode programs",style="height:50px;width:100px") Auto + + input#tab2(type="radio", name="tabs", @click="tab = 'mdi'") + label(for="tab2", title="Manual GCode entry",style="height:50px;width:100px") MDI + + input#tab3(type="radio", name="tabs", @click="tab = 'messages'") + label(for="tab3",style="height:50px;width:100px") Messages + + input#tab4(type="radio", name="tabs", @click="tab = 'indicators'") + label(for="tab4",style="height:50px;width:100px") Indicators + + + + + section#content1.tab-content.pure-form + .toolbar.pure-control-group + button.pure-button(:class="{'attention': is_holding}", + title="{{is_running ? 'Pause' : 'Start'}} program.", + @click="start_pause", :disabled="!state.selected", + style="height:100px;width:100px;font-weight:normal") + .fa(:class="is_running ? 'fa-pause' : 'fa-play'") + + button.pure-button(title="Stop program.", @click="stop", style="height:100px;width:100px;font-weight:normal") + .fa.fa-stop + + button.pure-button(title="Pause program at next optional stop (M1).", + @click="optional_pause", v-if="false", style="height:100px;width:100px;font-weight:normal") + .fa.fa-stop-circle-o + + button.pure-button(title="Execute one program step.", @click="step", + :disabled="(!is_ready && !is_holding) || !state.selected", + v-if="false", style="height:100px;width:100px;font-weight:normal") + .fa.fa-step-forward + + button.pure-button(title="Upload a new GCode program.", @click="open", + :disabled="!is_ready",style="height:100px;width:100px;font-weight:normal") + .fa.fa-folder-open + + form.gcode-file-input.file-upload + input(type="file", @change="upload", :disabled="!is_ready", + accept="text/*,.nc,.gcode,.gc,.ngc,.txt,.tap,.cnc") + + a.pure-button(:disabled="!state.selected", download, + :href="'/api/file/' + state.selected", + title="Download the selected GCode program.",style="height:100px;width:100px") + br + br + .fa.fa-download + + button.pure-button(title="Delete current GCode program.", + @click="deleteGCode = true", + :disabled="!state.selected || !is_ready",style="height:100px;width:100px;font-weight:normal") + .fa.fa-trash + + message(:show.sync="deleteGCode") + h3(slot="header") Delete GCode? + p(slot="body") + div(slot="footer") + button.pure-button(@click="deleteGCode = false") Cancel + button.pure-button.button-error(@click="delete_all") + .fa.fa-trash + |  all + button.pure-button.button-success(@click="delete_current") + .fa.fa-trash + |  selected + + select(title="Select previously uploaded GCode programs.", + v-model="state.selected", @change="load", :disabled="!is_ready") + option(v-for="file in state.files", :value="file") {{file}} + + .progress(v-if="toolpath_progress && toolpath_progress < 1", + title="Simulating GCode to check for errors, calculate ETA and " + + "generate 3D view. You can run GCode before the simulation " + + "finishes.") + div(:style="'width:' + (toolpath_progress || 0) * 100 + '%'") + label Simulating {{(toolpath_progress || 0) | percent}} + + path-viewer(:toolpath="toolpath", :state="state", :config="config") + gcode-viewer + + section#content2.tab-content + .mdi.pure-form(title="Manual GCode entry.") + button.pure-button(:disabled="!can_mdi", + :class="{'attention': is_holding}", + title="{{is_running ? 'Pause' : 'Start'}} command.", + @click="mdi_start_pause",style="height:100px;width:100px") + .fa(:class="is_running ? 'fa-pause' : 'fa-play'") + + button.pure-button(title="Stop command.", @click="stop",style="height:100px;width:100px") + .fa.fa-stop + + input(v-model="mdi", :disabled="!can_mdi", @keyup.enter="submit_mdi") + + .history(:class="{placeholder: !history}") + span(v-if="!history.length") MDI history displays here. + ul + li(v-for="item in history", @click="load_history($index)", + track-by="$index") + | {{item}} + + section#content3.tab-content + console + + section#content4.tab-content + indicators(:state="state", :template="template") + + + + + .override(title="Feed rate override.") + label Feed + input(type="range", min="0", max="2", step="0.01", + v-model="feed_override", @change="override_feed") + span.percent {{feed_override | percent 0}} + + .override(title="Spindle speed override.") + label Speed + input(type="range", min="0", max="2", step="0.01", + v-model="speed_override", @change="override_speed") + span.percent {{speed_override | percent 0}} + + diff --git a/src/pug/templates/estop.pug b/src/pug/templates/estop.pug new file mode 100644 index 0000000..2af3df0 --- /dev/null +++ b/src/pug/templates/estop.pug @@ -0,0 +1,118 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#estop-template(type="text/x-template") + svg(version="1.1", xmlns:svg="http://www.w3.org/2000/svg", + xmlns="http://www.w3.org/2000/svg", + xmlns:xlink="http://www.w3.org/1999/xlink", + width="130", height="130") + defs + path#text-path-1(d="m 73.735,673.129 c 0,55.107 44.673,99.780 99.780,99.780 55.107,0 99.780,-44.673 99.780,-99.780 0,-55.107 -44.673,-99.780 -99.780,-99.780 -55.107,0 -99.780,44.673 -99.780,99.780 z") + + path#text-path-2(d="m 258.714,673.129 c 0,47.053 -38.144,85.198 -85.198,85.198 -47.053,0 -85.198,-38.144 -85.198,-85.198 0,-47.053 38.144,-85.198 85.198,-85.198 47.053,0 85.198,38.144 85.198,85.198 z") + + filter#filter5134(style="color-interpolation-filters:sRGB") + feflood(flood-opacity="0.431", flood-color="rgb(0,0,0)") + fecomposite(in2="SourceGraphic", operator="in") + fegaussianblur(stddeviation="4", result="blur") + feoffset(dx="4", dy="4", result="offset") + fecomposite(in="SourceGraphic", in2="offset", operator="over", + result="fbSourceGraphic") + fecolormatrix(result="fbSourceGraphicAlpha", in="fbSourceGraphic", + values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0") + feflood(flood-opacity="0.431", flood-color="rgb(0,0,0)", + in="fbSourceGraphic") + fecomposite(in2="fbSourceGraphic", operator="out") + fegaussianblur(stddeviation="4", result="blur") + feoffset(dx="-4", dy="-4", result="offset") + fecomposite(in2="fbSourceGraphic", in="offset", operator="atop", + result="fbSourceGraphic") + fecolormatrix(result="fbSourceGraphicAlpha", in="fbSourceGraphic", + values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0") + feflood(flood-opacity="0.431", flood-color="rgb(0,0,0)", + in="fbSourceGraphic") + fecomposite(in2="fbSourceGraphic", operator="in") + fegaussianblur(stddeviation="4", result="blur") + feoffset(dx="4", dy="4", result="offset") + fecomposite(in2="offset", in="fbSourceGraphic", operator="over") + + filter#filter5158(style="color-interpolation-filters:sRGB",) + feflood(flood-opacity="0.431", flood-color="rgb(0,0,0)") + fecomposite(in2="SourceGraphic", operator="out") + fegaussianblur(stddeviation="7", result="blur") + feoffset(dx="4", dy="4") + fecomposite(in2="SourceGraphic", operator="atop") + + filter#filter5266(style="color-interpolation-filters:sRGB") + feflood(flood-opacity="0.372", flood-color="rgb(0,0,0)") + fecomposite(in2="SourceGraphic", operator="in") + fegaussianblur(stddeviation="2", result="blur") + feoffset(dx="1", dy="1", result="offset") + fecomposite(in="SourceGraphic", in2="offset", operator="over") + + filter#filter5278(style="color-interpolation-filters:sRGB") + feflood(flood-opacity="0.372", flood-color="rgb(0,0,0)") + fecomposite(in2="SourceGraphic", operator="out") + fegaussianblur(stddeviation="2", result="blur") + feoffset(dx="2", dy="2", result="offset") + fecomposite(in="offset", in2="SourceGraphic", operator="atop") + + + g(transform="scale(0.6, 0.6), translate(-65, -526)") + // Yellow ring + circle.ring(style="fill:#f5e138;filter:url(#filter5266)", + cx="173", cy="633", r="100") + + // Text + g(style="font-weight:bold;font-size:20px;font-family:Sans;fill:#666") + text(transform="matrix(-0.733,-0.679,0.679,-0.733,-156.726,1250.703)") + textpath(xlink:href="#text-path-2") EMERGENCY + + text(transform="matrix(0.276,-0.961,0.961,0.276,-523.818,609.026)") + textpath(xlink:href="#text-path-1") STOP + + g.button + circle(style="fill:#c42626;filter:url(#filter5134)", + cx="173", cy="633", r="74") + + // Inner circle + circle(style="fill:#c42626;filter:url(#filter5158)", + cx="173", cy="633", r="37") + + // Arrows + g(transform="matrix(0.327,0,0,0.327,50.806,478.966)", style="stroke:#fff;stroke-width:13;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;filter:url(#filter5278);fill:none") + g + path(transform="matrix(1.946,0,0,1.946,-359.987,-383.373)", d="m 411.696,521.147 c -10.574,4.472 -22.200,6.945 -34.404,6.945 -12.203,0 -23.830,-2.473 -34.404,-6.945 -10.574,-4.472 -20.097,-10.944 -28.095,-18.942 -7.997,-7.997 -12.913,-13.504 -17.386,-24.078") + path(d="m 221.801,582.657 -2.991,-35.524 28.482,14.166 z") + + g(transform="matrix(-0.5,-0.866,0.866,-0.5,149.811,1033.447)") + path(transform="matrix(1.946,0,0,1.946,-359.987,-383.373)", d="m 411.696,521.147 c -10.574,4.472 -22.200,6.945 -34.404,6.945 -12.203,0 -23.830,-2.473 -34.404,-6.945 -10.574,-4.472 -20.097,-10.944 -28.095,-18.942 -7.997,-7.997 -11.525,-14.205 -17.386,-24.078") + path(d="m 221.801,582.657 -2.991,-35.524 28.482,14.166 z") + + g(transform="matrix(-0.5,0.866,-0.866,-0.5,971.430,383.185)") + path(transform="matrix(1.946,0,0,1.946,-359.987,-383.373)", d="m 411.696,521.147 c -10.574,4.472 -22.200,6.945 -34.404,6.945 -12.203,0 -23.830,-2.473 -34.404,-6.945 -10.574,-4.472 -20.097,-10.944 -28.095,-18.942 -7.997,-7.997 -9.562,-8.624 -14.034,-19.199") + path(d="m 221.801,582.657 -2.991,-35.524 28.482,14.166 z") diff --git a/src/pug/templates/gcode-viewer.pug b/src/pug/templates/gcode-viewer.pug new file mode 100644 index 0000000..4a00617 --- /dev/null +++ b/src/pug/templates/gcode-viewer.pug @@ -0,0 +1,32 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#gcode-viewer-template(type="text/x-template") + .gcode + .clusterize + .clusterize-scroll + ul.clusterize-content diff --git a/src/pug/templates/help-view.pug b/src/pug/templates/help-view.pug new file mode 100644 index 0000000..4c1b677 --- /dev/null +++ b/src/pug/templates/help-view.pug @@ -0,0 +1,75 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#help-view-template(type="text/x-template") + #help + h2 Contact + p + | You can contact us here + | + a(href="https://onefinitycnc.com/support", target="_blank") + | onefinitycnc.com/support + |. + + h2 Discussion Forum + p + | Check out our support and discussion forum. + | + a(href="https://forum.onefinitycnc.com", target="_blank") + | forum.onefinitycnc.com + |. Register on the site and post a message. We can't wait to hear from you. + + h2 Buildbotics + p + | This controller is based on the Buildbotics CNC Controller. + | We encourage you to check out the + a(href="http://buildbotics.com", target="_blank") Buildbotics Website + |. + + h2 CAD/CAM Software + p + a(href="http://wikipedia.com/wiki/Computer-aided_manufacturing", + target="_blank") CAM + | + | software can be used to create GCode + | automatically from + | + a(href="http://wikipedia.com/wiki/Computer-aided_design", + target="_blank") CAD + | + | models. Here are a few CAD/CAM resources: + ul + li: a(href="http://camotics.org/", target="_blank") + | CAMotics - Open-Source CNC Simulator + li: a(href="http://librecad.org/", target="_blank") + | LibreCAD - Open-Source 2D CAD + li: a(href="https://www.freecadweb.org/", target="_blank") + | FreeCAD - Open-Source 3D CAD + li: a(href="http://www.openscad.org/", target="_blank") + | OpenSCAD - Open-Source 3D CAD for programmers + li: a(href="http://wiki.linuxcnc.org/cgi-bin/wiki.pl?Cam", + target="_blank") LinuxCNC CAM resources diff --git a/src/pug/templates/indicators.pug b/src/pug/templates/indicators.pug new file mode 100644 index 0000000..8414680 --- /dev/null +++ b/src/pug/templates/indicators.pug @@ -0,0 +1,264 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#indicators-template(type="text/x-template") + .indicators + table.legend + tr + th.header(colspan=100) Legend + + tr + td + .fa.fa-plus-circle.io + th Hi/+3.3v + th.separator + td + .fa.fa-minus-circle.io + th Lo/Gnd + th.separator + td + .fa.fa-circle.io.active + th Active + th.separator + td + .fa.fa-circle.io.inactive + th Inactive + th.separator + td + .fa.fa-circle-o.io + th Tristated/Disabled + + table.inputs + tr + th.header(colspan=7) Inputs + + tr + th State + th Pin + th Name + th.separator + th State + th Pin + th Name + + each motor in '0123' + tr + td: io-indicator(name=`min-switch-${motor}`, :state="state") + td {{get_min_pin(#{motor})}} + th Motor #{motor} Min + th.separator + td: io-indicator(name=`max-switch-${motor}`, :state="state") + td {{get_max_pin(#{motor})}} + th Motor #{motor} Max + + tr + td: io-indicator(name="estop", :state="state") + td 23 + th EStop + th.separator + td: io-indicator(name="probe", :state="state") + td 22 + th Probe + + tr(v-if="false") + td {{state['1ai'] | percent 0}} + td 24 + th Analog 1 + th.separator + td {{state['2ai'] | percent 0}} + td 18 + th Analog 2 + + table.outputs + tr + th.header(colspan=7) Outputs + + tr + th State + th Pin + th Name + th.separator + th State + th Pin + th Name + + tr + td: io-indicator(name="tool-enable-mode", :state="state") + td 15 + th Tool Enable + th.separator + td: io-indicator(name="load-1", :state="state") + td 2 + th Load 1 + + tr + td: io-indicator(name="tool-direction-mode", :state="state") + td 16 + th Tool Direction + th.separator + td: io-indicator(name="load-2", :state="state") + td 1 + th Load 2 + + tr + td {{state.pd | percent 0}} + td 17 + th Tool PWM + th.separator + td: io-indicator(name="fault", :state="state") + td 21 + th Fault + + table.pwr_fault + tr + th.header(colspan=5) + | Power Faults + span(v-if="state.pwr_version")  (Version {{state.pwr_version}}) + tr + th(:class="{error: state.under_voltage}") Under voltage + td(:class="{error: state.under_voltage}") + | {{state.under_voltage ? 'True' : 'False'}} + th.separator + th(:class="{error: state.over_voltage}") Over voltage + td(:class="{error: state.over_voltage}") + | {{state.over_voltage ? 'True' : 'False'}} + tr + th(:class="{error: state.over_current}") Over current + td(:class="{error: state.over_current}") + | {{state.over_current ? 'True' : 'False'}} + th.separator + th(:class="{error: state.sense_error}", :title="sense_error") + | Sense error + td(:class="{error: state.sense_error}") + | {{state.sense_error ? 'True' : 'False'}} + tr + th(:class="{error: state.shunt_overload}") Shunt overload + td(:class="{error: state.shunt_overload}") + | {{state.shunt_overload ? 'True' : 'False'}} + th.separator + th(:class="{error: state.shunt_error}") Shunt error + td(:class="{error: state.shunt_error}") + | {{state.shunt_error ? 'True' : 'False'}} + tr + th(:class="{error: state.load1_shutdown}") Load 1 shutdown + td(:class="{error: state.load1_shutdown}") + | {{state.load1_shutdown ? 'True' : 'False'}} + th.separator + th(:class="{error: state.load2_shutdown}") Load 2 shutdown + td(:class="{error: state.load2_shutdown}") + | {{state.load2_shutdown ? 'True' : 'False'}} + tr + th(:class="{error: state.motor_under_voltage}") Motor under volt + td(:class="{error: state.motor_under_voltage}") + | {{state.motor_under_voltage ? 'True' : 'False'}} + th.separator + th(:class="{error: state.motor_overload}") Motor overload + td(:class="{error: state.motor_overload}") + | {{state.motor_overload ? 'True' : 'False'}} + + tr + th(:class="{error: state.power_shutdown}") Power shutdown + td(:class="{error: state.power_shutdown}") + | {{state.power_shutdown ? 'True' : 'False'}} + th.separator + th + td + + table.motor_fault + tr + th.header(colspan=99) + | Motor Faults + .fa(:class="motor_fault_class()", title="General motor driver fault") + + tr + th Motor + th(title="Overtemperature fault"): .fa.fa-thermometer-full + th(title="Overcurrent motor channel A") A #[.fa.fa-bolt] + th(title="Predriver fault motor channel A") + | A #[.fa.fa-exclamation-triangle] + th(title="Overcurrent motor channel B") B #[.fa.fa-bolt] + th(title="Predriver fault motor channel B") + | B #[.fa.fa-exclamation-triangle] + th(title="Driver communication failure"): .fa.fa-handshake-o + th(title="Reset all motor flags") + .fa.fa-eraser(@click="motor_reset()") + + tr(v-for="motor in [0, 1, 2, 3]") + td {{motor}} + td: .fa(:class="motor_fault_class(motor, 0)", + title="Overtemperature fault") + td: .fa(:class="motor_fault_class(motor, 1)", + title="Overcurrent motor channel A") + td: .fa(:class="motor_fault_class(motor, 3)", + title="Predriver fault motor channel A") + td: .fa(:class="motor_fault_class(motor, 2)", + title="Overcurrent motor channel B") + td: .fa(:class="motor_fault_class(motor, 4)", + title="Predriver fault motor channel B") + td: .fa(:class="motor_fault_class(motor, 8)", + title="Driver communication failure") + td(:title="'Reset motor ' + motor + ' flags'") + .fa.fa-eraser(@click="motor_reset(motor)") + + table.measurements + tr + th.header(colspan=5) Measurements + + tr + td {{state.vin | fixed 1}} V + th Input + th.separator + td {{state.vout | fixed 1}} V + th Output + + tr + td {{state.motor | fixed 2}} A + th Motor + th.separator + td {{state.vdd | fixed 2}} A + th Low-side + + tr + td {{state.load1 | fixed 2}} A + th Load 1 + th.separator + td {{state.load2 | fixed 2}} A + th Load 2 + + tr + td {{state.temp | fixed 0}} ℃ + th Temp + th.separator + td(:class="{'error': 80 <= state.rpi_temp}") + | {{state.rpi_temp | fixed 0}} ℃ + th RPi Temp + + h2 DB25 breakout box + img(src="images/DB25_breakout_box.png") + + h2 DB25-M2 breakout + img(src="images/DB25-M2_breakout.png") diff --git a/src/pug/templates/io-indicator.pug b/src/pug/templates/io-indicator.pug new file mode 100644 index 0000000..8baf875 --- /dev/null +++ b/src/pug/templates/io-indicator.pug @@ -0,0 +1,29 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#io-indicator-template(type="text/x-template") + .fa.io(:class="klass", :title="tooltip") diff --git a/src/pug/templates/io-view.pug b/src/pug/templates/io-view.pug new file mode 100644 index 0000000..ffb3800 --- /dev/null +++ b/src/pug/templates/io-view.pug @@ -0,0 +1,49 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#io-view-template(type="text/x-template") + #io + h1 I/O Configuration + + .pure-form.pure-form-aligned + fieldset + h2 Switches + templated-input(v-for="templ in template.switches", :name="$key", + :model.sync="config.switches[$key]", :template="templ") + + label.extra(slot="extra", v-if="templ.pin") + | Pin {{templ.pin}} + io-indicator(:name="$key", :state="state") + + fieldset + h2 Outputs + templated-input(v-for="templ in template.outputs", :name="$key", + :model.sync="config.outputs[$key]", :template="templ") + + label.extra(slot="extra") + | Pin {{templ.pin}} + io-indicator(:name="$key", :state="state") diff --git a/src/pug/templates/message.pug b/src/pug/templates/message.pug new file mode 100644 index 0000000..dcaf93c --- /dev/null +++ b/src/pug/templates/message.pug @@ -0,0 +1,40 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#message-template(type="text/x-template") + .modal-mask(v-show="show", transition="modal") + .modal-wrapper + .modal-container + .modal-header + slot(name="header") default header + + .modal-body + slot(name="body") default body + + .modal-footer + slot(name="footer") + button.pure-button.button-success(@click="show = false") OK diff --git a/src/pug/templates/modbus-reg-view.pug b/src/pug/templates/modbus-reg-view.pug new file mode 100644 index 0000000..ac86cfd --- /dev/null +++ b/src/pug/templates/modbus-reg-view.pug @@ -0,0 +1,45 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#modbus-reg-view-template(type="text/x-template") + tr.modbus-reg + td.reg-index {{index}} + td.reg-type + select(v-model="model['reg-type']", @change="change") + option(v-for="opt in template['reg-type']['values']", :value="opt") + | {{opt}} + + td.reg-addr + input(v-model="model['reg-addr']", @change="change", type="text", + :min="template['reg-addr'].min", :max="template['reg-addr'].max", + pattern="[0-9]*", :disabled="model['reg-type'] == 'disabled'", + number) + + td.reg-value + input(v-model="model['reg-value']", @change="change", type="text", + :min="template['reg-value'].min", :max="template['reg-value'].max", + pattern="[0-9]*", :disabled="!has_user_value", number) diff --git a/src/pug/templates/motor-view.pug b/src/pug/templates/motor-view.pug new file mode 100644 index 0000000..a0ea712 --- /dev/null +++ b/src/pug/templates/motor-view.pug @@ -0,0 +1,73 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#motor-view-template(type="text/x-template") + .motor(:class="{slave: is_slave}") + h1 Motor {{index}} Configuration + + .pure-form.pure-form-aligned + fieldset(v-for="category in template.motors.template", :class="$key") + h2 {{$key}} + + templated-input(v-for="templ in category",v-if="show($key, templ)", :name="$key", + :model.sync="motor[$key]", :template="templ") + + label.extra(v-if="$key == 'microsteps'", slot="extra", + title="Microsteps per second") + | ({{ustepPerSec / 1000 | fixed 1}}k µstep/sec) + + label.extra(v-if="$key == 'max-velocity'", slot="extra", + title="Revolutions Per Minute") ({{rpm | fixed 0}} RPM) + + label.extra(v-if="$key == 'max-accel' && metric", slot="extra", + title="G-force") ({{gForce | fixed 3}} g) + + label.extra(v-if="$key == 'max-jerk' && metric", slot="extra", + title="G-force per minute") ({{gForcePerMin | fixed 2}} g/min) + + label.extra(v-if="$key == 'step-angle'", slot="extra", + title="Steps per revolution") ({{stepsPerRev | fixed 0}} steps/rev) + + label.extra(v-if="$key == 'travel-per-rev' && metric", slot="extra", + title="Micrometers per step") ({{umPerStep | fixed 1}} µm/step) + + label.extra(v-if="$key == 'travel-per-rev' && !metric", slot="extra", + title="Thousandths of an inch per step") + | ({{milPerStep | fixed 2}} mil/step) + + label.extra(v-if="$key == 'min-switch' || $key == 'max-switch'", + slot="extra") + | Pin {{templ.pins[index]}} + io-indicator(:name="$key + '-' + index", :state="state") + + label.extra(v-if="$key == 'search-velocity'", slot="extra", + title="Revolutions Per Minute") + | ({{stallRPM | fixed 0}} RPM) + + label.extra(v-if="$key == 'stall-microstep'", slot="extra", + title="Microsteps Per Second") + | ({{stallUStepPerSec / 1000 | fixed 1}}k µstep/sec) diff --git a/src/pug/templates/path-viewer.pug b/src/pug/templates/path-viewer.pug new file mode 100644 index 0000000..8b60c2a --- /dev/null +++ b/src/pug/templates/path-viewer.pug @@ -0,0 +1,70 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#path-viewer-template(type="text/x-template") + .path-viewer(v-show="enabled", :class="{small: small}") + .path-viewer-toolbar + .tool-button(title="Toggle path view size.", + @click="small = !small", :class="{active: !small}") + .fa.fa-arrows-alt + + .tool-button(@click="showTool = !showTool", :class="{active: showTool}", + title="Show/hide tool.") + img(src="images/tool.png") + + .tool-button(@click="showBBox = !showBBox", :class="{active: showBBox}", + title="Show/hide bounding box.") + img(src="images/bbox.png") + + .tool-button(@click="showAxes = !showAxes", :class="{active: showAxes}", + title="Show/hide axes.") + img(src="images/axes.png") + + .tool-button(@click="showIntensity = !showIntensity", + :class="{active: showIntensity}", title="Show/hide LASER intensity.") + img(src="images/intensity.png") + + each view in "isometric top front".split(" ") + .tool-button(@click=`snap('${view}')`, title=`Snap to ${view} view.`) + img(src=`images/${view}.png`) + + .path-viewer-content + + table.path-viewer-messages( + v-if="typeof toolpath.messages != 'undefined' && " + + "toolpath.messages.length") + tr + th Level + th Location + th Message + + tr(v-for="msg in toolpath.messages", :class="'log-' + msg.level") + td.level {{msg.level}} + td.location + | {{msg.line}} + span(v-if="msg.column") :{{msg.column}} + td.message {{msg.msg}} diff --git a/src/pug/templates/settings-view.pug b/src/pug/templates/settings-view.pug new file mode 100644 index 0000000..62b4392 --- /dev/null +++ b/src/pug/templates/settings-view.pug @@ -0,0 +1,79 @@ +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#settings-view-template(type="text/x-template") + #settings + h1 Settings + + .pure-form.pure-form-aligned + fieldset + h2 Units + templated-input(name="units", :model.sync="config.settings.units", + :template="template.settings.units") + + p + | Note, #[tt units] sets both the machine default units and the + | units used in motor configuration. GCode #[tt program-start], + | set below, may also change the default machine units. + + fieldset + h2 Probe Dimensions + templated-input(v-for="templ in template.probe", :name="$key", + :model.sync="config.probe[$key]", :template="templ") + + fieldset + h2 GCode + templated-input(v-for="templ in template.gcode", :name="$key", + :model.sync="config.gcode[$key]", :template="templ") + + fieldset + h2 Path Accuracy + templated-input(name="max-deviation", + :model.sync="config.settings['max-deviation']", + :template="template.settings['max-deviation']") + + p. + Lower #[tt max-deviation] to follow the programmed path more precisely + but at a slower speed. + + p. + In order to improve traversal speed, the path planner may merge + consecutive moves or round off sharp corners if doing so would deviate + from the program path by less than #[tt max-deviation]. + + - var base = '//linuxcnc.org/docs/html/gcode/g-code.html' + p. + GCode commands + #[a(href=base + "#gcode:g61-g61.1", target="_blank") G61, G61.1] and + #[a(href=base + "#gcode:g64", target="_blank") G64] also affect path + planning accuracy. + + h2 Cornering Speed (Advanced) + templated-input(name="junction-accel", + :model.sync="config.settings['junction-accel']", + :template="template.settings['junction-accel']") + + p. + Junction acceleration limits the cornering speed the planner will + allow. Increasing this value will allow for faster traversal of + corners but may cause the planner to violate axis jerk limits and + stall the motors. Use with caution. diff --git a/src/pug/templates/templated-input.pug b/src/pug/templates/templated-input.pug new file mode 100644 index 0000000..ff93c58 --- /dev/null +++ b/src/pug/templates/templated-input.pug @@ -0,0 +1,60 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#templated-input-template(type="text/x-template") + .pure-control-group(class="tmpl-input-{{name}}", :title="title") + label(:for="name") {{name}} + + select(v-if="template.type == 'enum' || template.values", v-model="view", + :name="name", @change="change") + option(v-for="opt in template.values", :value="opt") {{opt}} + + input(v-if="template.type == 'bool'", type="checkbox", v-model="view", + :name="name", @change="change") + + input(v-if="template.type == 'float'", v-model.number="view", number, + :min="template.min", :max="template.max", :step="template.step || 'any'", + type="number", :name="name", @change="change") + + input(v-if="template.type == 'int' && !template.values", number, + v-model.number="view", :min="template.min", :max="template.max", + type="number", :name="name", @change="change") + + input(v-if="template.type == 'string'", v-model="view", type="text", + :name="name", @change="change") + + textarea(v-if="template.type == 'text'", v-model="view", :name="name", + @change="change") + + span.range(v-if="template.type == 'percent'") + input(type="range", v-model="view", :name="name", number, min="0", + max="100", step="1", @change="change") + | {{view}} + + label.units {{units}} + + slot(name="extra") diff --git a/src/pug/templates/tool-view.pug b/src/pug/templates/tool-view.pug new file mode 100644 index 0000000..4f331a4 --- /dev/null +++ b/src/pug/templates/tool-view.pug @@ -0,0 +1,404 @@ +//-///////////////////////////////////////////////////////////////////////////// +//- // +//- This file is part of the Buildbotics firmware. // +//- // +//- Copyright (c) 2015 - 2018, Buildbotics LLC // +//- All rights reserved. // +//- // +//- This file ("the software") is free software: you can redistribute it // +//- and/or modify it under the terms of the GNU General Public License, // +//- version 2 as published by the Free Software Foundation. You should // +//- have received a copy of the GNU General Public License, version 2 // +//- along with the software. If not, see . // +//- // +//- The software 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 // +//- Lesser General Public License for more details. // +//- // +//- You should have received a copy of the GNU Lesser General Public // +//- License along with the software. If not, see // +//- . // +//- // +//- For information regarding this software email: // +//- "Joseph Coffland" // +//- // +//-///////////////////////////////////////////////////////////////////////////// + +script#tool-view-template(type="text/x-template") + #tool + h1 Tool Configuration + + .pure-form.pure-form-aligned + fieldset + templated-input(v-for="templ in template.tool", :name="$key", + :model.sync="config.tool[$key]", :template="templ", + v-if="tool_type != 'DISABLED' || $key == 'tool-type'") + + label.extra(slot="extra", + v-if="$key == 'tool-enable-mode' || $key == 'tool-direction-mode'") + | Pin {{templ.pin}} + io-indicator(:name="$key", :state="state") + + fieldset(v-if="tool_type == 'PWM SPINDLE'") + h2 PWM Spindle + templated-input(v-for="templ in template['pwm-spindle']", + :name="$key", :model.sync="config['pwm-spindle'][$key]", + :template="templ") + + fieldset(v-if="is_modbus") + h2 Modbus Configuration + templated-input(v-for="templ in template['modbus-spindle']", + :name="$key", :model.sync="config['modbus-spindle'][$key]", + :template="templ", v-if="show_modbus_field($key)") + + h2 Modbus Status + .pure-control-group(title="VFD connection status") + label connection + tt {{modbus_status}} + .pure-control-group(title="Numerical status reported by VFD") + label status + tt {{state.ss || 0}} + .pure-control-group(title="Speed reported by VFD") + label speed + tt {{state.s | fixed}} + label.units RPM + + fieldset.modbus-program( + v-if="is_modbus && this.tool_type != 'HUANYANG VFD'") + h2 Active Modbus Program + p(v-if="$root.modified") + | (Click #[tt(class="save") Save] to activate the selected + | #[b tool-type].) + table.modbus-regs.fixed-regs + tr + th Index + th Command + th Address + th Value + th Failures + + tr(v-for="(index, reg) in regs_tmpl.index", v-if="state[reg + 'vt']", + :class="{warn: get_reg_fails(reg)}") + td.reg-index {{index}} + td.reg-type {{get_reg_type(reg)}} + td.reg-addr {{get_reg_addr(reg)}} + td.reg-value {{get_reg_value(reg)}} + td.reg-fails {{get_reg_fails(reg)}} + + button.pure-button-secondary(@click="customize") Customize + button.pure-button-secondary(@click="clear", + v-if="tool_type == 'CUSTOM MODBUS VFD'") Clear + button.pure-button-secondary(@click="reset_failures") Reset Failures + + fieldset(v-if="tool_type == 'CUSTOM MODBUS VFD'") + h2 Edit Modbus Program + table.modbus-regs + tr + th Index + th Command + th Address + th Value + + tr(v-for="(index, reg) in config['modbus-spindle'].regs", + is="modbus-reg", :index="index", :model.sync="reg", + :template="template['modbus-spindle'].regs.template", + v-if="!index || reg['reg-type'] != 'disabled' || " + + "config['modbus-spindle'].regs[index - 1]['reg-type'] != " + + "'disabled'") + + .notes(v-if="tool_type == 'HUANYANG VFD'") + h2 Notes + p Set the following using the VFD's front panel. + table.modbus-regs.fixed-regs + tr + th Address + th Value + td Meaning + th Description + tr + td.reg-addr PD000 + td.reg-value 0 + td Unlock + td Unlock parameters + tr + td.reg-addr PD001 + td.reg-value 2 + td RS485 + td Command source + tr + td.reg-addr PD002 + td.reg-value 2 + td RS485 + td Speed/frequency source + tr + td.reg-addr PD163 + td.reg-value 1 + td Modbus ID + td Must match #[tt bus-id] above. + tr + td.reg-addr PD164 + td.reg-value 1 + td 9600 baud + td Must match #[tt baud] above. + tr + td.reg-addr PD165 + td.reg-value 3 + td 8 bit, no parity, RTU mode + td Must match #[tt parity] above. + + p + | Other settings according to the + | + a(href="https://buildbotics.com/upload/vfd/Huanyang-VFD-manual.pdf", + target="_blank") Huanyang VFD manual + | + | and spindle type. + + .notes(v-if="tool_type.startsWith('NOWFOREVER VFD')") + h2 Notes + p Set the following using the VFD's front panel. + table.modbus-regs.fixed-regs + tr + th Address + th Value + th Meaning + th Description + tr + td.reg-addr P0-000 + td.reg-value 2 + td Modbus communication + td Command source + tr + td.reg-addr P0-001 + td.reg-value 0 + td Main frequence X + td Select frequency source + tr + td.reg-addr P0-002 + td.reg-value 6 + td Modbus communication + td Main frequency X + tr + td.reg-addr P0-055 + td.reg-value 1 + td Modbus ID + td Must match #[tt bus-id] above + tr + td.reg-addr P0-056 + td.reg-value 2 + td 9600 baud + td Must match #[tt baud] above + tr + td.reg-addr P0-057 + td.reg-value 0 + td 1 start, 8 data, no parity, 1 stop + td Must match #[tt parity] above + + .notes(v-if="tool_type.startsWith('DELTA VFD015M21A')") + h2 Notes + p Set the following using the VFD's front panel. + table.modbus-regs.fixed-regs + tr + th Address + th Value + th Meaning + th Description + tr + td.reg-addr Pr.00 + td.reg-value 3 + td RS-485 + td Source of frequency command + tr + td.reg-addr Pr.01 + td.reg-value 3 + td RS-485 with STOP + td Source of operation command + tr + td.reg-addr Pr.88 + td.reg-value 1 + td Modbus ID + td Must match #[tt bus-id] above + tr + td.reg-addr Pr.89 + td.reg-value 1 + td 9600 baud + td Must match #[tt baud] above + tr + td.reg-addr Pr.92 + td.reg-value 3 + td 8 bit, no parity, RTU mode + td Must match #[tt parity] above + tr + td.reg-addr Pr.157 + td.reg-value 1 + td Modbus mode + td Communication mode + + p + | Other settings according to the + | + a(href="https://buildbotics.com/upload/vfd/Delta_VFD015M21A.pdf", + target="_blank") Delta VFD015M21A VFD manual + | + | and spindle type. + + .notes(v-if="tool_type.startsWith('YL600')") + h2 Notes + p Set the following using the VFD's front panel. + table.modbus-regs.fixed-regs + tr + th Address + th Value + th Meaning + th Description + tr + td.reg-addr P00.01 + td.reg-value 3 + td Modbus RS-485 + td Start / stop command source + tr + td.reg-addr P03.00 + td.reg-value 3 + td 9600 baud + td Must match #[tt baud] above + tr + td.reg-addr P03.01 + td.reg-value 1 + td Modbus ID + td Must match #[tt bus-id] above + tr + td.reg-addr P03.02 + td.reg-value 5 + td 8 bit, no parity, 2 stop + td Must match #[tt parity] above + tr + td.reg-addr P03.04 + td.reg-value 500 + td RS-485 max delay + td Time in milliseconds + tr + td.reg-addr P07.15 + td.reg-value 5 + td RS-485 + td Frequency source + + p + | Other settings according to the + | + a(href="https://buildbotics.com/upload/vfd/YL620-A.pdf", + target="_blank") YL600 VFD manual + | + | and spindle type. + + .notes(v-if="tool_type.startsWith('SUNFAR')") + h2 Notes + p Set the following using the VFD's front panel. + table.modbus-regs.fixed-regs + tr + th Address + th Value + th Meaning + th Description + tr + td.reg-addr F0.0 + td.reg-value 2 + td Serial communication + td Frequency source + tr + td.reg-addr F0.2 + td.reg-value 1002 + td Serial communication + td Control source + tr + td.reg-addr F4.0 + td.reg-value 0104 + td Modbus, no parity, 9600 baud + td Must match #[tt parity] and #[tt baud] above + tr + td.reg-addr F4.1 + td.reg-value 1 + td Bus ID + td Must match #[tt bus-id] above + tr + td.reg-addr F4.4 + td.reg-value 3 + td Seconds + td Communication timeout + + p + | Other settings according to the + | + a(href="https://buildbotics.com/upload/vfd/Sunfar-E300.pdf", + target="_blank") Sunfar E300 VFD manual + | + | and spindle type. + + .notes(v-if="tool_type.startsWith('OMRON')") + h2 Notes + p Set the following using the VFD's front panel. + table.modbus-regs.fixed-regs + tr + th Address + th Value + th Meaning + th Description + tr + td.reg-addr C071 + td.reg-value 5 + td 9600 BAUD + td Must match #[tt baud] above + tr + td.reg-addr C072 + td.reg-value 1 + td Bus ID 1 + td Must match #[tt bus-id] above + tr + td.reg-addr C074 + td.reg-value 0 + td No parity + td Must match #[tt parity] above + tr + td.reg-addr C075 + td.reg-value 2 + td 2 stop bits + td Serial stop bits + tr + td.reg-addr C076 + td.reg-value 4 + td Deceleration stop + td Communication error action + tr + td.reg-addr C077 + td.reg-value 500 + td 0.5 seconds + td Communication error timeout + tr + td.reg-addr C078 + td.reg-value 1 + td 1 milisecond + td Communication wait time + tr + td.reg-addr C096 + td.reg-value 0 + td Modbus-RTU + td Communication mode + tr + td.reg-addr P200 + td.reg-value 0 + td Standard + td Modbus mapping + tr + td.reg-addr P400 + td.reg-value 0 + td Big endian + td Communication byte order + + p + | Other settings according to the + | + a(href="https://buildbotics.com/upload/vfd/omron_i570_mx2.pdf", + target="_blank") OMRON MX2 VFD manual + | + | and spindle type. The VFD must be rebooted after changing + | the above settings. diff --git a/src/pwr/.gitignore b/src/pwr/.gitignore new file mode 100644 index 0000000..acbe7a5 --- /dev/null +++ b/src/pwr/.gitignore @@ -0,0 +1,3 @@ +/build +/*.elf +/*.hex diff --git a/src/pwr/Makefile b/src/pwr/Makefile new file mode 100644 index 0000000..4fed54b --- /dev/null +++ b/src/pwr/Makefile @@ -0,0 +1,116 @@ +# Makefile for the project Bulidbotics firmware +PROJECT = bbctrl-pwr-firmware +MCU = attiny1634 +CLOCK = 8000000 + +# Compile flags +CC = avr-gcc +CPP = avr-g++ +COMMON = -mmcu=$(MCU) +CFLAGS += $(COMMON) +CFLAGS += -Wall -Werror +CFLAGS += -std=gnu99 -DF_CPU=$(CLOCK)UL -O3 +CFLAGS += -funsigned-bitfields -fpack-struct -fshort-enums -funsigned-char +CFLAGS += -MD -MP -MT $@ -MF build/dep/$(@F).d +CFLAGS += -I. + +# Linker flags +LDFLAGS += $(COMMON) -Wl,-u,vfprintf -lprintf_flt -lm +LIBS += -lm + +# Programming flags +ifndef (PROGRAMMER) +PROGRAMMER = avrispmkII +#PROGRAMMER = jtag3pdi +endif +PDEV = usb +AVRDUDE_OPTS = -c $(PROGRAMMER) -p $(MCU) -P $(PDEV) + +FUSE_EX=0xff +FUSE_HI=0xdf +FUSE_LO=0x62 + +# SRC +SRC = $(wildcard *.c) +OBJ = $(patsubst %.c,build/%.o,$(SRC)) + +# Build +all: $(PROJECT).hex size + +# Compile +build/%.o: %.c + @mkdir -p $(shell dirname $@) + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +build/%.o: %.S + @mkdir -p $(shell dirname $@) + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +# Link +$(PROJECT).elf: $(OBJ) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.hex: %.elf + avr-objcopy -O ihex -R .fuse -R .lock -R .signature $< $@ + +%.lss: %.elf + avr-objdump -h -S $< > $@ + +_size: + @for X in A B C; do\ + echo '****************************************************************' ;\ + avr-size -$$X --mcu=$(MCU) $(SIZE_TARGET) ;\ + done + +size: $(PROJECT).elf + @$(MAKE) SIZE_TARGET=$< _size + +# Program +init: + $(MAKE) erase + -$(MAKE) fuses + $(MAKE) program + +reset: + avrdude $(AVRDUDE_OPTS) + +erase: + avrdude $(AVRDUDE_OPTS) -e + +program: $(PROJECT).hex + avrdude $(AVRDUDE_OPTS) -U flash:w:$(PROJECT).hex:i + +verify: $(PROJECT).hex + avrdude $(AVRDUDE_OPTS) -U flash:v:$(PROJECT).hex:i + +fuses: + avrdude $(AVRDUDE_OPTS) -U efuse:w:$(FUSE_EX):m -U lfuse:w:$(FUSE_LO):m \ + -U hfuse:w:$(FUSE_HI):m + +read_fuses: + avrdude $(AVRDUDE_OPTS) -q -q -U efuse:r:-:h -U lfuse:r:-:h -U hfuse:r:-:h + +signature: + avrdude $(AVRDUDE_OPTS) -q -q -U signature:r:-:h + +prodsig: + avrdude $(AVRDUDE_OPTS) -q -q -U prodsig:r:-:h + +usersig: + avrdude $(AVRDUDE_OPTS) -q -q -U usersig:r:-:h + +info: + avrdude $(AVRDUDE_OPTS) -v + +# Clean +tidy: + rm -f $(shell find -name \*~ -o -name \#\*) + +clean: tidy + rm -rf $(PROJECT).elf $(PROJECT).hex $(PROJECT).lss $(PROJECT).map build + +.PHONY: tidy clean size all reset erase program fuses read_fuses prodsig +.PHONY: signature usersig + +# Dependencies +-include $(shell mkdir -p build/dep) $(wildcard build/dep/*) diff --git a/src/pwr/config.h b/src/pwr/config.h new file mode 100644 index 0000000..206d91e --- /dev/null +++ b/src/pwr/config.h @@ -0,0 +1,164 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +#include "pins.h" + + +#define VERSION 6 + + +// Pins +enum { + AREF_PIN = PORT_A << 3, + PA1_PIN, // NC + PA2_PIN, // NC + CS1_PIN, + CS2_PIN, + CS3_PIN, + CS4_PIN, + VOUT_REF_PIN, + + VIN_REF_PIN = PORT_B << 3, + PWR_MOSI_PIN, + PWR_MISO_PIN, + SHUNT_PIN, + + MOTOR_PIN = PORT_C << 3, // IN1 + PWR_SCK_PIN, + PC2_PIN, // NC + PWR_RESET, + LOAD2_PIN, // IN3 + LOAD1_PIN, // IN4 +}; + + +// ADC channels +enum { + CS1_ADC, // Motor current + CS2_ADC, // Vdd current + CS3_ADC, // Load 2 current + CS4_ADC, // Load 1 current + VOUT_ADC, // Motor voltage + VIN_ADC, // Input voltage + NC6_ADC, + NC7_ADC, + NC8_ADC, + NC9_ADC, + NC10_ADC, + NC11_ADC, + NC12_ADC, + NC13_ADC, + TEMP_ADC, // Temperature +}; + + +#define SHUNT_FAIL_VOLTAGE 5 +#define CAP_CHARGE_TIME 20 // ms +#define VOLTAGE_MIN 11 +#define VOLTAGE_MAX 39 +#define CURRENT_MAX 25 +#define CURRENT_OVERTEMP 19 // Should read ~21A but > 11.86A is error +#define LOAD_OVERTEMP_MAX 10 +#define MOTOR_SHUTDOWN_THRESH 15 +#define VOLTAGE_SETTLE_COUNT 5 +#define VOLTAGE_SETTLE_PERIOD 20 // ms +#define VOLTAGE_SETTLE_TOLERANCE 0.01 +#define VOLTAGE_EXP 0.01 + +#define SHUNT_WATTS 5 +#define SHUNT_OHMS 5.1 +#define SHUNT_PERIOD 65000 // ms +#define SHUNT_JOULES 25 // Power per shunt period +#define SHUNT_JOULES_PER_MS ((float)SHUNT_JOULES / SHUNT_PERIOD) +#define SHUNT_MIN_V 2 + +#define VOLTAGE_REF 1.1 +#define VOLTAGE_REF_R1 37400 +#define VOLTAGE_REF_R2 1000 +#define CURRENT_REF_R2 137 +#define CURRENT_REF_MUL (100.0 * 2700 / CURRENT_REF_R2) // 2700 from datasheet + +#define REG_SCALE 100 +#define AVG_SCALE 3 +#define BUCKETS (1 << AVG_SCALE) + +// Addresses 0x60 to 0x67 +#define I2C_ADDR 0x60 +#define I2C_MASK 0b00001111 + +#define I2C_ERROR_BM (1 << TWBE) +#define I2C_DATA_INT_BM (1 << TWDIF) +#define I2C_READ_BM (1 << TWDIR) +#define I2C_ADDRESS_STOP_INT_BM (1 << TWASIF) +#define I2C_ADDRESS_MATCH_BM (1 << TWAS) + + +typedef enum { + TEMP_REG, + VIN_REG, + VOUT_REG, + MOTOR_REG, + LOAD1_REG, + LOAD2_REG, + VDD_REG, + FLAGS_REG, + VERSION_REG, + NUM_REGS +} regs_t; + + +enum { + // Fatal + UNDER_VOLTAGE_FLAG = 1 << 0, + OVER_VOLTAGE_FLAG = 1 << 1, + OVER_CURRENT_FLAG = 1 << 2, + SENSE_ERROR_FLAG = 1 << 3, + SHUNT_OVERLOAD_FLAG = 1 << 4, + MOTOR_OVERLOAD_FLAG = 1 << 5, + + // Non fatal + LOAD1_SHUTDOWN_FLAG = 1 << 6, + LOAD2_SHUTDOWN_FLAG = 1 << 7, + MOTOR_UNDER_VOLTAGE_FLAG = 1 << 8, + SHUNT_ERROR_FLAG = 1 << 15, + + // Sense errors + MOTOR_VOLTAGE_SENSE_ERROR_FLAG = 1 << 9, + MOTOR_CURRENT_SENSE_ERROR_FLAG = 1 << 10, + LOAD1_SENSE_ERROR_FLAG = 1 << 11, + LOAD2_SENSE_ERROR_FLAG = 1 << 12, + VDD_CURRENT_SENSE_ERROR_FLAG = 1 << 13, + + // State flags + POWER_SHUTDOWN_FLAG = 1 << 14, +}; + + +#define FATAL_FLAGS 0x003f +#define SENSE_ERROR_FLAGS 0x3e00 diff --git a/src/pwr/main.c b/src/pwr/main.c new file mode 100644 index 0000000..92007c4 --- /dev/null +++ b/src/pwr/main.c @@ -0,0 +1,486 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "config.h" + +#include +#include +#include + +#include + +#include + + +typedef struct { + volatile uint16_t value; + volatile uint16_t raw; + volatile uint16_t buckets[BUCKETS]; + volatile uint8_t index; + volatile uint8_t fill; + volatile uint32_t sum; +} reg_t; + + +typedef struct { + const regs_t reg; + const uint8_t pin; + volatile uint8_t overtemp; + volatile uint16_t shutdown_flag; +} load_t; + + +load_t loads[2] = { + {LOAD1_REG, LOAD1_PIN, 0, LOAD1_SHUTDOWN_FLAG}, + {LOAD2_REG, LOAD2_PIN, 0, LOAD2_SHUTDOWN_FLAG}, +}; + + +static const uint8_t ch_schedule[] = { + TEMP_ADC, VOUT_ADC, + VIN_ADC, VOUT_ADC, + CS1_ADC, VOUT_ADC, + CS2_ADC, VOUT_ADC, + CS3_ADC, VOUT_ADC, + CS4_ADC, VOUT_ADC, +}; + + +static reg_t regs[NUM_REGS] = {{0}}; +static volatile uint64_t time = 0; // ms +static volatile uint8_t motor_overload = 0; +static volatile float shunt_joules = 0; +static volatile bool initialized = false; +static volatile float vnom = 0; + + +void delay(uint16_t ms) { + uint64_t end = time + ms; + while (time < end) continue; +} + + +static void shutdown(); + + +static uint16_t flags_get(uint16_t flags) { + return regs[FLAGS_REG].value & flags; +} + + +static void flags_clear(uint16_t flags) {regs[FLAGS_REG].value &= ~flags;} + + +static void flags_set(uint16_t flags) { + regs[FLAGS_REG].value |= flags; + if (flags & FATAL_FLAGS) shutdown(); +} + + +static void flags(uint16_t flags, bool enable) { + if (enable) flags_set(flags); + else flags_clear(flags); +} + + +static void i2c_ack() {TWSCRB = (1 << TWCMD1) | (1 << TWCMD0);} +static void i2c_nack() {TWSCRB = (1 << TWAA) | (1 << TWCMD1) | (1 << TWCMD0);} + + +ISR(TWI_SLAVE_vect) { + static uint8_t byte = 0; + static uint16_t reg; + + // Stretch clock longer to work around RPi bug + // See https://github.com/raspberrypi/linux/issues/254 + _delay_us(10); // Must use software delay while in interrupt + + uint8_t status = TWSSRA; + + if (status & I2C_DATA_INT_BM) { + if (status & I2C_READ_BM) { + // Send response + if (byte < 2) { + TWSD = byte++ ? reg >> 8 : reg; + i2c_ack(); + + } else i2c_nack(); + + } else i2c_ack(); // Write ignore + + } else if (status & I2C_ADDRESS_STOP_INT_BM) { + if (status & I2C_ADDRESS_MATCH_BM) { + // Read address + uint8_t addr = (TWSD >> 1) & I2C_MASK; + + if (addr < NUM_REGS) { + i2c_ack(); + reg = regs[addr].value; + byte = 0; + + } else i2c_nack(); + + } else TWSCRB = (1 << TWCMD1) | (0 << TWCMD0); // Stop + } +} + + +static float get_reg(int reg) { + uint8_t sreg = SREG; + cli(); + float value = regs[reg].value; + SREG = sreg; + + return value / REG_SCALE; +} + + +static void update_shunt() { + if (!initialized) return; + + static float joules = SHUNT_JOULES; // Power disipation budget + + // Add power dissipation credit for the 1ms that elapsed + joules += SHUNT_JOULES_PER_MS; + if (SHUNT_JOULES < joules) joules = SHUNT_JOULES; // Max + + if (joules < shunt_joules) flags_set(SHUNT_OVERLOAD_FLAG); + else joules -= shunt_joules; // Subtract power dissipated +} + + +static void update_shunt_power() { + if (!initialized) return; + + float vout = get_reg(VOUT_REG); + + if (vnom + SHUNT_MIN_V < vout) { + // Compute joules shunted this cycle: J = V^2 / RT + shunt_joules = vout * vout / (SHUNT_OHMS * 1000.0); + IO_PORT_CLR(SHUNT_PIN); // Enable (lo) + + } else { + shunt_joules = 0; + IO_PORT_SET(SHUNT_PIN); // Disable (hi) + } +} + + +static void measure_nominal_voltage() { + float vin = regs[VIN_REG].raw / REG_SCALE; + + if (vnom < VOLTAGE_MIN) vnom = vin; + else vnom = vnom * (1 - VOLTAGE_EXP) + vin * VOLTAGE_EXP; +} + + +ISR(TIMER0_OVF_vect) { + static uint8_t tick = 0; + + if (++tick == 31) { + time++; + tick = 0; + + update_shunt(); // Every 1ms + if (!(time & 7)) measure_nominal_voltage(); // Every 8ms + } +} + + +static uint16_t average_reg(int index, uint16_t sample) { + reg_t *reg = ®s[index]; + + reg->raw = sample; + reg->sum -= reg->buckets[reg->index]; + reg->sum += sample; + reg->buckets[reg->index] = sample; + if (++reg->index == BUCKETS) reg->index = 0; + + if (reg->fill < BUCKETS) { + reg->fill++; + reg->value = reg->sum / reg->fill; + + } else reg->value = reg->sum >> AVG_SCALE; + + return reg->value; +} + + +static uint16_t convert_voltage(uint16_t sample) { + return + sample * (VOLTAGE_REF / 1024.0 * + (VOLTAGE_REF_R1 + VOLTAGE_REF_R2) / VOLTAGE_REF_R2 * REG_SCALE); +} + + +static uint16_t convert_current(uint16_t sample) { + return sample * (VOLTAGE_REF / 1024.0 * CURRENT_REF_MUL); +} + + +static void update_current(int reg, uint16_t sample) { + average_reg(reg, convert_current(sample)); + + // Check total current + if (!initialized) return; + uint16_t total_current = + regs[MOTOR_REG].value + regs[VDD_REG].value + regs[LOAD1_REG].value + + regs[LOAD2_REG].value; + if (CURRENT_MAX * REG_SCALE < total_current) flags_set(OVER_CURRENT_FLAG); +} + + +static void update_vin(uint16_t sample) { + uint16_t vin = average_reg(VIN_REG, convert_voltage(sample)); + + // Check voltage + if (!initialized) return; + if (vin < (VOLTAGE_MIN * REG_SCALE)) flags_set(UNDER_VOLTAGE_FLAG); + if ((VOLTAGE_MAX * REG_SCALE) < vin) flags_set(OVER_VOLTAGE_FLAG); +} + + +static void update_vout(uint16_t sample) { + uint16_t vout = average_reg(VOUT_REG, convert_voltage(sample)); + + update_shunt_power(); + + // Check voltage + if (!initialized) return; + if ((VOLTAGE_MAX * REG_SCALE) < vout) flags_set(OVER_VOLTAGE_FLAG); + flags(MOTOR_UNDER_VOLTAGE_FLAG, + vout < (VOLTAGE_MIN * REG_SCALE) && !flags_get(POWER_SHUTDOWN_FLAG)); +} + + +static void update_motor_current(uint16_t sample) { + update_current(MOTOR_REG, sample); + + // Check overtemp and motor overload + if (!initialized) return; + bool overtemp = CURRENT_OVERTEMP * REG_SCALE < regs[MOTOR_REG].value; + + if (overtemp) { + if (motor_overload < MOTOR_SHUTDOWN_THRESH) motor_overload++; + if (motor_overload == MOTOR_SHUTDOWN_THRESH) flags_set(MOTOR_OVERLOAD_FLAG); + + } else if (motor_overload != MOTOR_SHUTDOWN_THRESH && motor_overload) + motor_overload--; +} + + +static void load_shutdown(load_t *load) { + if (!flags_get(POWER_SHUTDOWN_FLAG)) flags_set(load->shutdown_flag); + IO_PORT_CLR(load->pin); // Lo + IO_DDR_SET(load->pin); // Output +} + + +static void update_load_current(load_t *load, uint16_t sample) { + update_current(load->reg, sample); + + if (!initialized || flags_get(load->shutdown_flag)) return; + + bool overtemp = CURRENT_OVERTEMP * REG_SCALE < regs[load->reg].value; + + if (overtemp) { + if (++load->overtemp == LOAD_OVERTEMP_MAX) load_shutdown(load); + } else if (load->overtemp) load->overtemp--; +} + + +static void read_conversion(uint8_t ch) { + uint16_t sample = ADC; + + switch (ch) { + case TEMP_ADC: regs[TEMP_REG].value = sample; break; // in Kelvin + case VIN_ADC: update_vin(sample); break; + case VOUT_ADC: update_vout(sample); break; + case CS1_ADC: update_motor_current(sample); break; + case CS2_ADC: update_current(VDD_REG, sample); break; + case CS3_ADC: update_load_current(&loads[1], sample); break; + case CS4_ADC: update_load_current(&loads[0], sample); break; + } +} + + +static void adc_conversion() { + static int i = 0; + + read_conversion(ch_schedule[i]); + if (++i == sizeof(ch_schedule)) i = 0; + + // Start next conversion + ADMUX = (ADMUX & 0xf0) | ch_schedule[i]; + ADCSRA |= 1 << ADSC; +} + + +ISR(ADC_vect) {adc_conversion();} + + +static bool is_within(float a, float b, float tolerance) { + return a * (1 - tolerance) < b && b < a * (1 + tolerance); +} + + +static void validate_input_voltage() { + int settle = 0; + float vlast = 0; + + while (settle < VOLTAGE_SETTLE_COUNT) { + delay(VOLTAGE_SETTLE_PERIOD); + + // Check that voltage is with in range and settled + float vin = get_reg(VIN_REG); + if (VOLTAGE_MIN < vin && vin < VOLTAGE_MAX && + is_within(vlast, vin, VOLTAGE_SETTLE_TOLERANCE)) settle++; + else settle = 0; + + vlast = vin; + } +} + + +static void charge_caps() { + IO_PORT_SET(SHUNT_PIN); // Disable shunt (hi) + IO_PORT_SET(MOTOR_PIN); // Motor voltage on + delay(CAP_CHARGE_TIME); +} + + +static void shunt_test() { + charge_caps(); + + // Discharge caps + IO_PORT_CLR(MOTOR_PIN); // Motor voltage off + IO_PORT_CLR(SHUNT_PIN); // Enable shunt (lo) + delay(CAP_CHARGE_TIME); + + if (SHUNT_FAIL_VOLTAGE < get_reg(VOUT_REG)) flags_set(SHUNT_ERROR_FLAG); +} + + +void init() { + cli(); + + // CPU Clock, disable CKOUT + CCP = 0xd8; + CLKSR = (1 << CSTR) | (1 << CKOUT_IO) | 0b0010; // 8Mhz internal clock + CCP = 0xd8; + CLKPR = 0; // div 1 + while (!((1 << 7) & CLKSR)) continue; // Wait for clock to stabilize + + // Power reduction + PRR = (0 << PRADC) | (1 << PRUSART0) | (1 << PRUSART1) | (1 << PRUSI) | + (0 << PRTIM0) | (0 << PRTIM1) | (0 << PRTWI); + + // IO + IO_PORT_CLR(MOTOR_PIN); // Motor voltage off + IO_DDR_SET(MOTOR_PIN); // Output + IO_DDR_CLR(LOAD1_PIN); // Tri-state + IO_DDR_CLR(LOAD2_PIN); // Tri-state + IO_PUE_SET(PWR_RESET); // Pull up reset line + IO_PORT_CLR(SHUNT_PIN); // Enable shunt + IO_DDR_SET(SHUNT_PIN); // Output + + // Disable digital IO on ADC lines + DIDR0 = (1 << ADC4D) | (1 << ADC3D) | (1 << ADC2D) | (1 << ADC1D) | + (1 << ADC0D) | (1 << AREFD); + DIDR1 = (1 << ADC5D); + + // ADC internal 1.1v, enable, with interrupt, prescale 64 + // Note, a conversion takes ~200uS + ADMUX = (1 << REFS1) | (0 << REFS0); + ADCSRA = (1 << ADEN) | (1 << ADIE) | + (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0); + ADCSRB = 0; + + // Timer 0, normal, clk/1 + TCCR0A = (0 << WGM01) | (0 << WGM00); + TCCR0B = (0 << WGM02) | (0 << CS02) | (0 << CS01) | (1 << CS00); + TIMSK = 1 << TOIE0; // Enable overflow interrupt + + // I2C, enable, enable address/stop interrupt + TWSCRA = (1 << TWEN) | (1 << TWASIE) | (1 << TWDIE); + TWSA = I2C_ADDR << 1; + TWSAM = I2C_MASK << 1; + + sei(); +} + + +static void shutdown() { + if (flags_get(POWER_SHUTDOWN_FLAG)) return; + flags_set(POWER_SHUTDOWN_FLAG); + initialized = false; + + // Disable loads + load_shutdown(&loads[0]); + load_shutdown(&loads[1]); + + // Motor power off + IO_PORT_CLR(MOTOR_PIN); // Lo + + // Turn shunt on + IO_PORT_CLR(SHUNT_PIN); // Lo +} + + +static void validate_measurements() { + const float max_voltage = 0.99 * convert_voltage(0x3ff); + const float max_current = 0.99 * convert_current(0x3ff); + + if (max_voltage < regs[VOUT_REG].value) + flags_set(MOTOR_VOLTAGE_SENSE_ERROR_FLAG); + if (max_current < regs[MOTOR_REG].value) + flags_set(MOTOR_CURRENT_SENSE_ERROR_FLAG); + if (max_current < regs[LOAD1_REG].value) + flags_set(LOAD1_SENSE_ERROR_FLAG); + if (max_current < regs[LOAD2_REG].value) + flags_set(LOAD2_SENSE_ERROR_FLAG); + if (max_current < regs[VDD_REG].value) + flags_set(VDD_CURRENT_SENSE_ERROR_FLAG); + if (flags_get(SENSE_ERROR_FLAGS)) flags_set(SENSE_ERROR_FLAG); +} + + +int main() { + regs[VERSION_REG].value = VERSION; + + init(); + adc_conversion(); // Start ADC + validate_input_voltage(); + shunt_test(); + charge_caps(); + validate_measurements(); + initialized = true; + + while (true) continue; + + return 0; +} diff --git a/src/pwr/pins.c b/src/pwr/pins.c new file mode 100644 index 0000000..7d3f51a --- /dev/null +++ b/src/pwr/pins.c @@ -0,0 +1,31 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include "pins.h" + + +uint16_t io_base[] = {0xf, 0xb, 0x7}; diff --git a/src/pwr/pins.h b/src/pwr/pins.h new file mode 100644 index 0000000..3852aa9 --- /dev/null +++ b/src/pwr/pins.h @@ -0,0 +1,63 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2018, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#pragma once + +enum {PORT_A = 1, PORT_B, PORT_C}; +enum {IO_REG_PIN, IO_REG_DDR, IO_REG_PORT, IO_REG_PUE}; + +#define IO_REG(PIN, REG) _SFR_IO8(io_base[((PIN) >> 3) - 1] + (REG)) +#define BM(PIN) (1 << ((PIN) & 7)) + +#include + +extern uint16_t io_base[]; + +#define IO_REG_GET(PIN, REG) (!!(IO_REG(PIN, REG) & BM(PIN))) +#define IO_REG_SET(PIN, REG) do {IO_REG(PIN, REG) |= BM(PIN);} while (0) +#define IO_REG_CLR(PIN, REG) do {IO_REG(PIN, REG) &= ~BM(PIN);} while (0) +#define IO_REG_TGL(PIN, REG) do {IO_REG(PIN, REG) ^= BM(PIN);} while (0) + +#define IO_PIN_GET(PIN) IO_REG_GET(PIN, IO_REG_PIN) +#define IO_PIN_SET(PIN) IO_REG_SET(PIN, IO_REG_PIN) +#define IO_PIN_CLR(PIN) IO_REG_CLR(PIN, IO_REG_PIN) +#define IO_PIN_TGL(PIN) IO_REG_TGL(PIN, IO_REG_PIN) + +#define IO_DDR_GET(PIN) IO_REG_GET(PIN, IO_REG_DDR) +#define IO_DDR_SET(PIN) IO_REG_SET(PIN, IO_REG_DDR) +#define IO_DDR_CLR(PIN) IO_REG_CLR(PIN, IO_REG_DDR) +#define IO_DDR_TGL(PIN) IO_REG_TGL(PIN, IO_REG_DDR) + +#define IO_PORT_GET(PIN) IO_REG_GET(PIN, IO_REG_PORT) +#define IO_PORT_SET(PIN) IO_REG_SET(PIN, IO_REG_PORT) +#define IO_PORT_CLR(PIN) IO_REG_CLR(PIN, IO_REG_PORT) +#define IO_PORT_TGL(PIN) IO_REG_TGL(PIN, IO_REG_PORT) + +#define IO_PUE_GET(PIN) IO_REG_GET(PIN, IO_REG_PUE) +#define IO_PUE_SET(PIN) IO_REG_SET(PIN, IO_REG_PUE) +#define IO_PUE_CLR(PIN) IO_REG_CLR(PIN, IO_REG_PUE) +#define IO_PUE_TGL(PIN) IO_REG_TGL(PIN, IO_REG_PUE) diff --git a/src/py/bbctrl/APIHandler.py b/src/py/bbctrl/APIHandler.py new file mode 100644 index 0000000..c21ca56 --- /dev/null +++ b/src/py/bbctrl/APIHandler.py @@ -0,0 +1,88 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import json +import traceback +import bbctrl + +from tornado.web import HTTPError +import tornado.httpclient + + +class APIHandler(bbctrl.RequestHandler): + def delete(self, *args, **kwargs): + self.delete_ok(*args, **kwargs) + self.write_json('ok') + + + def delete_ok(self): raise HTTPError(405) + + + def put(self, *args, **kwargs): + self.put_ok(*args, **kwargs) + self.write_json('ok') + + + def put_ok(self): raise HTTPError(405) + + + def prepare(self): + self.json = {} + + if self.request.body: + try: + self.json = tornado.escape.json_decode(self.request.body) + except ValueError: + raise HTTPError(400, 'Unable to parse JSON') + + + def set_default_headers(self): + self.set_header('Content-Type', 'application/json') + + + def write_error(self, status_code, **kwargs): + e = {} + + if 'message' in kwargs: e['message'] = kwargs['message'] + + elif 'exc_info' in kwargs: + typ, value, tb = kwargs['exc_info'] + if isinstance(value, HTTPError) and value.log_message: + e['message'] = value.log_message % value.args + else: e['message'] = str(kwargs['exc_info'][1]) + + else: e['message'] = 'Unknown error' + + e['code'] = status_code + + self.write_json(e) + + + def write_json(self, data, pretty = False): + if pretty: data = json.dumps(data, indent = 2, separators = (',', ': ')) + else: data = json.dumps(data, separators = (',', ':')) + self.write(data) diff --git a/src/py/bbctrl/AVR.py b/src/py/bbctrl/AVR.py new file mode 100644 index 0000000..211fe6e --- /dev/null +++ b/src/py/bbctrl/AVR.py @@ -0,0 +1,162 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import serial +import time +import traceback +import ctypes + +import bbctrl +import bbctrl.Cmd as Cmd + + +class serial_struct(ctypes.Structure): + _fields_ = [ + ('type', ctypes.c_int), + ('line', ctypes.c_int), + ('port', ctypes.c_uint), + ('irq', ctypes.c_int), + ('flags', ctypes.c_int), + ('xmit_fifo_size', ctypes.c_int), + ('custom_divisor', ctypes.c_int), + ('baud_base', ctypes.c_int), + ('close_delay', ctypes.c_ushort), + ('io_type', ctypes.c_byte), + ('reserved', ctypes.c_byte), + ('hub6', ctypes.c_int), + ('closing_wait', ctypes.c_ushort), + ('closing_wait2', ctypes.c_ushort), + ('iomem_base', ctypes.c_char_p), + ('iomem_reg_shift', ctypes.c_ushort), + ('port_high', ctypes.c_uint), + ('iomap_base', ctypes.c_ulong), + ] + + +def serial_set_low_latency(sp): + import fcntl + import termios + + ASYNCB_LOW_LATENCY = 13 + + ss = serial_struct() + fcntl.ioctl(sp, termios.TIOCGSERIAL, ss) + ss.flags |= 1 << ASYNCB_LOW_LATENCY # pylint: disable=no-member + fcntl.ioctl(sp, termios.TIOCSSERIAL, ss) + + +class AVR(object): + def __init__(self, ctrl): + self.ctrl = ctrl + self.log = ctrl.log.get('AVR') + + self.sp = None + self.i2c_addr = ctrl.args.avr_addr + self.read_cb = None + self.write_cb = None + + + def close(self): pass + + + def _start(self): + try: + self.sp = serial.Serial(self.ctrl.args.serial, self.ctrl.args.baud, + rtscts = 1, timeout = 0, write_timeout = 0) + self.sp.nonblocking() + #serial_set_low_latency(self.sp) + + except Exception as e: + self.sp = None + self.log.warning('Failed to open serial port: %s', e) + + if self.sp is not None: + self.ctrl.ioloop.add_handler(self.sp, self._serial_handler, + self.ctrl.ioloop.READ) + + + def set_handlers(self, read_cb, write_cb): + if self.read_cb is not None or self.write_cb is not None: + raise Exception('Handler already set') + + self.read_cb = read_cb + self.write_cb = write_cb + self._start() + + + def enable_write(self, enable): + if self.sp is None: return + + flags = self.ctrl.ioloop.READ + if enable: flags |= self.ctrl.ioloop.WRITE + self.ctrl.ioloop.update_handler(self.sp, flags) + + + def _serial_write(self): + self.write_cb(lambda data: self.sp.write(data)) + + + def _serial_read(self): + try: + data = '' + data = self.sp.read(self.sp.in_waiting) + self.read_cb(data) + + except Exception as e: + self.log.warning('%s: %s', e, data) + + + def _serial_handler(self, fd, events): + try: + if self.ctrl.ioloop.READ & events: self._serial_read() + if self.ctrl.ioloop.WRITE & events: self._serial_write() + + except Exception as e: + self.log.warning('Serial handler error: %s', traceback.format_exc()) + + + def i2c_command(self, cmd, byte = None, word = None, block = None): + self.log.info('I2C: %s b=%s w=%s d=%s' % (cmd, byte, word, block)) + retry = 10 + cmd = ord(cmd[0]) + + while True: + try: + self.ctrl.i2c.write(self.i2c_addr, cmd, byte, word, block) + break + + except Exception as e: + retry -= 1 + + if retry: + self.log.warning('I2C failed, retrying: %s' % e) + time.sleep(0.25) + continue + + else: + self.log.error('I2C failed: %s' % e) + raise diff --git a/src/py/bbctrl/AVREmu.py b/src/py/bbctrl/AVREmu.py new file mode 100644 index 0000000..7f6be71 --- /dev/null +++ b/src/py/bbctrl/AVREmu.py @@ -0,0 +1,207 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import sys +import traceback +import signal + +import bbctrl +import bbctrl.Cmd as Cmd + + +class AVREmu(object): + def __init__(self, ctrl): + self.ctrl = ctrl + self.log = ctrl.log.get('AVREmu') + + self.avrOut = None + self.avrIn = None + self.i2cOut = None + self.read_cb = None + self.write_cb = None + self.pid = None + + + def close(self): + # Close pipes + def _close(fd, withHandle): + if fd is None: return + try: + if withHandle: self.ctrl.ioloop.remove_handler(fd) + except: pass + try: + os.close(fd) + except: pass + + _close(self.avrOut, True) + _close(self.avrIn, True) + _close(self.i2cOut, False) + + self.avrOut, self.avrIn, self.i2cOut = None, None, None + + # Kill process and wait for it + if self.pid is not None: + os.kill(self.pid, signal.SIGKILL) + os.waitpid(self.pid, 0) + self.pid = None + + + def _start(self): + try: + self.close() + + # Create pipes + stdinFDs = os.pipe() + stdoutFDs = os.pipe() + i2cFDs = os.pipe() + + self.pid = os.fork() + + if not self.pid: + # Dup child ends + os.dup2(stdinFDs[0], 0) + os.dup2(stdoutFDs[1], 1) + os.dup2(i2cFDs[0], 3) + + # Close orig fds + os.close(stdinFDs[0]) + os.close(stdoutFDs[1]) + os.close(i2cFDs[0]) + + # Close parent ends + os.close(stdinFDs[1]) + os.close(stdoutFDs[0]) + os.close(i2cFDs[1]) + + cmd = ['bbemu'] + if self.ctrl.args.fast_emu: cmd.append('--fast') + + os.execvp(cmd[0], cmd) + os._exit(1) # In case of failure + + # Parent, close child ends + os.close(stdinFDs[0]) + os.close(stdoutFDs[1]) + os.close(i2cFDs[0]) + + # Non-blocking IO + os.set_blocking(stdinFDs[1], False) + os.set_blocking(stdoutFDs[0], False) + os.set_blocking(i2cFDs[1], False) + + self.avrOut = stdinFDs[1] + self.avrIn = stdoutFDs[0] + self.i2cOut = i2cFDs[1] + + ioloop = self.ctrl.ioloop + ioloop.add_handler(self.avrOut, self._avr_write_handler, + ioloop.WRITE | ioloop.ERROR) + ioloop.add_handler(self.avrIn, self._avr_read_handler, + ioloop.READ | ioloop.ERROR) + + self.write_enabled = True + + except Exception: + self.close() + self.log.exception('Failed to start bbemu') + + + def set_handlers(self, read_cb, write_cb): + if self.read_cb is not None or self.write_cb is not None: + raise Exception('AVR handler already set') + + self.read_cb = read_cb + self.write_cb = write_cb + self._start() + + + def enable_write(self, enable): + if self.avrOut is None: return + + flags = self.ctrl.ioloop.WRITE if enable else 0 + self.ctrl.ioloop.update_handler(self.avrOut, flags) + self.write_enabled = enable + + + def _avr_write(self, data): + try: + length = os.write(self.avrOut, data) + self.continue_write = length and length == len(data) + return length + + except BlockingIOError: pass + except BrokenPipeError: pass + + return 0 + + + def _avr_write_handler(self, fd, events): + if self.avrOut is None: return + + if events & self.ctrl.ioloop.ERROR: + self._start() + return + + try: + while True: + self.continue_write = False + self.write_cb(self._avr_write) + if not self.continue_write: break + + except Exception as e: + self.log.warning('AVR write handler error: %s', + traceback.format_exc()) + + + def _avr_read_handler(self, fd, events): + if self.avrIn is None: return + + if events & self.ctrl.ioloop.ERROR: + self._start() + return + + try: + data = os.read(self.avrIn, 4096) + if data is not None: self.read_cb(data) + + except Exception as e: + self.log.warning('AVR read handler error: %s %s' % + (data, traceback.format_exc())) + + + def i2c_command(self, cmd, byte = None, word = None, block = None): + if byte is not None: data = chr(byte) + elif word is not None: data = word + elif block is not None: data = block + else: data = '' + + try: + if self.i2cOut is not None: + os.write(self.i2cOut, bytes(cmd + data + '\n', 'utf-8')) + + except BrokenPipeError: pass diff --git a/src/py/bbctrl/Camera.py b/src/py/bbctrl/Camera.py new file mode 100644 index 0000000..17f1bb7 --- /dev/null +++ b/src/py/bbctrl/Camera.py @@ -0,0 +1,510 @@ +#!/usr/bin/env python3 +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import fcntl +import select +import struct +import mmap +import pyudev +import base64 +import socket +import ctypes +from tornado import gen, web, iostream +import bbctrl + +try: + import v4l2 +except: + import bbctrl.v4l2 as v4l2 + + +def array_to_string(a): + def until_zero(a): + for c in a: + if c == 0: return + yield c + + return ''.join([chr(i) for i in until_zero(a)]) + + +def fourcc_to_string(i): + return \ + chr((i >> 0) & 0xff) + \ + chr((i >> 8) & 0xff) + \ + chr((i >> 16) & 0xff) + \ + chr((i >> 24) & 0xff) + + +def string_to_fourcc(s): return v4l2.v4l2_fourcc(s[0], s[1], s[2], s[3]) + + +def format_frame(frame): + frame = [b'--', VideoHandler.boundary.encode('utf8'), b'\r\n', + b'Content-type: image/jpeg\r\n', + b'Content-length: %d\r\n\r\n' % len(frame), frame] + return b''.join(frame) + + +def get_image_resource(path): + path = bbctrl.get_resource(path) + + with open(path, 'rb') as f: + return format_frame(f.read()) + + +class VideoDevice(object): + def __init__(self, path = '/dev/video0'): + self.fd = os.open(path, os.O_RDWR | os.O_NONBLOCK | os.O_CLOEXEC) + self.buffers = [] + + + def fileno(self): return self.fd + + + def get_audio(self): + b = v4l2.v4l2_audio() + b.index = 0 + + l = [] + + while True: + try: + fcntl.ioctl(self, v4l2.VIDIOC_ENUMAUDIO, b) + l.append((array_to_string(b.name), b.capability, b.mode)) + b.index += 1 + + except OSError: break + + return l + + + def get_formats(self): + b = v4l2.v4l2_fmtdesc() + b.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE + b.index = 0 + + l = [] + + while True: + try: + fcntl.ioctl(self, v4l2.VIDIOC_ENUM_FMT, b) + + l.append((fourcc_to_string(b.pixelformat), + array_to_string(b.description))) + + b.index += 1 + + except OSError: break + + return l + + + def get_frame_sizes(self, fourcc): + b = v4l2.v4l2_frmsizeenum() + b.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE + b.pixel_format = fourcc + + sizes = [] + + while True: + try: + fcntl.ioctl(self, v4l2.VIDIOC_ENUM_FRAMESIZES, b) + + if b.type == v4l2.V4L2_FRMSIZE_TYPE_DISCRETE: + sizes.append((b.discrete.width, b.discrete.height)) + + else: + sizes.append((b.stepwise.min_width, b.stepwise.max_width, + b.stepwise.step_width, b.stepwise.min_height, + b.stepwise.max_height, + b.stepwise.step_height)) + + b.index += 1 # pylint: disable=no-member + + except OSError: break + + return sizes + + + def set_format(self, width, height, fourcc): + fmt = v4l2.v4l2_format() + fmt.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE + fcntl.ioctl(self, v4l2.VIDIOC_G_FMT, fmt) + + fmt.fmt.pix.width = width + fmt.fmt.pix.height = height + fmt.fmt.pix.pixelformat = fourcc + + fcntl.ioctl(self, v4l2.VIDIOC_S_FMT, fmt) + + + def create_buffers(self, count): + # Create buffers + rbuf = v4l2.v4l2_requestbuffers() + rbuf.count = count; + rbuf.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE; + rbuf.memory = v4l2.V4L2_MEMORY_MMAP; + + fcntl.ioctl(self, v4l2.VIDIOC_REQBUFS, rbuf) + + for i in range(rbuf.count): + # Get buffer + buf = v4l2.v4l2_buffer() + buf.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE + buf.memory = v4l2.V4L2_MEMORY_MMAP + buf.index = i + fcntl.ioctl(self, v4l2.VIDIOC_QUERYBUF, buf) + + # Mem map buffer + mm = mmap.mmap(self.fileno(), buf.length, mmap.MAP_SHARED, + mmap.PROT_READ | mmap.PROT_WRITE, + offset = buf.m.offset) + self.buffers.append(mm) + + # Queue the buffer for capture + fcntl.ioctl(self, v4l2.VIDIOC_QBUF, buf) + + + def _dqbuf(self): + buf = v4l2.v4l2_buffer() + buf.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE + buf.memory = v4l2.V4L2_MEMORY_MMAP + fcntl.ioctl(self, v4l2.VIDIOC_DQBUF, buf) + + return buf + + + def _qbuf(self, buf): + fcntl.ioctl(self, v4l2.VIDIOC_QBUF, buf) + + + def read_frame(self): + buf = self._dqbuf() + mm = self.buffers[buf.index] + + frame = mm.read(buf.bytesused) + mm.seek(0) + self._qbuf(buf) + + return frame + + + def flush_frame(self): self._qbuf(self._dqbuf()) + + + def get_info(self): + caps = v4l2.v4l2_capability() + fcntl.ioctl(self, v4l2.VIDIOC_QUERYCAP, caps) + + caps._driver = array_to_string(caps.driver) + caps._card = array_to_string(caps.card) + caps._bus_info = array_to_string(caps.bus_info) + + l = [] + c = caps.capabilities + if c & v4l2.V4L2_CAP_VIDEO_CAPTURE: l.append('video_capture') + if c & v4l2.V4L2_CAP_VIDEO_OUTPUT: l.append('video_output') + if c & v4l2.V4L2_CAP_VIDEO_OVERLAY: l.append('video_overlay') + if c & v4l2.V4L2_CAP_VBI_CAPTURE: l.append('vbi_capture') + if c & v4l2.V4L2_CAP_VBI_OUTPUT: l.append('vbi_output') + if c & v4l2.V4L2_CAP_SLICED_VBI_CAPTURE: l.append('sliced_vbi_capture') + if c & v4l2.V4L2_CAP_SLICED_VBI_OUTPUT: l.append('sliced_vbi_output') + if c & v4l2.V4L2_CAP_RDS_CAPTURE: l.append('rds_capture') + if c & v4l2.V4L2_CAP_VIDEO_OUTPUT_OVERLAY: + l.append('video_output_overlay') + if c & v4l2.V4L2_CAP_HW_FREQ_SEEK: l.append('hw_freq_seek') + if c & v4l2.V4L2_CAP_RDS_OUTPUT: l.append('rds_output') + if c & v4l2.V4L2_CAP_TUNER: l.append('tuner') + if c & v4l2.V4L2_CAP_AUDIO: l.append('audio') + if c & v4l2.V4L2_CAP_RADIO: l.append('radio') + if c & v4l2.V4L2_CAP_MODULATOR: l.append('modulator') + if c & v4l2.V4L2_CAP_READWRITE: l.append('readwrite') + if c & v4l2.V4L2_CAP_ASYNCIO: l.append('asyncio') + if c & v4l2.V4L2_CAP_STREAMING: l.append('streaming') + caps._caps = l + + return caps + + + def set_fps(self, fps): + setfps = v4l2.v4l2_streamparm() + setfps.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE; + setfps.parm.capture.timeperframe.numerator = 1 + setfps.parm.capture.timeperframe.denominator = fps + fcntl.ioctl(self, v4l2.VIDIOC_S_PARM, setfps) + + + def start(self): + buf_type = v4l2.v4l2_buf_type(v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE) + fcntl.ioctl(self, v4l2.VIDIOC_STREAMON, buf_type) + + + def stop(self): + buf_type = v4l2.v4l2_buf_type(v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE) + fcntl.ioctl(self, v4l2.VIDIOC_STREAMOFF, buf_type) + + + def close(self): + if self.fd is None: return + try: + os.close(self.fd) + finally: self.fd = None + + +class Camera(object): + def __init__(self, ioloop, args, log): + self.ioloop = ioloop + self.log = log.get('Camera') + + self.width = args.width + self.height = args.height + self.fps = args.fps + self.fourcc = 'MJPG' + self.max_clients = args.camera_clients + + self.overtemp = False + self.dev = None + self.clients = [] + self.path = None + self.have_camera = False + + # Find connected cameras + for i in range(4): + path = '/dev/video%d' % i + if os.path.exists(path): + self.have_camera = True + self.open(path) + break + + # Get notifications of camera (un)plug events + self.udevCtx = pyudev.Context() + self.udevMon = pyudev.Monitor.from_netlink(self.udevCtx) + self.udevMon.filter_by(subsystem = 'video4linux') + ioloop.add_handler(self.udevMon, self._udev_handler, ioloop.READ) + self.udevMon.start() + + + def _udev_handler(self, fd, events): + action, device = self.udevMon.receive_device() + if device is None or self.dev is not None: return + + path = str(device.device_node) + + if action == 'add': + self.have_camera = True + self.open(path) + + if action == 'remove' and path == self.path: + self.have_camera = False + self.close() + + + def _send_frame(self, frame): + if not len(self.clients): return + + try: + frame = format_frame(frame) + for i in range(self.max_clients): + if i < len(self.clients): + self.clients[-(i + 1)].write_frame(frame) + + except Exception as e: + self.log.warning('Failed to write frame to client: %s' % e) + + + def _fd_handler(self, fd, events): + try: + if len(self.clients): + frame = self.dev.read_frame() + self._send_frame(frame) + + else: self.dev.flush_frame() + + except Exception as e: + if isinstance(e, BlockingIOError): return + + self.log.warning('Failed to read from camera.') + self.ioloop.remove_handler(fd) + self.close() + + + def _update_client_image(self): + if self.have_camera and not self.overtemp: return + if self.overtemp and self.have_camera: img = 'overtemp' + else: img = 'offline' + + if len(self.clients): self.clients[-1].write_img(img) + + + def open(self, path): + try: + self._update_client_image() + self.path = path + if self.overtemp: return + self.dev = VideoDevice(path) + + caps = self.dev.get_info() + self.log.info('%s, %s, %s, %s', caps._driver, caps._card, + caps._bus_info, caps._caps) + + if caps.capabilities & v4l2.V4L2_CAP_VIDEO_CAPTURE == 0: + raise Exception('Video capture not supported.') + + fourcc = string_to_fourcc(self.fourcc) + formats = self.dev.get_formats() + sizes = self.dev.get_frame_sizes(fourcc) + + self.log.info('Formats: %s', formats) + self.log.info('Sizes: %s', sizes) + self.log.info('Audio: %s', self.dev.get_audio()) + + hasFormat = False + for name, description in formats: + if name == self.fourcc: hasFormat = True + + if not hasFormat: + raise Exception(self.fourcc + ' video format not supported.') + + self.dev.set_format(self.width, self.height, fourcc = fourcc) + self.dev.set_fps(self.fps) + self.dev.create_buffers(4) + self.dev.start() + + self.ioloop.add_handler(self.dev, self._fd_handler, + self.ioloop.READ) + + self.log.info('Opened camera ' + path) + + + except Exception as e: + self.log.warning('While loading camera: %s' % e) + self._close_dev() + + + def _close_dev(self): + if self.dev is None: return + try: + self.dev.close() + except Exception as e: self.log.warning('While closing camera: %s', e) + + self.dev = None + + + def close(self, overtemp = False): + self._update_client_image() + if self.dev is None: return + + try: + self.ioloop.remove_handler(self.dev) + try: + self.dev.stop() + except: pass + + self._close_dev() + self.log.info('Closed camera') + + except: self.log.exception('Exception while closing camera') + finally: self.dev = None + + + def add_client(self, client): + self.log.info('Adding camera client: %d' % len(self.clients)) + + if self.max_clients <= len(self.clients): + self.clients[-self.max_clients].write_img('in-use') + + self.clients.append(client) + self._update_client_image() + + + def remove_client(self, client): + self.log.info('Removing camera client') + try: + self.clients.remove(client) + except: pass + + + def set_overtemp(self, overtemp): + if self.overtemp == overtemp: return + self.overtemp = overtemp + + if overtemp: self.close(True) + elif self.path is not None: self.open(self.path) + + + +class VideoHandler(web.RequestHandler): + boundary = '-f36a3a39e5c955484390e0e3a6b031d1---' + + + def __init__(self, app, request, **kwargs): + super().__init__(app, request, **kwargs) + self.camera = app.camera + + + @web.asynchronous + def get(self): + self.request.connection.stream.max_write_buffer_size = 10000 + + self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, ' + 'pre-check=0, post-check=0, max-age=0') + self.set_header('Connection', 'close') + self.set_header('Content-Type', 'multipart/x-mixed-replace;boundary=' + + self.boundary) + self.set_header('Expires', 'Mon, 3 Jan 2000 12:34:56 GMT') + self.set_header('Pragma', 'no-cache') + + if self.camera is None: self.write_img('offline') + else: self.camera.add_client(self) + + + def write_img(self, name): + self.write_frame_twice(get_image_resource('http/images/%s.jpg' % name)) + + + def write_frame(self, frame): + # Don't allow too many frames to queue up + min_size = len(frame) * 2 + if self.request.connection.stream.max_write_buffer_size < min_size: + self.request.connection.stream.max_write_buffer_size = min_size + + try: + self.write(frame) + self.flush() + + except iostream.StreamBufferFullError: + pass # Drop frame if buffer is full + + + def write_frame_twice(self, frame): + self.write_frame(frame) + self.write_frame(frame) + + + def on_connection_close(self): self.camera.remove_client(self) diff --git a/src/py/bbctrl/Cmd.py b/src/py/bbctrl/Cmd.py new file mode 100644 index 0000000..88c111b --- /dev/null +++ b/src/py/bbctrl/Cmd.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 + +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import struct +import base64 +import json + +# Keep this in sync with AVR code command.def +SET = '$' +SET_SYNC = '#' +MODBUS_READ = 'm' +MODBUS_WRITE = 'M' +SEEK = 's' +SET_AXIS = 'a' +LINE = 'l' +SYNC_SPEED = '%' +SPEED = 'p' +INPUT = 'I' +DWELL = 'd' +PAUSE = 'P' +STOP = 'S' +UNPAUSE = 'U' +JOG = 'j' +REPORT = 'r' +REBOOT = 'R' +RESUME = 'c' +ESTOP = 'E' +SHUTDOWN = 'X' +CLEAR = 'C' +FLUSH = 'F' +DUMP = 'D' +HELP = 'h' + +SEEK_ACTIVE = 1 << 0 +SEEK_ERROR = 1 << 1 + + +def encode_float(x): + return base64.b64encode(struct.pack('. # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import serial +import json +import time +import traceback +from collections import deque + +import bbctrl +import bbctrl.Cmd as Cmd + + +# Must be kept in sync with drv8711.h +DRV8711_STATUS_OTS_bm = 1 << 0 +DRV8711_STATUS_AOCP_bm = 1 << 1 +DRV8711_STATUS_BOCP_bm = 1 << 2 +DRV8711_STATUS_APDF_bm = 1 << 3 +DRV8711_STATUS_BPDF_bm = 1 << 4 +DRV8711_STATUS_UVLO_bm = 1 << 5 +DRV8711_STATUS_STD_bm = 1 << 6 +DRV8711_STATUS_STDLAT_bm = 1 << 7 +DRV8711_COMM_ERROR_bm = 1 << 8 + +# Ignoring stall and stall latch flags for now +DRV8711_MASK = ~(DRV8711_STATUS_STD_bm | DRV8711_STATUS_STDLAT_bm) + + +def _driver_flags_to_string(flags): + if DRV8711_STATUS_OTS_bm & flags: yield 'over temp' + if DRV8711_STATUS_AOCP_bm & flags: yield 'over current a' + if DRV8711_STATUS_BOCP_bm & flags: yield 'over current b' + if DRV8711_STATUS_APDF_bm & flags: yield 'driver fault a' + if DRV8711_STATUS_BPDF_bm & flags: yield 'driver fault b' + if DRV8711_STATUS_UVLO_bm & flags: yield 'undervoltage' + if DRV8711_STATUS_STD_bm & flags: yield 'stall' + if DRV8711_STATUS_STDLAT_bm & flags: yield 'stall latch' + if DRV8711_COMM_ERROR_bm & flags: yield 'comm error' + + +def driver_flags_to_string(flags): + return ', '.join(_driver_flags_to_string(flags)) + + +class Comm(object): + def __init__(self, ctrl, avr): + self.ctrl = ctrl + self.avr = avr + self.log = self.ctrl.log.get('Comm') + self.queue = deque() + self.in_buf = '' + self.command = None + self.last_motor_flags = [0] * 4 + + avr.set_handlers(self._read, self._write) + self._poll_cb(False) + + + def comm_next(self): raise Exception('Not implemented') + def comm_error(self): raise Exception('Not implemented') + + + def is_active(self): return len(self.queue) or self.command is not None + + + def i2c_command(self, cmd, byte = None, word = None, block = None): + self.log.info('I2C: %s b=%s w=%s d=%s' % (cmd, byte, word, block)) + self.avr.i2c_command(cmd, byte, word, block) + + + def flush(self): self.avr.enable_write(True) + + + def _load_next_command(self, cmd): + self.log.info('< ' + json.dumps(cmd).strip('"')) + self.command = bytes(cmd.strip() + '\n', 'utf-8') + + + def resume(self): self.queue_command(Cmd.RESUME) + + + def queue_command(self, cmd): + self.queue.append(cmd) + self.flush() + + + def _poll_cb(self, now = True): + # Checks periodically for new commands from planner via comm_next() + if now: self.flush() + self.ctrl.ioloop.call_later(1, self._poll_cb) + + + def _write(self, write_cb): + # Finish writing current command + if self.command is not None: + try: + count = write_cb(self.command) + + except Exception as e: + self.command = None + raise e + + self.command = self.command[count:] + if len(self.command): return # There's more + self.command = None + + # Load next command from queue + if len(self.queue): self._load_next_command(self.queue.popleft()) + + # Load next command from callback + else: + cmd = self.comm_next() # pylint: disable=assignment-from-no-return + + if cmd is None: self.avr.enable_write(False) # Stop writing + else: self._load_next_command(cmd) + + + def _update_vars(self, msg): + try: + self.ctrl.state.set_machine_vars(msg['variables']) + self.ctrl.configure() + self.queue_command(Cmd.DUMP) # Refresh all vars + + # Set axis positions + for axis in 'xyzabc': + position = self.ctrl.state.get(axis + 'p', 0) + self.queue_command(Cmd.set_axis(axis, position)) + + except Exception as e: + self.log.warning('AVR reload failed: %s', traceback.format_exc()) + self.ctrl.ioloop.call_later(1, self.connect) + + + def _log_msg(self, msg): + level = msg.get('level', 'info') + where = msg.get('where') + msg = msg['msg'] + + if level == 'info': self.log.info(msg, where = where) + elif level == 'debug': self.log.debug(msg, where = where) + elif level == 'warning': self.log.warning(msg, where = where) + elif level == 'error': self.log.error(msg, where = where) + + if level == 'error': self.comm_error() + + # Treat machine alarmed warning as an error + if level == 'warning' and 'code' in msg and msg['code'] == 11: + self.comm_error() + + + def _log_motor_flags(self, update): + for motor in range(3): + var = '%ddf' % motor + + if var in update: + flags = update[var] & DRV8711_MASK + + if self.last_motor_flags[motor] == flags: continue + self.last_motor_flags[motor] = flags + + flags = driver_flags_to_string(flags) + self.log.info('Motor %d flags: %s' % (motor, flags)) + + + def _update_state(self, update): + self.ctrl.state.update(update) + + if 'xx' in update: # State change + self.ctrl.ready() # We've received data from AVR + self.flush() # May have more data to send now + + self._log_motor_flags(update) + + + def _read(self, data): + self.in_buf += data.decode('utf-8') + + # Parse incoming serial data into lines + while True: + i = self.in_buf.find('\n') + if i == -1: break + line = self.in_buf[0:i].strip() + self.in_buf = self.in_buf[i + 1:] + + if line: + self.log.info('> ' + line) + + try: + msg = json.loads(line) + + except Exception as e: + self.log.warning('%s, data: %s', e, line) + continue + + if 'variables' in msg: self._update_vars(msg) + elif 'msg' in msg: self._log_msg(msg) + + elif 'firmware' in msg: + self.log.info('AVR firmware rebooted') + self.connect() + + else: self._update_state(msg) + + + def estop(self): + if self.ctrl.state.get('xx', '') != 'ESTOPPED': + self.i2c_command(Cmd.ESTOP) + + + def clear(self): + if self.ctrl.state.get('xx', '') == 'ESTOPPED': + self.i2c_command(Cmd.CLEAR) + + + def pause(self): + self.i2c_command(Cmd.PAUSE, byte = ord('0')) # User pause + + + def reboot(self): self.queue_command(Cmd.REBOOT) + + + def connect(self): + try: + # Resume once current queue of GCode commands has flushed + self.queue_command(Cmd.RESUME) + self.queue_command(Cmd.HELP) # Load AVR commands and variables + + except Exception as e: + self.log.warning('Connect failed: %s', e) + self.ctrl.ioloop.call_later(1, self.connect) diff --git a/src/py/bbctrl/CommandQueue.py b/src/py/bbctrl/CommandQueue.py new file mode 100644 index 0000000..c167c57 --- /dev/null +++ b/src/py/bbctrl/CommandQueue.py @@ -0,0 +1,84 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import bbctrl +from collections import deque + + +# 16-bit less with wrap around +def id_less(a, b): return (1 << 15) < (a - b) & ((1 << 16) - 1) + + +class CommandQueue(): + def __init__(self, ctrl): + self.log = ctrl.log.get('CmdQ') + self.log.set_level(bbctrl.log.WARNING) + + self.lastEnqueueID = 0 + self.releaseID = 0 + self.q = deque() + + + def is_active(self): return len(self.q) + + + def clear(self): + self.lastEnqueueID = 0 + self.releaseID = 0 + self.q.clear() + + + def enqueue(self, id, cb, *args, **kwargs): + self.log.info('add(#%d) releaseID=%d', id, self.releaseID) + self.lastEnqueueID = id + self.q.append([id, cb, args, kwargs]) + self._release() + + + def _release(self): + while len(self.q): + id, cb, args, kwargs = self.q[0] + + # Execute commands <= releaseID + if id_less(self.releaseID, id): return + + self.log.info('releasing id=%d' % id) + self.q.popleft() + + try: + if cb is not None: cb(*args, **kwargs) + except Exception: + self.log.exception('During command queue callback') + + + + def release(self, id): + if id and not id_less(self.releaseID, id): + self.log.debug('id out of order %d <= %d' % (id, self.releaseID)) + self.releaseID = id + + self._release() diff --git a/src/py/bbctrl/Config.py b/src/py/bbctrl/Config.py new file mode 100644 index 0000000..0034cfe --- /dev/null +++ b/src/py/bbctrl/Config.py @@ -0,0 +1,244 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import json +import pkg_resources +import subprocess +import copy +from pkg_resources import Requirement, resource_filename + + +def get_resource(path): + return resource_filename(Requirement.parse('bbctrl'), 'bbctrl/' + path) + + +class Config(object): + def __init__(self, ctrl): + self.ctrl = ctrl + self.log = ctrl.log.get('Config') + + self.values = {} + + try: + self.version = pkg_resources.require('bbctrl')[0].version + + # Load config template + with open(get_resource('http/config-template.json'), 'r', + encoding = 'utf-8') as f: + self.template = json.load(f) + + except Exception: self.log.exception() + + + def get(self, name, default = None): + return self.values.get(name, default) + + + def get_index(self, name, index, default = None): + return self.values.get(name, {}).get(str(index), None) + + + def load(self): + path = self.ctrl.get_path('config.json') + + try: + if os.path.exists(path): + with open(path, 'r') as f: config = json.load(f) + else: config = {'version': self.version} + + try: + self.upgrade(config) + except Exception: self.log.exception() + + except Exception as e: + self.log.warning('%s', e) + config = {'version': self.version} + + self._defaults(config) + return config + + + def _valid_value(self, template, value): + type = template['type'] + + try: + if type == 'int': value = int(value) + if type == 'float': value = float(value) + if type == 'text': value = str(value) + if type == 'bool': value = bool(value) + except: + return False + + if 'values' in template and value not in template['values']: + return False + + return True + + + def __defaults(self, config, name, template): + if 'type' in template: + if (not name in config or + not self._valid_value(template, config[name])): + config[name] = template['default'] + + elif 'max' in template and template['max'] < config[name]: + config[name] = template['max'] + + elif 'min' in template and config[name] < template['min']: + config[name] = template['min'] + + if template['type'] == 'list': + config = config[name] + + for i in range(len(template['index'])): + if len(config) <= i: config.append({}) + + for name, tmpl in template['template'].items(): + self.__defaults(config[i], name, tmpl) + + else: + for name, tmpl in template.items(): + self.__defaults(config, name, tmpl) + + + def _defaults(self, config): + for name, tmpl in self.template.items(): + if not 'type' in tmpl: + if not name in config: config[name] = {} + conf = config[name] + else: conf = config + + self.__defaults(conf, name, tmpl) + + + def upgrade(self, config): + version = tuple(map(int, config['version'].split('.'))) + + if version < (0, 2, 4): + for motor in config['motors']: + for key in 'max-jerk max-velocity'.split(): + if key in motor: motor[key] /= 1000 + + if version < (0, 3, 4): + for motor in config['motors']: + for key in 'max-accel latch-velocity search-velocity'.split(): + if key in motor: motor[key] /= 1000 + + if version <= (0, 3, 22): + if 'tool' in config: + if 'spindle-type' in config['tool']: + type = config['tool']['spindle-type'] + if type == 'PWM': type = 'PWM Spindle' + if type == 'Huanyang': type = 'Huanyang VFD' + config['tool']['tool-type'] = type + del config['tool']['spindle-type'] + + if 'spin-reversed' in config['tool']: + reversed = config['tool']['spin-reversed'] + config['tool']['tool-reversed'] = reversed + del config['tool']['spin-reversed'] + + if version <= (0, 4, 6): + for motor in config['motors']: + if 2 < motor.get('idle-current', 0): motor['idle-current'] = 2 + if 'enabled' not in motor: + motor['enabled'] = motor.get('power-mode', '') != 'disabled' + + config['version'] = self.version + + + def save(self, config): + self.upgrade(config) + self._update(config, False) + + with open(self.ctrl.get_path('config.json'), 'w') as f: + json.dump(config, f) + + os.sync() + + self.ctrl.preplanner.invalidate_all() + self.log.info('Saved') + + + def reset(self): + if os.path.exists('config.json'): os.unlink('config.json') + self.reload() + self.ctrl.preplanner.invalidate_all() + + + def _encode(self, name, index, config, tmpl, with_defaults): + # Handle category + if not 'type' in tmpl: + for name, entry in tmpl.items(): + if 'type' in entry and config is not None: + conf = config.get(name, None) + else: conf = config + self._encode(name, index, conf, entry, with_defaults) + return + + # Handle defaults + if config is not None: value = config + elif with_defaults: value = tmpl['default'] + else: return + + # Handle list + if tmpl['type'] == 'list': + for i in range(len(tmpl['index'])): + if config is not None and i < len(config): conf = config[i] + else: conf = None + self._encode(name, index + tmpl['index'][i], conf, + tmpl['template'], with_defaults) + return + + # Update config values + if index: + if not name in self.values: self.values[name] = {} + self.values[name][index] = value + + else: self.values[name] = value + + # Update state variable + if not 'code' in tmpl: return + + if tmpl['type'] == 'enum': + if value in tmpl['values']: value = tmpl['values'].index(value) + else: value = tmpl['default'] + + elif tmpl['type'] == 'bool': value = 1 if value else 0 + elif tmpl['type'] == 'percent': value /= 100.0 + + self.ctrl.state.config(index + tmpl['code'], value) + + + def _update(self, config, with_defaults): + for name, tmpl in self.template.items(): + conf = config.get(name, None) + self._encode(name, '', conf, tmpl, with_defaults) + + + def reload(self): self._update(self.load(), True) diff --git a/src/py/bbctrl/Ctrl.py b/src/py/bbctrl/Ctrl.py new file mode 100644 index 0000000..9503ee2 --- /dev/null +++ b/src/py/bbctrl/Ctrl.py @@ -0,0 +1,115 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import time +import bbctrl + + +class Ctrl(object): + def __init__(self, args, ioloop, id): + self.args = args + self.ioloop = bbctrl.IOLoop(ioloop) + self.id = id + self.timeout = None # Used in demo mode + + if id and not os.path.exists(id): os.mkdir(id) + + # Start log + if args.demo: log_path = self.get_path(filename = 'bbctrl.log') + else: log_path = args.log + self.log = bbctrl.log.Log(args, self.ioloop, log_path) + + self.state = bbctrl.State(self) + self.config = bbctrl.Config(self) + + self.log.get('Ctrl').info('Starting %s' % self.id) + + try: + if args.demo: self.avr = bbctrl.AVREmu(self) + else: self.avr = bbctrl.AVR(self) + + self.i2c = bbctrl.I2C(args.i2c_port, args.demo) + self.lcd = bbctrl.LCD(self) + self.mach = bbctrl.Mach(self, self.avr) + self.preplanner = bbctrl.Preplanner(self) + if not args.demo: self.jog = bbctrl.Jog(self) + self.pwr = bbctrl.Pwr(self) + + self.mach.connect() + + self.lcd.add_new_page(bbctrl.MainLCDPage(self)) + self.lcd.add_new_page(bbctrl.IPLCDPage(self.lcd)) + + os.environ['GCODE_SCRIPT_PATH'] = self.get_upload() + + except Exception: self.log.get('Ctrl').exception() + + + def __del__(self): print('Ctrl deleted') + + + def clear_timeout(self): + if self.timeout is not None: self.ioloop.remove_timeout(self.timeout) + self.timeout = None + + + def set_timeout(self, cb, *args, **kwargs): + self.clear_timeout() + t = self.args.client_timeout + self.timeout = self.ioloop.call_later(t, cb, *args, **kwargs) + + + def get_path(self, dir = None, filename = None): + path = './' + self.id if self.id else '.' + path = path if dir is None else (path + '/' + dir) + return path if filename is None else (path + '/' + filename) + + + def get_upload(self, filename = None): + return self.get_path('upload', filename) + + + def get_plan(self, filename = None): + return self.get_path('plans', filename) + + + def configure(self): + # Indirectly configures state via calls to config() and the AVR + self.config.reload() + + + def ready(self): + # This is used to synchronize the start of the preplanner + self.preplanner.start() + + + def close(self): + self.log.get('Ctrl').info('Closing %s' % self.id) + self.ioloop.close() + self.avr.close() + self.mach.planner.close() diff --git a/src/py/bbctrl/FileHandler.py b/src/py/bbctrl/FileHandler.py new file mode 100644 index 0000000..8fb9d31 --- /dev/null +++ b/src/py/bbctrl/FileHandler.py @@ -0,0 +1,85 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import bbctrl +import glob +import html +from tornado import gen +from tornado.web import HTTPError + + +def safe_remove(path): + try: + os.unlink(path) + except OSError: pass + + +class FileHandler(bbctrl.APIHandler): + def prepare(self): pass + + + def delete_ok(self, filename): + if not filename: + # Delete everything + for path in glob.glob(self.get_upload('*')): safe_remove(path) + self.get_ctrl().preplanner.delete_all_plans() + self.get_ctrl().state.clear_files() + + else: + # Delete a single file + filename = os.path.basename(filename) + safe_remove(self.get_upload(filename)) + self.get_ctrl().preplanner.delete_plans(filename) + self.get_ctrl().state.remove_file(filename) + + + def put_ok(self, *args): + gcode = self.request.files['gcode'][0] + filename = os.path.basename(gcode['filename'].replace('\\', '/')) + filename = filename.replace('#', '-').replace('?', '-') + + if not os.path.exists(self.get_upload()): os.mkdir(self.get_upload()) + + with open(self.get_upload(filename).encode('utf8'), 'wb') as f: + f.write(gcode['body']) + os.sync() + + self.get_ctrl().preplanner.invalidate(filename) + self.get_ctrl().state.add_file(filename) + self.get_log('FileHandler').info('GCode received: ' + filename) + + + @gen.coroutine + def get(self, filename): + if not filename: raise HTTPError(400, 'Missing filename') + filename = os.path.basename(filename) + + with open(self.get_upload(filename).encode('utf8'), 'r') as f: + self.write(f.read()) + + self.get_ctrl().state.select_file(filename) diff --git a/src/py/bbctrl/I2C.py b/src/py/bbctrl/I2C.py new file mode 100644 index 0000000..a97a528 --- /dev/null +++ b/src/py/bbctrl/I2C.py @@ -0,0 +1,91 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import errno + +try: + try: + import smbus + except: + import smbus2 as smbus +except: + smbus = None + + +class I2C(object): + def __init__(self, port, disabled): + self.port = port + self.i2c_bus = None + self.disabled = disabled or smbus is None + + + def connect(self): + if self.disabled: return + if self.i2c_bus is None: + try: + self.i2c_bus = smbus.SMBus(self.port) + + except OSError as e: + self.i2c_bus = None + if e.errno == errno.ENOENT: self.disabled = True + else: raise type(e)('I2C failed to open device: %s' % e) + + + def read_word(self, addr): + self.connect() + if self.disabled: return + + try: + return self.i2c_bus.read_word_data(addr, 0) + + except IOError as e: + self.i2c_bus.close() + self.i2c_bus = None + raise type(e)('I2C read word failed: %s' % e) + + + def write(self, addr, cmd, byte = None, word = None, block = None): + self.connect() + if self.disabled: return + + try: + if byte is not None: + self.i2c_bus.write_byte_data(addr, cmd, byte) + + elif word is not None: + self.i2c_bus.write_word_data(addr, cmd, word) + + elif block is not None: + if isinstance(block, str): block = list(map(ord, block)) + self.i2c_bus.write_i2c_block_data(addr, cmd, block) + + else: self.i2c_bus.write_byte(addr, cmd) + + except IOError as e: + self.i2c_bus.close() + self.i2c_bus = None + raise type(e)('I2C write failed: %s' % e) diff --git a/src/py/bbctrl/IOLoop.py b/src/py/bbctrl/IOLoop.py new file mode 100644 index 0000000..c9c614b --- /dev/null +++ b/src/py/bbctrl/IOLoop.py @@ -0,0 +1,93 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import tornado.ioloop +import bbctrl + + +class CB(object): + def __init__(self, ioloop, delay, cb, *args, **kwargs): + self.ioloop = ioloop + self.cb = cb + + io = ioloop.ioloop + self.h = io.call_later(delay, self._cb, *args, **kwargs) + + ioloop.callbacks[self.h] = self + + def _cb(self, *args, **kwarg): + del self.ioloop.callbacks[self.h] + return self.cb(*args, **kwarg) + + +class IOLoop(object): + READ = tornado.ioloop.IOLoop.READ + WRITE = tornado.ioloop.IOLoop.WRITE + ERROR = tornado.ioloop.IOLoop.ERROR + + + def __init__(self, ioloop): + self.ioloop = ioloop + self.fds = set() + self.handles = set() + self.callbacks = {} + + + def close(self): + for fd in list(self.fds): self.ioloop.remove_handler(fd) + for h in list(self.handles): self.ioloop.remove_timeout(h) + for h in list(self.callbacks): self.ioloop.remove_timeout(h) + + + def add_handler(self, fd, handler, events): + self.ioloop.add_handler(fd, handler, events) + if hasattr(fd, 'fileno'): fd = fd.fileno() + self.fds.add(fd) + + + def remove_handler(self, h): + self.ioloop.remove_handler(h) + if hasattr(h, 'fileno'): h = h.fileno() + self.fds.remove(h) + + + def update_handler(self, fd, events): self.ioloop.update_handler(fd, events) + + + def call_later(self, delay, callback, *args, **kwargs): + cb = CB(self, delay, callback, *args, **kwargs) + return cb.h + + + def remove_timeout(self, h): + self.ioloop.remove_timeout(h) + if h in self.handles: self.handles.remove(h) + if h in self.callbacks: del self.callbacks[h] + + + def add_callback(self, cb, *args, **kwargs): + self.ioloop.add_callback(cb, *args, **kwargs) diff --git a/src/py/bbctrl/IPLCDPage.py b/src/py/bbctrl/IPLCDPage.py new file mode 100644 index 0000000..4f5ee4e --- /dev/null +++ b/src/py/bbctrl/IPLCDPage.py @@ -0,0 +1,48 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import subprocess + +import bbctrl + + +class IPLCDPage(bbctrl.LCDPage): + # From bbctrl.LCDPage + def activate(self): + p = subprocess.Popen(['hostname', '-I'], stdout = subprocess.PIPE) + ips = p.communicate()[0].decode('utf-8').split() + + p = subprocess.Popen(['hostname'], stdout = subprocess.PIPE) + hostname = p.communicate()[0].decode('utf-8').strip() + + self.clear() + + self.text('Host: %s' % hostname[0:14], 0, 0) + + for i in range(min(3, len(ips))): + if len(ips[i]) <= 16: + self.text('IP: %s' % ips[i], 0, i + 1) diff --git a/src/py/bbctrl/Jog.py b/src/py/bbctrl/Jog.py new file mode 100644 index 0000000..c4544e2 --- /dev/null +++ b/src/py/bbctrl/Jog.py @@ -0,0 +1,93 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import inevent +from inevent.Constants import * + + +# Listen for input events +class Jog(inevent.JogHandler): + def __init__(self, ctrl): + self.ctrl = ctrl + self.log = ctrl.log.get('Jog') + + config = { + "Logitech Logitech RumblePad 2 USB": { + "deadband": 0.1, + "axes": [ABS_X, ABS_Y, ABS_RZ, ABS_Z], + "dir": [1, -1, -1, 1], + "arrows": [ABS_HAT0X, ABS_HAT0Y], + "speed": [0x120, 0x121, 0x122, 0x123], + "lock": [0x124, 0x125], + }, + + "default": { + "deadband": 0.1, + "axes": [ABS_X, ABS_Y, ABS_RY, ABS_RX], + "dir": [1, -1, -1, 1], + "arrows": [ABS_HAT0X, ABS_HAT0Y], + "speed": [0x133, 0x130, 0x131, 0x134], + "lock": [0x136, 0x137], + } + } + + super().__init__(config) + + self.v = [0.0] * 4 + self.lastV = self.v + self.callback() + + self.processor = inevent.InEvent(ctrl.ioloop, self, types = ['js']) + + + def up(self): self.ctrl.lcd.page_up() + def down(self): self.ctrl.lcd.page_down() + def left(self): self.ctrl.lcd.page_left() + def right(self): self.ctrl.lcd.page_right() + + + def callback(self): + if self.v != self.lastV: + self.lastV = self.v + try: + axes = {} + for i in range(len(self.v)): axes["xyzabc"[i]] = self.v[i] + self.ctrl.mach.jog(axes) + + except Exception as e: + self.log.warning('Jog: %s', e) + + self.ctrl.ioloop.call_later(0.25, self.callback) + + + def changed(self): + scale = 1.0 + if self.speed == 1: scale = 1.0 / 128.0 + if self.speed == 2: scale = 1.0 / 32.0 + if self.speed == 3: scale = 1.0 / 4.0 + + self.v = [x * scale for x in self.axes] diff --git a/src/py/bbctrl/LCD.py b/src/py/bbctrl/LCD.py new file mode 100644 index 0000000..38f6adb --- /dev/null +++ b/src/py/bbctrl/LCD.py @@ -0,0 +1,207 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import lcd +import atexit + + +class LCDPage: + def __init__(self, lcd, text = None): + self.lcd = lcd + self.data = lcd.new_screen() + + if text is not None: + self.text(text, (lcd.width - len(text)) // 2, 1) + + + def activate(self): pass + def deactivate(self): pass + + + def put(self, c, x, y): + y += x // self.lcd.width + x %= self.lcd.width + y %= self.lcd.height + + if self.data[x][y] != c: + self.data[x][y] = c + if self == self.lcd.page: self.lcd.update() + + + def text(self, s, x, y): + for c in s: + self.put(c, x, y) + x += 1 + + + def clear(self): + self.data = self.lcd.new_screen() + self.lcd.redraw = True + + + def shift_left(self): pass + def shift_right(self): pass + def shift_up(self): pass + def shift_down(self): pass + + +class LCD: + def __init__(self, ctrl): + self.ctrl = ctrl + self.log = ctrl.log.get('LCD') + + self.addrs = self.ctrl.args.lcd_addr + self.addr = self.addrs[0] + self.addr_num = 0 + + self.width = 20 + self.height = 4 + self.lcd = None + self.timeout = None + self.reset = False + self.page = None + self.pages = [] + self.current_page = 0 + self.screen = self.new_screen() + self.set_message('Loading...') + + self._redraw(False) + if not ctrl.args.demo: atexit.register(self.goodbye) + + + def set_message(self, msg): + try: + self.load_page(LCDPage(self, msg)) + self._update() + except IOError as e: + self.log.warning('LCD communication failed: %s' % e) + + + def new_screen(self): + return [[' ' for y in range(self.height)] for x in range(self.width)] + + + def new_page(self): return LCDPage(self) + def add_page(self, page): self.pages.append(page) + + + def add_new_page(self, page = None): + if page is None: page = self.new_page() + page.id = len(self.pages) + self.add_page(page) + return page + + + def load_page(self, page): + if self.page != page: + if self.page is not None: self.page.deactivate() + page.activate() + self.page = page + self.redraw = True + self.update() + + + def set_current_page(self, current_page): + self.current_page = current_page % len(self.pages) + self.load_page(self.pages[self.current_page]) + + + def page_up(self): pass + def page_down(self): pass + def page_right(self): self.set_current_page(self.current_page + 1) + def page_left(self): self.set_current_page(self.current_page - 1) + + + def update(self): + if self.timeout is None: + self.timeout = self.ctrl.ioloop.call_later(0.25, self._update) + + + def _redraw(self, now = True): + if now: + self.redraw = True + self.update() + self.redraw_timer = self.ctrl.ioloop.call_later(5, self._redraw) + + + def _update(self): + self.timeout = None + + try: + if self.lcd is None: + self.lcd = lcd.LCD(self.ctrl.i2c, self.addr, self.height, + self.width) + + if self.reset: + self.lcd.reset() + self.redraw = True + self.reset = False + + cursorX, cursorY = -1, -1 + + for y in range(self.height): + for x in range(self.width): + c = self.page.data[x][y] + + if self.redraw or self.screen[x][y] != c: + if cursorX != x or cursorY != y: + self.lcd.goto(x, y) + cursorX, cursorY = x, y + + self.lcd.put_char(c) + cursorX += 1 + self.screen[x][y] = c + + self.redraw = False + + except IOError as e: + # Try next address + #self.addr_num += 1 + #if len(self.addrs) <= self.addr_num: self.addr_num = 0 + #self.addr = self.addrs[self.addr_num] + #self.lcd = None + + #self.log.warning('LCD communication failed, ' + + # 'retrying on address 0x%02x: %s' % (self.addr, e)) + + #self.log.warning('LCD not present.') + + #self.reset = True + self.reset = False + #self.timeout = self.ctrl.ioloop.call_later(1, self._update) + + + def goodbye(self, message = ''): + if self.timeout: + self.ctrl.ioloop.remove_timeout(self.timeout) + self.timeout = None + + if self.redraw_timer: + self.ctrl.ioloop.remove_timeout(self.redraw_timer) + self.redraw_timer = None + + if self.lcd is not None: self.set_message(message) diff --git a/src/py/bbctrl/Log.py b/src/py/bbctrl/Log.py new file mode 100644 index 0000000..0436375 --- /dev/null +++ b/src/py/bbctrl/Log.py @@ -0,0 +1,185 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import sys +import io +import datetime +import traceback +import pkg_resources +from inspect import getframeinfo, stack +import bbctrl + + +DEBUG = 0 +INFO = 1 +MESSAGE = 2 +WARNING = 3 +ERROR = 4 + + +level_names = 'debug info message warning error'.split() + +def get_level_name(level): return level_names[level] + + +# Get this file's name +_srcfile = os.path.normcase(get_level_name.__code__.co_filename) + + +class Logger(object): + def __init__(self, log, name, level): + self.log = log + self.name = name + self.level = level + + + def set_level(self, level): self.level = level + def _enabled(self, level): return self.level <= level and level <= ERROR + + + def _find_caller(self): + f = sys._getframe() + if f is not None: f = f.f_back + + while hasattr(f, 'f_code'): + co = f.f_code + + filename = os.path.normcase(co.co_filename) + if filename == _srcfile: + f = f.f_back + continue + + return co.co_filename, f.f_lineno, co.co_name + + return '(unknown file)', 0, '(unknown function)' + + + def _log(self, level, msg, *args, **kwargs): + if not self._enabled(level): return + + if not 'where' in kwargs: + filename, line, func = self._find_caller() + kwargs['where'] = '%s:%d' % (os.path.basename(filename), line) + + if len(args): msg %= args + + self.log._log(msg, level = level, prefix = self.name, **kwargs) + + + def debug (self, *args, **kwargs): self._log(DEBUG, *args, **kwargs) + def message(self, *args, **kwargs): self._log(MESSAGE, *args, **kwargs) + def info (self, *args, **kwargs): self._log(INFO, *args, **kwargs) + def warning(self, *args, **kwargs): self._log(WARNING, *args, **kwargs) + def error (self, *args, **kwargs): self._log(ERROR, *args, **kwargs) + + + def exception(self, *args, **kwargs): + msg = traceback.format_exc() + if len(args): msg = args[0] % args[1:] + '\n' + msg + self._log(ERROR, msg, **kwargs) + + +class Log(object): + def __init__(self, args, ioloop, path): + self.path = path + self.listeners = [] + self.loggers = {} + + self.level = DEBUG if args.verbose else INFO + + # Open log, rotate if necessary + self.f = None + self._open() + + # Log header + version = pkg_resources.require('bbctrl')[0].version + self._log('Log started v%s' % version) + self._log_time(ioloop) + + + def get_path(self): return self.path + + def add_listener(self, listener): self.listeners.append(listener) + def remove_listener(self, listener): self.listeners.remove(listener) + + + def get(self, name, level = None): + if not name in self.loggers: + self.loggers[name] = Logger(self, name, self.level) + return self.loggers[name] + + + def _log_time(self, ioloop): + self._log(datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')) + ioloop.call_later(60 * 60, self._log_time, ioloop) + + + def broadcast(self, msg): + for listener in self.listeners: listener(msg) + + + def _log(self, msg, level = INFO, prefix = '', where = None): + if not msg: return + + hdr = '%s:%s:' % ('DIMWE'[level], prefix) + s = hdr + ('\n' + hdr).join(msg.split('\n')) + + if self.f is not None: + if 1e22 <= self.bytes_written + len(s) + 1: self._open() + self.f.write(s + '\n') + self.f.flush() + self.bytes_written += len(s) + 1 + + print(s) + + # Broadcast to log listeners + if level == INFO: return + + msg = dict(level = get_level_name(level), source = prefix, msg = msg) + if where is not None: msg['where'] = where + + self.broadcast(dict(log = msg)) + + + def _open(self): + if self.path is None: return + if self.f is not None: self.f.close() + self._rotate(self.path) + self.f = open(self.path, 'w') + self.bytes_written = 0 + + + def _rotate(self, path, n = None): + fullpath = '%s.%d' % (path, n) if n is not None else path + nextN = (0 if n is None else n) + 1 + + if os.path.exists(fullpath): + if n == 16: os.unlink(fullpath) + else: self._rotate(path, nextN) + + os.rename(fullpath, '%s.%d' % (path, nextN)) diff --git a/src/py/bbctrl/Mach.py b/src/py/bbctrl/Mach.py new file mode 100644 index 0000000..285faa1 --- /dev/null +++ b/src/py/bbctrl/Mach.py @@ -0,0 +1,351 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import bbctrl +from bbctrl.Comm import Comm +import bbctrl.Cmd as Cmd + + +# Axis homing procedure: +# +# Mark axis unhomed +# Set feed rate to search_vel +# Seek closed by search_dist +# Set feed rate to latch_vel +# Seek open latch_backoff +# Seek closed latch_backoff * -1.5 +# Rapid to zero_backoff +# Mark axis homed and set absolute position + +axis_homing_procedure = ''' + G28.2 %(axis)s0 F[#<_%(axis)s_search_velocity>] + G38.6 %(axis)s[#<_%(axis)s_home_travel>] + G38.8 %(axis)s[#<_%(axis)s_latch_backoff>] F[#<_%(axis)s_latch_velocity>] + G38.6 %(axis)s[#<_%(axis)s_latch_backoff> * -8] + G91 G0 G53 %(axis)s[#<_%(axis)s_zero_backoff>] + G90 G28.3 %(axis)s[#<_%(axis)s_home_position>] +''' + +stall_homing_procedure = ''' + G28.2 %(axis)s0 F[#<_%(axis)s_search_velocity>] + G38.6 %(axis)s[#<_%(axis)s_home_travel>] + G91 G1 G53 %(axis)s[#<_%(axis)s_zero_backoff>] F100 + G90 G28.3 %(axis)s[#<_%(axis)s_home_position>] +''' + +motor_fault_error = '''\ +Motor %d driver fault. A potentially damaging electrical condition was \ +detected and the motor driver was shutdown. Please power down the controller \ +and check your motor cabling. See the "Motor Faults" table on the "Indicators" \ +for more information.\ +''' + +def overrides(interface_class): + def overrider(method): + if not method.__name__ in dir(interface_class): + raise Exception('%s does not override %s' % ( + method.__name__, interface_class.__name__)) + + return method + + return overrider + + +class Mach(Comm): + def __init__(self, ctrl, avr): + super().__init__(ctrl, avr) + + self.ctrl = ctrl + self.mlog = self.ctrl.log.get('Mach') + + self.planner = bbctrl.Planner(ctrl) + self.unpausing = False + + ctrl.state.set('cycle', 'idle') + + ctrl.state.add_listener(self._update) + + super().reboot() + + + def _get_state(self): return self.ctrl.state.get('xx', '') + def _is_estopped(self): return self._get_state() == 'ESTOPPED' + def _is_holding(self): return self._get_state() == 'HOLDING' + def _is_ready(self): return self._get_state() == 'READY' + def _get_pause_reason(self): return self.ctrl.state.get('pr', '') + def _get_cycle(self): return self.ctrl.state.get('cycle', 'idle') + + + def _is_paused(self): + if not self._is_holding() or self.unpausing: return False + return self._get_pause_reason() in ( + 'User pause', 'Program pause', 'Optional pause') + + + def _set_cycle(self, cycle): self.ctrl.state.set('cycle', cycle) + + + def _begin_cycle(self, cycle): + current = self._get_cycle() + if current == cycle: return # No change + + if current != 'idle': + raise Exception('Cannot enter %s cycle while in %s cycle' % + (cycle, current)) + + # TODO handle jogging during pause + # if current == 'idle' or (cycle == 'jogging' and self._is_paused()): + self._set_cycle(cycle) + + + def _update(self, update): + # Detect motor faults + for motor in range(4): + key = '%ddf' % motor + if key in update and update[key] & 0x1f: + self.mlog.error(motor_fault_error % motor) + + # Get state + state_changed = 'xc' in update + state = self._get_state() + + # Handle EStop + if state_changed and state == 'ESTOPPED': self.planner.reset(False) + + # Exit cycle if state changed to READY + if (state_changed and self._get_cycle() != 'idle' and + self._is_ready() and not self.planner.is_busy() and + not super().is_active()): + self.planner.position_change() + self._set_cycle('idle') + + # Unpause sync + if state_changed and state != 'HOLDING': self.unpausing = False + + # Entering HOLDING state + if state_changed and state == 'HOLDING': + # Always flush queue after pause + super().i2c_command(Cmd.FLUSH) + super().resume() + + # Automatically unpause after seek or stop hold + # Must be after holding commands above + op = self.ctrl.state.get('optional_pause', False) + pr = self._get_pause_reason() + if ((state_changed or 'pr' in update) and self._is_holding() and + (pr in ('Switch found', 'User stop') or + (pr == 'Optional pause' and not op))): + self._unpause() + + + def _unpause(self): + pause_reason = self._get_pause_reason() + self.mlog.info('Unpause: ' + pause_reason) + + if pause_reason == 'User stop': + self.planner.stop() + self.ctrl.state.set('line', 0) + + else: self.planner.restart() + + super().i2c_command(Cmd.UNPAUSE) + self.unpausing = True + + + def _reset(self): self.planner.reset() + + + def _i2c_block(self, block): + super().i2c_command(block[0], block = block[1:]) + + + def _i2c_set(self, name, value): self._i2c_block(Cmd.set(name, value)) + + + @overrides(Comm) + def comm_next(self): + if self.planner.is_running() and not self._is_holding(): + return self.planner.next() + + + @overrides(Comm) + def comm_error(self): self._reset() + + + @overrides(Comm) + def connect(self): + self._reset() + super().connect() + + + def _query_var(self, cmd): + equal = cmd.find('=') + if equal == -1: + self.mlog.info('%s=%s' % (cmd, self.ctrl.state.get(cmd[1:]))) + + else: + name, value = cmd[1:equal], cmd[equal + 1:] + + if value.lower() == 'true': value = True + elif value.lower() == 'false': value = False + else: + try: + value = float(value) + except: pass + + self.ctrl.state.config(name, value) + + + def mdi(self, cmd, with_limits = True): + if not len(cmd): return + if cmd[0] == '$': self._query_var(cmd) + elif cmd[0] == '\\': super().queue_command(cmd[1:]) + else: + self._begin_cycle('mdi') + self.planner.mdi(cmd, with_limits) + super().resume() + + + def set(self, code, value): + super().queue_command('${}={}'.format(code, value)) + + + def jog(self, axes): + self._begin_cycle('jogging') + self.planner.position_change() + super().queue_command(Cmd.jog(axes)) + + + def home(self, axis, position = None): + state = self.ctrl.state + + if axis is None: axes = 'zxyabc' # TODO This should be configurable + else: axes = '%c' % axis + + for axis in axes: + enabled = state.is_axis_enabled(axis) + mode = state.axis_homing_mode(axis) + + # If this is not a request to home a specific axis and the + # axis is disabled or in manual homing mode, don't show any + # warnings + if 1 < len(axes) and (not enabled or mode == 'manual'): + continue + + # Error when axes cannot be homed + reason = state.axis_home_fail_reason(axis) + if reason is not None: + self.mlog.error('Cannot home %s axis: %s' % ( + axis.upper(), reason)) + continue + + if mode == 'manual': + if position is None: raise Exception('Position not set') + self.mdi('G28.3 %c%f' % (axis, position)) + continue + + # Home axis + self.mlog.info('Homing %s axis' % axis) + self._begin_cycle('homing') + if mode.startswith('stall-'): procedure = stall_homing_procedure + else: procedure = axis_homing_procedure + self.planner.mdi(procedure % {'axis': axis}, False) + + # self.planner.mdi(axis_homing_procedure % {'axis': axis}, False) + super().resume() + + + def unhome(self, axis): self.mdi('G28.2 %c0' % axis) + def estop(self): super().estop() + + + def clear(self): + if self._is_estopped(): + self._reset() + super().clear() + + + def start(self): + filename = self.ctrl.state.get('selected', '') + if not filename: return + self._begin_cycle('running') + self.planner.load(filename) + super().resume() + + + def step(self): + raise Exception('NYI') # TODO + if self._get_cycle() != 'running': self.start() + else: super().i2c_command(Cmd.UNPAUSE) + + + def stop(self): super().i2c_command(Cmd.STOP) + def pause(self): super().pause() + + + def unpause(self): + if self._is_paused(): + self.ctrl.state.set('optional_pause', False) + self._unpause() + + + def optional_pause(self, enable = True): + self.ctrl.state.set('optional_pause', enable) + + + def set_position(self, axis, position): + axis = axis.lower() + state = self.ctrl.state + + if state.is_axis_homed(axis): + # If homed, change the offset rather than the absolute position + self.mdi('G92%s%f' % (axis, position)) + + elif state.is_axis_enabled(axis): + if self._get_cycle() != 'idle' and not self._is_paused(): + raise Exception('Cannot set position during ' + + self._get_cycle()) + + # Set the absolute position both locally and via the AVR + target = position + state.get('offset_' + axis) + state.set(axis + 'p', target) + super().queue_command(Cmd.set_axis(axis, target)) + + + def override_feed(self, override): + self._i2c_set('fo', int(1000 * override)) + + + def override_speed(self, override): + self._i2c_set('so', int(1000 * override)) + + + def modbus_read(self, addr): self._i2c_block(Cmd.modbus_read(addr)) + + + def modbus_write(self, addr, value): + self._i2c_block(Cmd.modbus_write(addr, value)) diff --git a/src/py/bbctrl/MainLCDPage.py b/src/py/bbctrl/MainLCDPage.py new file mode 100644 index 0000000..ba17391 --- /dev/null +++ b/src/py/bbctrl/MainLCDPage.py @@ -0,0 +1,76 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import bbctrl + + +class MainLCDPage(bbctrl.LCDPage): + def __init__(self, ctrl): + bbctrl.LCDPage.__init__(self, ctrl.lcd) + + self.ctrl = ctrl + self.install = True + + ctrl.state.add_listener(self.update) + + + def update(self, update): + state = self.ctrl.state + + # Must be after machine vars have loaded + if self.install and hasattr(self, 'id'): + self.install = False + self.ctrl.lcd.set_current_page(self.id) + + self.text('%-9s' % state.get('xx', ''), 0, 0) + + metric = not state.get('imperial', False) + scale = 1 if metric else 25.4 + + # Show enabled axes + row = 0 + for axis in 'xyzabc': + if state.is_axis_faulted(axis): + self.text(' FAULT %s' % axis.upper(), 9, row) + row += 1 + + elif state.is_axis_enabled(axis): + position = state.get(axis + 'p', 0) + position += state.get('offset_' + axis, 0) + position /= scale + self.text('% 10.3f%s' % (position, axis.upper()), 9, row) + row += 1 + + while row < 4: + self.text(' ' * 11, 9, row) + row += 1 + + # Show tool, units, feed and speed + self.text('%2uT' % state.get('tool', 0), 6, 1) + self.text('%-6s' % 'MM' if metric else 'INCH', 0, 1) + self.text('%8uF' % (state.get('feed', 0) / scale), 0, 2) + self.text('%8dS' % state.get('speed', 0), 0, 3) diff --git a/src/py/bbctrl/MonitorTemp.py b/src/py/bbctrl/MonitorTemp.py new file mode 100644 index 0000000..0d1501d --- /dev/null +++ b/src/py/bbctrl/MonitorTemp.py @@ -0,0 +1,104 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import time + + +def read_temp(): + with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f: + return round(int(f.read()) / 1000) + + +def set_max_freq(freq): + filename = '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq' + with open(filename, 'w') as f: f.write('%d\n' % freq) + + +class MonitorTemp(object): + def __init__(self, app): + self.app = app + + self.ctrl = app.get_ctrl() + self.log = self.ctrl.log.get('Mon') + self.ioloop = self.ctrl.ioloop + + self.last_temp_warn = 0 + self.temp_thresh = 80 + self.min_temp = 60 + self.max_temp = 80 + self.min_freq = 600000 + self.max_freq = 1200000 + self.low_camera_temp = 75 + self.high_camera_temp = 80 + + self.callback() + + + # Scale max CPU based on temperature + def scale_cpu(self, temp): + if temp < self.min_temp: cpu_freq = self.max_freq + elif self.max_temp < temp: cpu_freq = self.min_freq + else: + r = 1 - float(temp - self.min_temp) / \ + (self.max_temp - self.min_temp) + cpu_freq = self.min_freq + (self.max_freq - self.min_freq) * r + + set_max_freq(cpu_freq) + + + def update_camera(self, temp): + if self.app.camera is None: return + + # Disable camera if temp too high + if temp < self.low_camera_temp: self.app.camera.set_overtemp(False) + elif self.high_camera_temp < temp: + self.app.camera.set_overtemp(True) + + + def log_warnings(self, temp): + # Reset temperature warning threshold after timeout + if time.time() < self.last_temp_warn + 60: self.temp_thresh = 80 + + if self.temp_thresh < temp: + self.last_temp_warn = time.time() + self.temp_thresh = temp + + self.log.info('Hot RaspberryPi at %d°C' % temp) + + + def callback(self): + try: + temp = read_temp() + + self.ctrl.state.set('rpi_temp', temp) + self.scale_cpu(temp) + self.update_camera(temp) + self.log_warnings(temp) + + except: self.log.exception() + + self.ioloop.call_later(5, self.callback) diff --git a/src/py/bbctrl/ObjGraph.py b/src/py/bbctrl/ObjGraph.py new file mode 100644 index 0000000..7957bbb --- /dev/null +++ b/src/py/bbctrl/ObjGraph.py @@ -0,0 +1,1223 @@ +""" +Tools for drawing Python object reference graphs with graphviz. + +You can find documentation online at https://mg.pov.lt/objgraph/ + +Copyright (c) 2008-2017 Marius Gedminas and contributors + +Released under the MIT licence. +""" +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +from __future__ import print_function + +import codecs +import collections +import gc +import re +import inspect +import types +import operator +import os +import subprocess +import tempfile +import sys +import itertools + +try: + # Python 2.x compatibility + from StringIO import StringIO +except ImportError: + from io import StringIO + +try: + from types import InstanceType +except ImportError: + # Python 3.x compatibility + InstanceType = None + + +__author__ = "Marius Gedminas (marius@gedmin.as)" +__copyright__ = "Copyright (c) 2008-2017 Marius Gedminas and contributors" +__license__ = "MIT" +__version__ = '3.4.1.dev0' +__date__ = '2018-02-13' + + +try: + basestring +except NameError: + # Python 3.x compatibility + basestring = str + +try: + iteritems = dict.iteritems +except AttributeError: + # Python 3.x compatibility + iteritems = dict.items + +IS_INTERACTIVE = False +try: # pragma: nocover + import graphviz + if get_ipython().__class__.__name__ != 'TerminalInteractiveShell': + IS_INTERACTIVE = True +except (NameError, ImportError): + pass + + +def _isinstance(object, classinfo): + """Return whether an object is an instance of a class or its subclass. + + Differs from the builtin isinstance() implementation in that it does not + depend on the ``__class__`` attribute which is proxied by + mock.Mock(spec=...). + """ + return issubclass(type(object), classinfo) + + +def count(typename, objects=None): + """Count objects tracked by the garbage collector with a given class name. + + The class name can optionally be fully qualified. + + Example: + + >>> count('dict') + 42 + >>> count('mymodule.MyClass') + 2 + + .. note:: + + The Python garbage collector does not track simple + objects like int or str. See + https://docs.python.org/3/library/gc.html#gc.is_tracked + for more information. + + Instead of looking through all objects tracked by the GC, you may + specify your own collection, e.g. + + >>> count('MyClass', get_leaking_objects()) + 3 + + See also: :func:`get_leaking_objects`. + + .. versionchanged:: 1.7 + New parameter: ``objects``. + + .. versionchanged:: 1.8 + Accepts fully-qualified type names (i.e. 'package.module.ClassName') + as well as short type names (i.e. 'ClassName'). + + """ + if objects is None: + objects = gc.get_objects() + try: + if '.' in typename: + return sum(1 for o in objects if _long_typename(o) == typename) + else: + return sum(1 for o in objects if _short_typename(o) == typename) + finally: + del objects # clear cyclic references to frame + + +def typestats(objects=None, shortnames=True, filter=None): + """Count the number of instances for each type tracked by the GC. + + Note that the GC does not track simple objects like int or str. + + Note that classes with the same name but defined in different modules + will be lumped together if ``shortnames`` is True. + + If ``filter`` is specified, it should be a function taking one argument and + returning a boolean. Objects for which ``filter(obj)`` returns ``False`` + will be ignored. + + Example: + + >>> typestats() + {'list': 12041, 'tuple': 10245, ...} + >>> typestats(get_leaking_objects()) + {'MemoryError': 1, 'tuple': 2795, 'RuntimeError': 1, 'list': 47, ...} + + .. versionadded:: 1.1 + + .. versionchanged:: 1.7 + New parameter: ``objects``. + + .. versionchanged:: 1.8 + New parameter: ``shortnames``. + + .. versionchanged:: 3.1.3 + New parameter: ``filter``. + + """ + if objects is None: + objects = gc.get_objects() + try: + if shortnames: + typename = _short_typename + else: + typename = _long_typename + stats = {} + for o in objects: + if filter and not filter(o): + continue + n = typename(o) + stats[n] = stats.get(n, 0) + 1 + return stats + finally: + del objects # clear cyclic references to frame + + +def most_common_types(limit=10, objects=None, shortnames=True, filter=None): + """Count the names of types with the most instances. + + Returns a list of (type_name, count), sorted most-frequent-first. + + Limits the return value to at most ``limit`` items. You may set ``limit`` + to None to avoid that. + + If ``filter`` is specified, it should be a function taking one argument and + returning a boolean. Objects for which ``filter(obj)`` returns ``False`` + will be ignored. + + The caveats documented in :func:`typestats` apply. + + Example: + + >>> most_common_types(limit=2) + [('list', 12041), ('tuple', 10245)] + + .. versionadded:: 1.4 + + .. versionchanged:: 1.7 + New parameter: ``objects``. + + .. versionchanged:: 1.8 + New parameter: ``shortnames``. + + .. versionchanged:: 3.1.3 + New parameter: ``filter``. + + """ + stats = sorted( + typestats(objects, shortnames=shortnames, filter=filter).items(), + key=operator.itemgetter(1), reverse=True) + if limit: + stats = stats[:limit] + return stats + + +def show_most_common_types( + limit=10, + objects=None, + shortnames=True, + file=None, + filter=None): + """Print the table of types of most common instances. + + If ``filter`` is specified, it should be a function taking one argument and + returning a boolean. Objects for which ``filter(obj)`` returns ``False`` + will be ignored. + + The caveats documented in :func:`typestats` apply. + + Example: + + >>> show_most_common_types(limit=5) + tuple 8959 + function 2442 + wrapper_descriptor 1048 + dict 953 + builtin_function_or_method 800 + + .. versionadded:: 1.1 + + .. versionchanged:: 1.7 + New parameter: ``objects``. + + .. versionchanged:: 1.8 + New parameter: ``shortnames``. + + .. versionchanged:: 3.0 + New parameter: ``file``. + + .. versionchanged:: 3.1.3 + New parameter: ``filter``. + + """ + if file is None: + file = sys.stdout + stats = most_common_types(limit, objects, shortnames=shortnames, + filter=filter) + width = max(len(name) for name, count in stats) + for name, count in stats: + file.write('%-*s %i\n' % (width, name, count)) + + +def growth(limit=10, peak_stats={}, shortnames=True, filter=None): + """Count the increase in peak object since last call. + + Returns a list of (type_name, total_count, increase_delta), + descending order by increase_delta. + + Limits the output to ``limit`` largest deltas. You may set ``limit`` to + None to see all of them. + + Uses and updates ``peak_stats``, a dictionary from type names to previously + seen peak object counts. Usually you don't need to pay attention to this + argument. + + If ``filter`` is specified, it should be a function taking one argument and + returning a boolean. Objects for which ``filter(obj)`` returns ``False`` + will be ignored. + + The caveats documented in :func:`typestats` apply. + + Example: + + >>> growth(2) + [(tuple, 12282, 10), (dict, 1922, 7)] + + .. versionadded:: 3.3.0 + + """ + gc.collect() + stats = typestats(shortnames=shortnames, filter=filter) + deltas = {} + for name, count in iteritems(stats): + old_count = peak_stats.get(name, 0) + if count > old_count: + deltas[name] = count - old_count + peak_stats[name] = count + deltas = sorted(deltas.items(), key=operator.itemgetter(1), + reverse=True) + if limit: + deltas = deltas[:limit] + + return [(name, stats[name], delta) for name, delta in deltas] + + +def show_growth(limit=10, peak_stats=None, shortnames=True, file=None, + filter=None): + """Show the increase in peak object counts since last call. + + if ``peak_stats`` is None, peak object counts will recorded in + func `growth`, and your can record the counts by yourself with set + ``peak_stats`` to a dictionary. + + The caveats documented in :func:`growth` apply. + + Example: + + >>> show_growth() + wrapper_descriptor 970 +14 + tuple 12282 +10 + dict 1922 +7 + ... + + .. versionadded:: 1.5 + + .. versionchanged:: 1.8 + New parameter: ``shortnames``. + + .. versionchanged:: 2.1 + New parameter: ``file``. + + .. versionchanged:: 3.1.3 + New parameter: ``filter``. + + """ + if peak_stats is None: + result = growth(limit, shortnames=shortnames, filter=filter) + else: + result = growth(limit, peak_stats, shortnames, filter) + if result: + if file is None: + file = sys.stdout + width = max(len(name) for name, _, _ in result) + for name, count, delta in result: + file.write('%-*s%9d %+9d\n' % (width, name, count, delta)) + + +def get_new_ids(skip_update=False, limit=10, sortby='deltas', + shortnames=None, file=None, _state={}): + """Find and display new objects allocated since last call. + + Shows the increase in object counts since last call to this + function and returns the memory address ids for new objects. + + Returns a dictionary mapping object type names to sets of object IDs + that have been created since the last time this function was called. + + ``skip_update`` (bool): If True, returns the same dictionary that + was returned during the previous call without updating the internal + state or examining the objects currently in memory. + + ``limit`` (int): The maximum number of rows that you want to print + data for. Use 0 to suppress the printing. Use None to print everything. + + ``sortby`` (str): This is the column that you want to sort by in + descending order. Possible values are: 'old', 'current', 'new', + 'deltas' + + ``shortnames`` (bool): If True, classes with the same name but + defined in different modules will be lumped together. If False, + all type names will be qualified with the module name. If None (default), + ``get_new_ids`` will remember the value from previous calls, so it's + enough to prime this once. By default the primed value is True. + + ``_state`` (dict): Stores old, current, and new_ids in memory. + It is used by the function to store the internal state between calls. + Never pass in this argument unless you know what you're doing. + + The caveats documented in :func:`growth` apply. + + When one gets new_ids from :func:`get_new_ids`, one can use + :func:`at_addrs` to get a list of those objects. Then one can iterate over + the new objects, print out what they are, and call :func:`show_backrefs` or + :func:`show_chain` to see where they are referenced. + + Example: + + >>> _ = get_new_ids() # store current objects in _state + >>> _ = get_new_ids() # current_ids become old_ids in _state + >>> a = [0, 1, 2] # list we don't know about + >>> b = [3, 4, 5] # list we don't know about + >>> new_ids = get_new_ids(limit=3) # we see new lists + ====================================================================== + Type Old_ids Current_ids New_ids Count_Deltas + ====================================================================== + list 324 326 +3 +2 + dict 1125 1125 +0 +0 + wrapper_descriptor 1001 1001 +0 +0 + ====================================================================== + >>> new_lists = at_addrs(new_ids['list']) + >>> a in new_lists + True + >>> b in new_lists + True + + .. versionadded:: 3.4 + """ + if not _state: + _state['old'] = collections.defaultdict(set) + _state['current'] = collections.defaultdict(set) + _state['new'] = collections.defaultdict(set) + _state['shortnames'] = True + new_ids = _state['new'] + if skip_update: + return new_ids + old_ids = _state['old'] + current_ids = _state['current'] + if shortnames is None: + shortnames = _state['shortnames'] + else: + _state['shortnames'] = shortnames + gc.collect() + objects = gc.get_objects() + for class_name in old_ids: + old_ids[class_name].clear() + for class_name, ids_set in current_ids.items(): + old_ids[class_name].update(ids_set) + for class_name in current_ids: + current_ids[class_name].clear() + for o in objects: + if shortnames: + class_name = _short_typename(o) + else: + class_name = _long_typename(o) + id_number = id(o) + current_ids[class_name].add(id_number) + for class_name in new_ids: + new_ids[class_name].clear() + rows = [] + keys_to_remove = [] + for class_name in current_ids: + num_old = len(old_ids[class_name]) + num_current = len(current_ids[class_name]) + if num_old == 0 and num_current == 0: + # remove the key from our dicts if we don't have any old or + # current class_name objects + keys_to_remove.append(class_name) + continue + new_ids_set = current_ids[class_name] - old_ids[class_name] + new_ids[class_name].update(new_ids_set) + num_new = len(new_ids_set) + num_delta = num_current - num_old + row = (class_name, num_old, num_current, num_new, num_delta) + rows.append(row) + for key in keys_to_remove: + del old_ids[key] + del current_ids[key] + del new_ids[key] + index_by_sortby = {'old': 1, 'current': 2, 'new': 3, 'deltas': 4} + rows.sort(key=operator.itemgetter(index_by_sortby[sortby], 0), + reverse=True) + if limit is not None: + rows = rows[:limit] + if not rows: + return new_ids + if file is None: + file = sys.stdout + width = max(len(row[0]) for row in rows) + print('='*(width+13*4), file=file) + print('%-*s%13s%13s%13s%13s' % + (width, 'Type', 'Old_ids', 'Current_ids', 'New_ids', 'Count_Deltas'), + file=file) + print('='*(width+13*4), file=file) + for row_class, old, current, new, delta in rows: + print('%-*s%13d%13d%+13d%+13d' % + (width, row_class, old, current, new, delta), file=file) + print('='*(width+13*4), file=file) + return new_ids + + +def get_leaking_objects(objects=None): + """Return objects that do not have any referents. + + These could indicate reference-counting bugs in C code. Or they could + be legitimate. + + Note that the GC does not track simple objects like int or str. + + .. versionadded:: 1.7 + """ + if objects is None: + gc.collect() + objects = gc.get_objects() + try: + ids = set(id(i) for i in objects) + for i in objects: + ids.difference_update(id(j) for j in gc.get_referents(i)) + # this then is our set of objects without referrers + return [i for i in objects if id(i) in ids] + finally: + del objects, i # clear cyclic references to frame + + +def by_type(typename, objects=None): + """Return objects tracked by the garbage collector with a given class name. + + Example: + + >>> by_type('MyClass') + [] + + Note that the GC does not track simple objects like int or str. + + .. versionchanged:: 1.7 + New parameter: ``objects``. + + .. versionchanged:: 1.8 + Accepts fully-qualified type names (i.e. 'package.module.ClassName') + as well as short type names (i.e. 'ClassName'). + + """ + if objects is None: + objects = gc.get_objects() + try: + if '.' in typename: + return [o for o in objects if _long_typename(o) == typename] + else: + return [o for o in objects if _short_typename(o) == typename] + finally: + del objects # clear cyclic references to frame + + +def at(addr): + """Return an object at a given memory address. + + The reverse of id(obj): + + >>> at(id(obj)) is obj + True + + Note that this function does not work on objects that are not tracked by + the GC (e.g. ints or strings). + """ + for o in gc.get_objects(): + if id(o) == addr: + return o + return None + + +def at_addrs(address_set): + """Return a list of objects for a given set of memory addresses. + + The reverse of [id(obj1), id(obj2), ...]. Note that objects are returned + in an arbitrary order. + + When one gets ``new_ids`` from :func:`get_new_ids`, one can use this + function to get a list of those objects. Then one can iterate over the new + objects, print out what they are, and call :func:`show_backrefs` or + :func:`show_chain` to see where they are referenced. + + >>> a = [0, 1, 2] + >>> new_ids = get_new_ids() + >>> new_lists = at_addrs(new_ids['list']) + >>> a in new_lists + True + + Note that this function does not work on objects that are not tracked + by the GC (e.g. ints or strings). + + .. versionadded:: 3.4 + """ + res = [] + for o in gc.get_objects(): + if id(o) in address_set: + res.append(o) + return res + + +def find_ref_chain(obj, predicate, max_depth=20, extra_ignore=()): + """Find a shortest chain of references leading from obj. + + The end of the chain will be some object that matches your predicate. + + ``predicate`` is a function taking one argument and returning a boolean. + + ``max_depth`` limits the search depth. + + ``extra_ignore`` can be a list of object IDs to exclude those objects from + your search. + + Example: + + >>> find_ref_chain(obj, lambda x: isinstance(x, MyClass)) + [obj, ..., ] + + Returns ``[obj]`` if such a chain could not be found. + + .. versionadded:: 1.7 + """ + return _find_chain(obj, predicate, gc.get_referents, + max_depth=max_depth, extra_ignore=extra_ignore)[::-1] + + +def find_backref_chain(obj, predicate, max_depth=20, extra_ignore=()): + """Find a shortest chain of references leading to obj. + + The start of the chain will be some object that matches your predicate. + + ``predicate`` is a function taking one argument and returning a boolean. + + ``max_depth`` limits the search depth. + + ``extra_ignore`` can be a list of object IDs to exclude those objects from + your search. + + Example: + + >>> find_backref_chain(obj, is_proper_module) + [, ..., obj] + + Returns ``[obj]`` if such a chain could not be found. + + .. versionchanged:: 1.5 + Returns ``obj`` instead of ``None`` when a chain could not be found. + + """ + return _find_chain(obj, predicate, gc.get_referrers, + max_depth=max_depth, extra_ignore=extra_ignore) + + +def show_backrefs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10, + highlight=None, filename=None, extra_info=None, + refcounts=False, shortnames=True, output=None): + """Generate an object reference graph ending at ``objs``. + + The graph will show you what objects refer to ``objs``, directly and + indirectly. + + ``objs`` can be a single object, or it can be a list of objects. If + unsure, wrap the single object in a new list. + + ``filename`` if specified, can be the name of a .dot or a image + file, whose extension indicates the desired output format; note + that output to a specific format is entirely handled by GraphViz: + if the desired format is not supported, you just get the .dot + file. If ``filename`` and ``output`` are not specified, ``show_backrefs`` + will try to display the graph inline (if you're using IPython), otherwise + it'll try to produce a .dot file and spawn a viewer (xdot). If xdot is + not available, ``show_backrefs`` will convert the .dot file to a + .png and print its name. + + ``output`` if specified, the GraphViz output will be written to this + file object. ``output`` and ``filename`` should not both be specified. + + Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the + graph. + + Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to + remove undesired objects from the graph. + + Use ``highlight`` (a predicate) to highlight certain graph nodes in blue. + + Use ``extra_info`` (a function taking one argument and returning a + string) to report extra information for objects. + + Specify ``refcounts=True`` if you want to see reference counts. + These will mostly match the number of arrows pointing to an object, + but can be different for various reasons. + + Specify ``shortnames=False`` if you want to see fully-qualified type + names ('package.module.ClassName'). By default you get to see only the + class name part. + + Examples: + + >>> show_backrefs(obj) + >>> show_backrefs([obj1, obj2]) + >>> show_backrefs(obj, max_depth=5) + >>> show_backrefs(obj, filter=lambda x: not inspect.isclass(x)) + >>> show_backrefs(obj, highlight=inspect.isclass) + >>> show_backrefs(obj, extra_ignore=[id(locals())]) + + .. versionchanged:: 1.3 + New parameters: ``filename``, ``extra_info``. + + .. versionchanged:: 1.5 + New parameter: ``refcounts``. + + .. versionchanged:: 1.8 + New parameter: ``shortnames``. + + .. versionchanged:: 2.0 + New parameter: ``output``. + + """ + # For show_backrefs(), it makes sense to stop when reaching a + # module because you'll end up in sys.modules and explode the + # graph with useless clutter. That's why we're specifying + # cull_func here, but not in show_graph(). + return _show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore, + filter=filter, too_many=too_many, highlight=highlight, + edge_func=gc.get_referrers, swap_source_target=False, + filename=filename, output=output, extra_info=extra_info, + refcounts=refcounts, shortnames=shortnames, + cull_func=is_proper_module) + + +def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10, + highlight=None, filename=None, extra_info=None, + refcounts=False, shortnames=True, output=None): + """Generate an object reference graph starting at ``objs``. + + The graph will show you what objects are reachable from ``objs``, directly + and indirectly. + + ``objs`` can be a single object, or it can be a list of objects. If + unsure, wrap the single object in a new list. + + ``filename`` if specified, can be the name of a .dot or a image + file, whose extension indicates the desired output format; note + that output to a specific format is entirely handled by GraphViz: + if the desired format is not supported, you just get the .dot + file. If ``filename`` and ``output`` is not specified, ``show_refs`` will + try to display the graph inline (if you're using IPython), otherwise it'll + try to produce a .dot file and spawn a viewer (xdot). If xdot is + not available, ``show_refs`` will convert the .dot file to a + .png and print its name. + + ``output`` if specified, the GraphViz output will be written to this + file object. ``output`` and ``filename`` should not both be specified. + + Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the + graph. + + Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to + remove undesired objects from the graph. + + Use ``highlight`` (a predicate) to highlight certain graph nodes in blue. + + Use ``extra_info`` (a function returning a string) to report extra + information for objects. + + Specify ``refcounts=True`` if you want to see reference counts. + + Examples: + + >>> show_refs(obj) + >>> show_refs([obj1, obj2]) + >>> show_refs(obj, max_depth=5) + >>> show_refs(obj, filter=lambda x: not inspect.isclass(x)) + >>> show_refs(obj, highlight=inspect.isclass) + >>> show_refs(obj, extra_ignore=[id(locals())]) + + .. versionadded:: 1.1 + + .. versionchanged:: 1.3 + New parameters: ``filename``, ``extra_info``. + + .. versionchanged:: 1.5 + Follows references from module objects instead of stopping. + New parameter: ``refcounts``. + + .. versionchanged:: 1.8 + New parameter: ``shortnames``. + + .. versionchanged:: 2.0 + New parameter: ``output``. + """ + return _show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore, + filter=filter, too_many=too_many, highlight=highlight, + edge_func=gc.get_referents, swap_source_target=True, + filename=filename, extra_info=extra_info, + refcounts=refcounts, shortnames=shortnames, + output=output) + + +def show_chain(*chains, **kw): + """Show a chain (or several chains) of object references. + + Useful in combination with :func:`find_ref_chain` or + :func:`find_backref_chain`, e.g. + + >>> show_chain(find_backref_chain(obj, is_proper_module)) + + You can specify if you want that chain traced backwards or forwards + by passing a ``backrefs`` keyword argument, e.g. + + >>> show_chain(find_ref_chain(obj, is_proper_module), + ... backrefs=False) + + Ideally this shouldn't matter, but for some objects + :func:`gc.get_referrers` and :func:`gc.get_referents` are not perfectly + symmetrical. + + You can specify ``highlight``, ``extra_info``, ``refcounts``, + ``shortnames``, ``filename`` or ``output`` arguments like for + :func:`show_backrefs` or :func:`show_refs`. + + .. versionadded:: 1.5 + + .. versionchanged:: 1.7 + New parameter: ``backrefs``. + + .. versionchanged:: 2.0 + New parameter: ``output``. + + """ + backrefs = kw.pop('backrefs', True) + chains = [chain for chain in chains if chain] # remove empty ones + + def in_chains(x, ids=set(map(id, itertools.chain(*chains)))): + return id(x) in ids + max_depth = max(map(len, chains)) - 1 + if backrefs: + show_backrefs([chain[-1] for chain in chains], max_depth=max_depth, + filter=in_chains, **kw) + else: + show_refs([chain[0] for chain in chains], max_depth=max_depth, + filter=in_chains, **kw) + + +def is_proper_module(obj): + """ + Returns ``True`` if ``obj`` can be treated like a garbage collector root. + + That is, if ``obj`` is a module that is in ``sys.modules``. + + >>> import types + >>> is_proper_module([]) + False + >>> is_proper_module(types) + True + >>> is_proper_module(types.ModuleType('foo')) + False + + .. versionadded:: 1.8 + """ + return ( + inspect.ismodule(obj) + and obj is sys.modules.get(getattr(obj, '__name__', None)) + ) + + +# +# Internal helpers +# + +def _find_chain(obj, predicate, edge_func, max_depth=20, extra_ignore=()): + queue = [obj] + depth = {id(obj): 0} + parent = {id(obj): None} + ignore = set(extra_ignore) + ignore.add(id(extra_ignore)) + ignore.add(id(queue)) + ignore.add(id(depth)) + ignore.add(id(parent)) + ignore.add(id(ignore)) + ignore.add(id(sys._getframe())) # this function + ignore.add(id(sys._getframe(1))) # find_chain/find_backref_chain + gc.collect() + while queue: + target = queue.pop(0) + if predicate(target): + chain = [target] + while parent[id(target)] is not None: + target = parent[id(target)] + chain.append(target) + return chain + tdepth = depth[id(target)] + if tdepth < max_depth: + referrers = edge_func(target) + ignore.add(id(referrers)) + for source in referrers: + if id(source) in ignore: + continue + if id(source) not in depth: + depth[id(source)] = tdepth + 1 + parent[id(source)] = target + queue.append(source) + return [obj] # not found + + +def _show_graph(objs, edge_func, swap_source_target, + max_depth=3, extra_ignore=(), filter=None, too_many=10, + highlight=None, filename=None, extra_info=None, + refcounts=False, shortnames=True, output=None, + cull_func=None): + if not _isinstance(objs, (list, tuple)): + objs = [objs] + + is_interactive = False + if filename and output: + raise ValueError('Cannot specify both output and filename.') + elif output: + f = output + elif filename and filename.endswith('.dot'): + f = codecs.open(filename, 'w', encoding='utf-8') + dot_filename = filename + elif IS_INTERACTIVE: + is_interactive = True + f = StringIO() + else: + fd, dot_filename = tempfile.mkstemp(prefix='objgraph-', + suffix='.dot', text=True) + f = os.fdopen(fd, "w") + if getattr(f, 'encoding', None): + # Python 3 will wrap the file in the user's preferred encoding + # Re-wrap it for utf-8 + import io + f = io.TextIOWrapper(f.detach(), 'utf-8') + f.write('digraph ObjectGraph {\n' + ' node[shape=box, style=filled, fillcolor=white];\n') + queue = [] + depth = {} + ignore = set(extra_ignore) + ignore.add(id(objs)) + ignore.add(id(extra_ignore)) + ignore.add(id(queue)) + ignore.add(id(depth)) + ignore.add(id(ignore)) + ignore.add(id(sys._getframe())) # this function + ignore.add(id(sys._getframe().f_locals)) + ignore.add(id(sys._getframe(1))) # show_refs/show_backrefs + ignore.add(id(sys._getframe(1).f_locals)) + for obj in objs: + f.write(' %s[fontcolor=red];\n' % (_obj_node_id(obj))) + depth[id(obj)] = 0 + queue.append(obj) + del obj + gc.collect() + nodes = 0 + while queue: + nodes += 1 + # The names "source" and "target" are reversed here because + # originally there was just show_backrefs() and we were + # traversing the reference graph backwards. + target = queue.pop(0) + tdepth = depth[id(target)] + f.write(' %s[label="%s"];\n' % (_obj_node_id(target), + _obj_label(target, extra_info, + refcounts, shortnames))) + h, s, v = _gradient((0, 0, 1), (0, 0, .3), tdepth, max_depth) + if inspect.ismodule(target): + h = .3 + s = 1 + if highlight and highlight(target): + h = .6 + s = .6 + v = 0.5 + v * 0.5 + f.write(' %s[fillcolor="%g,%g,%g"];\n' + % (_obj_node_id(target), h, s, v)) + if v < 0.5: + f.write(' %s[fontcolor=white];\n' % (_obj_node_id(target))) + if hasattr(getattr(target, '__class__', None), '__del__'): + f.write(' %s->%s_has_a_del[color=red,style=dotted,' + 'len=0.25,weight=10];\n' % (_obj_node_id(target), + _obj_node_id(target))) + f.write(' %s_has_a_del[label="__del__",shape=doublecircle,' + 'height=0.25,color=red,fillcolor="0,.5,1",fontsize=6];\n' + % (_obj_node_id(target))) + if tdepth >= max_depth: + continue + if cull_func is not None and cull_func(target): + continue + neighbours = edge_func(target) + ignore.add(id(neighbours)) + n = 0 + skipped = 0 + for source in neighbours: + if id(source) in ignore: + continue + if filter and not filter(source): + continue + if n >= too_many: + skipped += 1 + continue + if swap_source_target: + srcnode, tgtnode = target, source + else: + srcnode, tgtnode = source, target + elabel = _edge_label(srcnode, tgtnode, shortnames) + f.write(' %s -> %s%s;\n' % (_obj_node_id(srcnode), + _obj_node_id(tgtnode), elabel)) + if id(source) not in depth: + depth[id(source)] = tdepth + 1 + queue.append(source) + n += 1 + del source + del neighbours + if skipped > 0: + h, s, v = _gradient((0, 1, 1), (0, 1, .3), tdepth + 1, max_depth) + if swap_source_target: + label = "%d more references" % skipped + edge = "%s->too_many_%s" % (_obj_node_id(target), + _obj_node_id(target)) + else: + label = "%d more backreferences" % skipped + edge = "too_many_%s->%s" % (_obj_node_id(target), + _obj_node_id(target)) + f.write(' %s[color=red,style=dotted,len=0.25,weight=10];\n' + % edge) + f.write(' too_many_%s[label="%s",shape=box,height=0.25,' + 'color=red,fillcolor="%g,%g,%g",fontsize=6];\n' + % (_obj_node_id(target), label, h, s, v)) + f.write(' too_many_%s[fontcolor=white];\n' + % (_obj_node_id(target))) + f.write("}\n") + + if output: + return + + if is_interactive: + return graphviz.Source(f.getvalue()) + else: + # The file should only be closed if this function was in charge of + # opening the file. + f.close() + print("Graph written to %s (%d nodes)" % (dot_filename, nodes)) + _present_graph(dot_filename, filename) + + +def _present_graph(dot_filename, filename=None): + """Present a .dot file to the user in the requested fashion. + + If ``filename`` is provided, runs ``dot`` to convert the .dot file + into the desired format, determined by the filename extension. + + If ``filename`` is not provided, tries to launch ``xdot``, a + graphical .dot file viewer. If ``xdot`` is not present on the system, + converts the graph to a PNG. + """ + if filename == dot_filename: + # nothing to do, the user asked for a .dot file and got it + return + if not filename and _program_in_path('xdot'): + print("Spawning graph viewer (xdot)") + subprocess.Popen(['xdot', dot_filename], close_fds=True) + elif _program_in_path('dot'): + if not filename: + print("Graph viewer (xdot) not found, generating a png instead") + filename = dot_filename[:-4] + '.png' + stem, ext = os.path.splitext(filename) + cmd = ['dot', '-T' + ext[1:], '-o' + filename, dot_filename] + dot = subprocess.Popen(cmd, close_fds=False) + dot.wait() + if dot.returncode != 0: + # XXX: shouldn't this go to stderr or a log? + print('dot failed (exit code %d) while executing "%s"' + % (dot.returncode, ' '.join(cmd))) + else: + print("Image generated as %s" % filename) + else: + if not filename: + print("Graph viewer (xdot) and image renderer (dot) not found," + " not doing anything else") + else: + print("Image renderer (dot) not found, not doing anything else") + + +def _obj_node_id(obj): + return ('o%d' % id(obj)).replace('-', '_') + + +def _obj_label(obj, extra_info=None, refcounts=False, shortnames=True): + if shortnames: + label = [_short_typename(obj)] + else: + label = [_long_typename(obj)] + if refcounts: + label[0] += ' [%d]' % (sys.getrefcount(obj) - 4) + # Why -4? To ignore the references coming from + # obj_label's frame (obj) + # show_graph's frame (target variable) + # sys.getrefcount()'s argument + # something else that doesn't show up in gc.get_referrers() + label.append(_safe_repr(obj)) + if extra_info: + label.append(str(extra_info(obj))) + return _quote('\n'.join(label)) + + +def _quote(s): + return (s.replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("\n", "\\n") + .replace("\0", "\\\\0")) + + +def _get_obj_type(obj): + objtype = type(obj) + if type(obj) == InstanceType: + objtype = obj.__class__ + return objtype + + +def _short_typename(obj): + return _get_obj_type(obj).__name__ + + +def _long_typename(obj): + objtype = _get_obj_type(obj) + name = objtype.__name__ + module = getattr(objtype, '__module__', None) + if module: + return '%s.%s' % (module, name) + else: + return name + + +def _safe_repr(obj): + try: + return _short_repr(obj) + except Exception: + return '(unrepresentable)' + + +def _name_or_repr(value): + try: + result = value.__name__ + except AttributeError: + result = repr(value)[:40] + + if _isinstance(result, basestring): + return result + else: + return repr(value)[:40] + + +def _short_repr(obj): + if _isinstance(obj, (type, types.ModuleType, types.BuiltinMethodType, + types.BuiltinFunctionType)): + return _name_or_repr(obj) + if _isinstance(obj, types.MethodType): + name = _name_or_repr(obj.__func__) + if obj.__self__: + return name + ' (bound)' + else: + return name + # NB: types.LambdaType is an alias for types.FunctionType! + if _isinstance(obj, types.LambdaType) and obj.__name__ == '': + return 'lambda: %s:%s' % (os.path.basename(obj.__code__.co_filename), + obj.__code__.co_firstlineno) + if _isinstance(obj, types.FrameType): + return '%s:%s' % (obj.f_code.co_filename, obj.f_lineno) + if _isinstance(obj, (tuple, list, dict, set)): + return '%d items' % len(obj) + return repr(obj)[:40] + + +def _gradient(start_color, end_color, depth, max_depth): + if max_depth == 0: + # avoid division by zero + return start_color + h1, s1, v1 = start_color + h2, s2, v2 = end_color + f = float(depth) / max_depth + h = h1 * (1-f) + h2 * f + s = s1 * (1-f) + s2 * f + v = v1 * (1-f) + v2 * f + return h, s, v + + +def _edge_label(source, target, shortnames=True): + if (_isinstance(target, dict) + and target is getattr(source, '__dict__', None)): + return ' [label="__dict__",weight=10]' + if _isinstance(source, types.FrameType): + if target is source.f_locals: + return ' [label="f_locals",weight=10]' + if target is source.f_globals: + return ' [label="f_globals",weight=10]' + if _isinstance(source, types.MethodType): + try: + if target is source.__self__: + return ' [label="__self__",weight=10]' + if target is source.__func__: + return ' [label="__func__",weight=10]' + except AttributeError: # pragma: nocover + # Python < 2.6 compatibility + if target is source.im_self: + return ' [label="im_self",weight=10]' + if target is source.im_func: + return ' [label="im_func",weight=10]' + if _isinstance(source, types.FunctionType): + for k in dir(source): + if target is getattr(source, k): + return ' [label="%s",weight=10]' % _quote(k) + if _isinstance(source, dict): + for k, v in iteritems(source): + if v is target: + if _isinstance(k, basestring) and _is_identifier(k): + return ' [label="%s",weight=2]' % _quote(k) + else: + if shortnames: + tn = _short_typename(k) + else: + tn = _long_typename(k) + return ' [label="%s"]' % _quote(tn + "\n" + _safe_repr(k)) + return '' + + +_is_identifier = re.compile('[a-zA-Z_][a-zA-Z_0-9]*$').match + + +def _program_in_path(program): + # XXX: Consider using distutils.spawn.find_executable or shutil.which + path = os.environ.get("PATH", os.defpath).split(os.pathsep) + path = [os.path.join(dir, program) for dir in path] + path = [True for file in path + if os.path.isfile(file) or os.path.isfile(file + '.exe')] + return bool(path) diff --git a/src/py/bbctrl/Planner.py b/src/py/bbctrl/Planner.py new file mode 100644 index 0000000..05e928e --- /dev/null +++ b/src/py/bbctrl/Planner.py @@ -0,0 +1,388 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import json +import math +import re +import time +from collections import deque +import camotics.gplan as gplan # pylint: disable=no-name-in-module,import-error +import bbctrl.Cmd as Cmd +from bbctrl.CommandQueue import CommandQueue + + +reLogLine = re.compile( + r'^(?P[A-Z])[0-9 ]:' + r'((?P[^:]+):)?' + r'((?P\d+):)?' + r'((?P\d+):)?' + r'(?P.*)$') + + +def log_floats(o): + if isinstance(o, float): return round(o, 2) + if isinstance(o, dict): return {k: log_floats(v) for k, v in o.items()} + if isinstance(o, (list, tuple)): return [log_floats(x) for x in o] + return o + + +def log_json(o): return json.dumps(log_floats(o)) + + +class Planner(): + def __init__(self, ctrl): + self.ctrl = ctrl + self.log = ctrl.log.get('Planner') + self.cmdq = CommandQueue(ctrl) + self.planner = None + self._position_dirty = False + self.where = '' + + ctrl.state.add_listener(self._update) + + self.reset(False) + self._report_time() + + + def is_busy(self): return self.is_running() or self.cmdq.is_active() + def is_running(self): return self.planner.is_running() + def position_change(self): self._position_dirty = True + + + def _sync_position(self, force = False): + if not force and not self._position_dirty: return + self._position_dirty = False + self.planner.set_position(self.ctrl.state.get_position()) + + + def get_config(self, mdi, with_limits): + state = self.ctrl.state + config = self.ctrl.config + + cfg = { + # NOTE Must get current units not configured default units + 'default-units': 'METRIC' if state.get('metric') else 'IMPERIAL', + 'max-vel': state.get_axis_vector('vm', 1000), + 'max-accel': state.get_axis_vector('am', 1000000), + 'max-jerk': state.get_axis_vector('jm', 1000000), + 'rapid-auto-off': config.get('rapid-auto-off'), + 'max-blend-error': config.get('max-deviation'), + 'max-merge-error': config.get('max-deviation'), + 'junction-accel': config.get('junction-accel'), + } + + if with_limits: + minLimit = state.get_soft_limit_vector('tn', -math.inf) + maxLimit = state.get_soft_limit_vector('tm', math.inf) + + # If max <= min then no limit + for axis in 'xyzabc': + if maxLimit[axis] <= minLimit[axis]: + minLimit[axis], maxLimit[axis] = -math.inf, math.inf + + cfg['min-soft-limit'] = minLimit + cfg['max-soft-limit'] = maxLimit + + if not mdi: + program_start = config.get('program-start') + if program_start: cfg['program-start'] = program_start + + overrides = {} + + tool_change = config.get('tool-change') + if tool_change: overrides['M6'] = tool_change + + program_end = config.get('program-end') + if program_end: overrides['M2'] = program_end + + if overrides: cfg['overrides'] = overrides + + self.log.info('Config:' + log_json(cfg)) + + return cfg + + + def _update(self, update): + if 'id' in update: + id = update['id'] + self.planner.set_active(id) # Release planner commands + self.cmdq.release(id) # Synchronize planner variables + + + def _get_var_cb(self, name, units): + value = 0 + + if len(name) and name[0] == '_': + value = self.ctrl.state.get(name[1:], 0) + try: + float(value) + if units == 'IMPERIAL': value /= 25.4 # Assume metric + except ValueError: value = 0 + + self.log.info('Get: %s=%s (units=%s)' % (name, value, units)) + + return value + + + def _log_cb(self, line): + line = line.strip() + m = reLogLine.match(line) + if not m: return + + level = m.group('level') + msg = m.group('msg') + filename = m.group('file') + line = m.group('line') + column = m.group('column') + + where = ':'.join(filter(None.__ne__, [filename, line, column])) + + if line is not None: line = int(line) + if column is not None: column = int(column) + + if level == 'I': self.log.info (msg, where = where) + elif level == 'D': self.log.debug (msg, where = where) + elif level == 'W': self.log.warning (msg, where = where) + elif level == 'E': self.log.error (msg, where = where) + else: self.log.error('Could not parse planner log line: ' + line) + + + def _add_message(self, text): + self.ctrl.state.add_message(text) + + line = self.ctrl.state.get('line', 0) + if 0 <= line: where = '%s:%d' % (self.where, line) + else: where = self.where + + self.log.message(text, where = where) + + + def _enqueue_set_cmd(self, id, name, value): + self.log.info('set(#%d, %s, %s)', id, name, value) + self.cmdq.enqueue(id, self.ctrl.state.set, name, value) + + + def _report_time(self): + state = self.ctrl.state.get('xx', '') + + if state in ('STOPPING', 'RUNNING') and self.move_start: + delta = time.time() - self.move_start + if self.move_time < delta: delta = self.move_time + plan_time = self.current_plan_time + delta + + self.ctrl.state.set('plan_time', round(plan_time)) + + elif state != 'HOLDING': self.ctrl.state.set('plan_time', 0) + + self.ctrl.ioloop.call_later(1, self._report_time) + + + def _plan_time_restart(self): + self.plan_time = self.ctrl.state.get('plan_time', 0) + + + def _update_time(self, plan_time, move_time): + self.current_plan_time = plan_time + self.move_time = move_time + self.move_start = time.time() + + + def _enqueue_line_time(self, block): + if block.get('first', False) or block.get('seeking', False): return + + # Sum move times + move_time = sum(block['times']) / 1000 # To seconds + + self.cmdq.enqueue(block['id'], self._update_time, self.plan_time, + move_time) + + self.plan_time += move_time + + + def _enqueue_dwell_time(self, block): + self.cmdq.enqueue(block['id'], self._update_time, self.plan_time, + block['seconds']) + self.plan_time += block['seconds'] + + + def __encode(self, block): + type, id = block['type'], block['id'] + + if type != 'set': self.log.info('Cmd:' + log_json(block)) + + if type == 'line': + self._enqueue_line_time(block) + return Cmd.line(block['target'], block['exit-vel'], + block['max-accel'], block['max-jerk'], + block['times'], block.get('speeds', [])) + + if type == 'set': + name, value = block['name'], block['value'] + + if name == 'message': + self.cmdq.enqueue(id, self._add_message, value) + + if name in ['line', 'tool']: self._enqueue_set_cmd(id, name, value) + + if name == 'speed': + self._enqueue_set_cmd(id, name, value) + return Cmd.speed(value) + + if len(name) and name[0] == '_': + # Don't queue axis positions, can be triggered by new position + if len(name) != 2 or name[1] not in 'xyzabc': + self._enqueue_set_cmd(id, name[1:], value) + + if name == '_feed': # Must come after _enqueue_set_cmd() above + return Cmd.set_sync('if', 1 / value if value else 0) + + if name[0:1] == '_' and name[1:2] in 'xyzabc': + if name[2:] == '_home': return Cmd.set_axis(name[1], value) + + if name[2:] == '_homed': + motor = self.ctrl.state.find_motor(name[1]) + if motor is not None: + return Cmd.set_sync('%dh' % motor, value) + + return + + if type == 'input': + # TODO handle timeout + self.planner.synchronize(0) # TODO Fix this + return Cmd.input(block['port'], block['mode'], block['timeout']) + + if type == 'output': + return Cmd.output(block['port'], int(float(block['value']))) + + if type == 'dwell': + self._enqueue_dwell_time(block) + return Cmd.dwell(block['seconds']) + + if type == 'pause': return Cmd.pause(block['pause-type']) + + if type == 'seek': + sw = self.ctrl.state.get_switch_id(block['switch']) + return Cmd.seek(sw, block['active'], block['error']) + + if type == 'end': return '' # Sends id + + raise Exception('Unknown planner command "%s"' % type) + + + def _encode(self, block): + cmd = self.__encode(block) + + if cmd is not None: + self.cmdq.enqueue(block['id'], None) + return Cmd.set_sync('id', block['id']) + '\n' + cmd + + + def reset_times(self): + self.move_start = 0 + self.move_time = 0 + self.plan_time = 0 + self.current_plan_time = 0 + + + def close(self): + # Release planner callbacks + if self.planner is not None: + self.planner.set_resolver(None) + self.planner.set_logger(None) + + + def reset(self, stop = True): + if stop: self.ctrl.mach.stop() + self.planner = gplan.Planner() + self.planner.set_resolver(self._get_var_cb) + # TODO logger is global and will not work correctly in demo mode + self.planner.set_logger(self._log_cb, 1, 'LinePlanner:3') + self._position_dirty = True + self.cmdq.clear() + self.reset_times() + self.ctrl.state.reset() + + + def mdi(self, cmd, with_limits = True): + self.where = '' + self.log.info('MDI:' + cmd) + self._sync_position() + self.planner.load_string(cmd, self.get_config(True, with_limits)) + self.reset_times() + + + def load(self, path): + self.where = path + path = self.ctrl.get_path('upload', path) + self.log.info('GCode:' + path) + self._sync_position() + self.planner.load(path, self.get_config(False, True)) + self.reset_times() + + + def stop(self): + try: + self.planner.stop() + self.cmdq.clear() + + except: + self.log.exception() + self.reset() + + + def restart(self): + try: + id = self.ctrl.state.get('id') + position = self.ctrl.state.get_position() + + self.log.info('Planner restart: %d %s' % (id, log_json(position))) + + self.cmdq.clear() + self.cmdq.release(id) + self._plan_time_restart() + self.planner.restart(id, position) + + except: + self.log.exception() + self.stop() + + + def next(self): + try: + while self.planner.has_more(): + cmd = self.planner.next() + cmd = self._encode(cmd) + if cmd is not None: return cmd + + except RuntimeError as e: + # Pass on the planner message + self.log.error(str(e)); + self.stop() + + except: + self.log.exception() + self.stop() diff --git a/src/py/bbctrl/Preplanner.py b/src/py/bbctrl/Preplanner.py new file mode 100644 index 0000000..d961d78 --- /dev/null +++ b/src/py/bbctrl/Preplanner.py @@ -0,0 +1,271 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import time +import json +import hashlib +import glob +import tempfile +import signal +from concurrent.futures import Future +from tornado import gen, process, iostream +import bbctrl + + +def hash_dump(o): + s = json.dumps(o, separators = (',', ':'), sort_keys = True) + return s.encode('utf8') + + +def plan_hash(path, config): + h = hashlib.sha256() + h.update('v4'.encode('utf8')) + h.update(hash_dump(config)) + + with open(path, 'rb') as f: + while True: + buf = f.read(1024 * 1024) + if not buf: break + h.update(buf) + + return h.hexdigest() + + +def safe_remove(path): + try: + os.unlink(path) + except: pass + + +class Plan(object): + def __init__(self, preplanner, ctrl, filename): + self.preplanner = preplanner + + # Copy planner state + self.state = ctrl.state.snapshot() + self.config = ctrl.mach.planner.get_config(False, False) + del self.config['default-units'] + + self.progress = 0 + self.cancel = False + self.pid = None + + root = ctrl.get_path() + self.gcode = '%s/upload/%s' % (root, filename) + self.base = '%s/plans/%s' % (root, filename) + self.hid = plan_hash(self.gcode, self.config) + fbase = '%s.%s.' % (self.base, self.hid) + self.files = [ + fbase + 'json', + fbase + 'positions.gz', + fbase + 'speeds.gz'] + + self.future = Future() + ctrl.ioloop.add_callback(self._load) + + + def terminate(self): + if self.cancel: return + self.cancel = True + if self.pid is not None: + try: + os.kill(self.pid, signal.SIGKILL) + except: pass + + + def delete(self): + files = glob.glob(self.base + '.*') + for path in files: safe_remove(path) + + + def clean(self, max = 2): + plans = glob.glob(self.base + '.*.json') + if len(plans) <= max: return + + # Delete oldest plans + plans = [(os.path.getmtime(path), path) for path in plans] + plans.sort() + + for mtime, path in plans[:len(plans) - max]: + safe_remove(path) + safe_remove(path[:-4] + 'positions.gz') + safe_remove(path[:-4] + 'speeds.gz') + + + def _exists(self): + for path in self.files: + if not os.path.exists(path): return False + return True + + + def _read(self): + if self.cancel: return + + try: + with open(self.files[0], 'r') as f: meta = json.load(f) + with open(self.files[1], 'rb') as f: positions = f.read() + with open(self.files[2], 'rb') as f: speeds = f.read() + + return meta, positions, speeds + + except: + self.preplanner.log.exception() + + # Clean + for path in self.files: + if os.path.exists(path): + os.remove(path) + + + @gen.coroutine + def _exec(self): + self.clean() # Clean up old plans + + with tempfile.TemporaryDirectory() as tmpdir: + cmd = ( + '/usr/bin/env', 'python3', + bbctrl.get_resource('plan.py'), + os.path.abspath(self.gcode), json.dumps(self.state), + json.dumps(self.config), + '--max-time=%s' % self.preplanner.max_plan_time, + '--max-loop=%s' % self.preplanner.max_loop_time + ) + + self.preplanner.log.info('Running: %s', cmd) + + proc = process.Subprocess(cmd, stdout = process.Subprocess.STREAM, + stderr = process.Subprocess.STREAM, + cwd = tmpdir) + errs = '' + self.pid = proc.proc.pid + + try: + try: + while True: + line = yield proc.stdout.read_until(b'\n') + self.progress = float(line.strip()) + if self.cancel: return + except iostream.StreamClosedError: pass + + self.progress = 1 + + ret = yield proc.wait_for_exit(False) + if ret: + errs = yield proc.stderr.read_until_close() + raise Exception('Plan failed: ' + errs.decode('utf8')) + + finally: + proc.stderr.close() + proc.stdout.close() + + if not self.cancel: + os.rename(tmpdir + '/meta.json', self.files[0]) + os.rename(tmpdir + '/positions.gz', self.files[1]) + os.rename(tmpdir + '/speeds.gz', self.files[2]) + os.sync() + + + @gen.coroutine + def _load(self): + try: + if self._exists(): + data = self._read() + if data is not None: + self.future.set_result(data) + return + + if not self._exists(): yield self._exec() + self.future.set_result(self._read()) + + except: + self.preplanner.log.exception() + + + +class Preplanner(object): + def __init__(self, ctrl, max_plan_time = 60 * 60 * 24, max_loop_time = 300): + self.ctrl = ctrl + self.log = ctrl.log.get('Preplanner') + + self.max_plan_time = max_plan_time + self.max_loop_time = max_loop_time + + path = self.ctrl.get_plan() + if not os.path.exists(path): os.mkdir(path) + + self.started = Future() + self.plans = {} + + + def start(self): + if not self.started.done(): + self.log.info('Preplanner started') + self.started.set_result(True) + + + def invalidate(self, filename): + if filename in self.plans: + self.plans[filename].terminate() + del self.plans[filename] + + + def invalidate_all(self): + for filename, plan in self.plans.items(): + plan.terminate() + self.plans = {} + + + def delete_all_plans(self): + files = glob.glob(self.ctrl.get_plan('*')) + for path in files: safe_remove(path) + self.invalidate_all() + + + def delete_plans(self, filename): + if filename in self.plans: + self.plans[filename].delete() + self.invalidate(filename) + + @gen.coroutine + def get_plan(self, filename): + if filename is None: raise Exception('Filename cannot be None') + + # Wait until state is fully initialized + yield self.started + + if filename in self.plans: plan = self.plans[filename] + else: + plan = Plan(self, self.ctrl, filename) + self.plans[filename] = plan + + data = yield plan.future + return data + + + def get_plan_progress(self, filename): + return self.plans[filename].progress if filename in self.plans else 0 diff --git a/src/py/bbctrl/Pwr.py b/src/py/bbctrl/Pwr.py new file mode 100644 index 0000000..9c4579a --- /dev/null +++ b/src/py/bbctrl/Pwr.py @@ -0,0 +1,194 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import bbctrl +import bbctrl.Cmd as Cmd + + +# Must match regs in pwr firmware +TEMP_REG = 0 +VIN_REG = 1 +VOUT_REG = 2 +MOTOR_REG = 3 +LOAD1_REG = 4 +LOAD2_REG = 5 +VDD_REG = 6 +FLAGS_REG = 7 +VERSION_REG = 8 + +# Must be kept in sync with pwr firmware +UNDER_VOLTAGE_FLAG = 1 << 0 +OVER_VOLTAGE_FLAG = 1 << 1 +OVER_CURRENT_FLAG = 1 << 2 +SENSE_ERROR_FLAG = 1 << 3 +SHUNT_OVERLOAD_FLAG = 1 << 4 +MOTOR_OVERLOAD_FLAG = 1 << 5 +LOAD1_SHUTDOWN_FLAG = 1 << 6 +LOAD2_SHUTDOWN_FLAG = 1 << 7 +MOTOR_UNDER_VOLTAGE_FLAG = 1 << 8 +MOTOR_VOLTAGE_SENSE_ERROR_FLAG = 1 << 9 +MOTOR_CURRENT_SENSE_ERROR_FLAG = 1 << 10 +LOAD1_SENSE_ERROR_FLAG = 1 << 11 +LOAD2_SENSE_ERROR_FLAG = 1 << 12 +VDD_CURRENT_SENSE_ERROR_FLAG = 1 << 13 +POWER_SHUTDOWN_FLAG = 1 << 14 +SHUNT_ERROR_FLAG = 1 << 15 + +reg_names = 'temp vin vout motor load1 load2 vdd pwr_flags pwr_version'.split() + + +class Pwr(): + def __init__(self, ctrl): + self.ctrl = ctrl + self.log = ctrl.log.get('Pwr') + + self.i2c_addr = ctrl.args.pwr_addr + self.regs = [-1] * 9 + self.lcd_page = ctrl.lcd.add_new_page() + self.failures = 0 + + self._update_cb(False) + + + def check_fault(self, var, status): + status = bool(status) + + if not self.ctrl.state.has(var) or status != self.ctrl.state.get(var): + self.ctrl.state.set(var, status) + if status: return True + + return False + + + def check_faults(self): + flags = self.regs[FLAGS_REG] + + if self.check_fault('under_voltage', flags & UNDER_VOLTAGE_FLAG): + self.log.warning('Device under voltage') + + if self.check_fault('over_voltage', flags & OVER_VOLTAGE_FLAG): + self.log.warning('Device over voltage') + + if self.check_fault('over_current', flags & OVER_CURRENT_FLAG): + self.log.warning('Device total current limit exceeded') + + if self.check_fault('sense_error', flags & SENSE_ERROR_FLAG): + self.log.warning('Power sense error') + + if self.check_fault('shunt_overload', flags & SHUNT_OVERLOAD_FLAG): + self.log.warning('Power shunt overload') + + if self.check_fault('motor_overload', flags & MOTOR_OVERLOAD_FLAG): + self.log.warning('Motor power overload') + + if self.check_fault('load1_shutdown', flags & LOAD1_SHUTDOWN_FLAG): + self.log.warning('Load 1 over temperature shutdown') + + if self.check_fault('load2_shutdown', flags & LOAD2_SHUTDOWN_FLAG): + self.log.warning('Load 2 over temperature shutdown') + + if self.check_fault('motor_under_voltage', + flags & MOTOR_UNDER_VOLTAGE_FLAG): + self.log.warning('Motor under voltage') + + if self.check_fault('motor_voltage_sense_error', + flags & MOTOR_VOLTAGE_SENSE_ERROR_FLAG): + self.log.warning('Motor voltage sense error') + + if self.check_fault('motor_current_sense_error', + flags & MOTOR_CURRENT_SENSE_ERROR_FLAG): + self.log.warning('Motor current sense error') + + if self.check_fault('load1_sense_error', + flags & LOAD1_SENSE_ERROR_FLAG): + self.log.warning('Load1 sense error') + + if self.check_fault('load2_sense_error', + flags & LOAD2_SENSE_ERROR_FLAG): + self.log.warning('Load2 sense error') + + if self.check_fault('vdd_current_sense_error', + flags & VDD_CURRENT_SENSE_ERROR_FLAG): + self.log.warning('Vdd current sense error') + + if self.check_fault('power_shutdown', flags & POWER_SHUTDOWN_FLAG): + self.log.warning('Power shutdown') + self.ctrl.mach.i2c_command(Cmd.SHUTDOWN) + + if self.check_fault('shunt_error', flags & SHUNT_ERROR_FLAG): + self.log.warning('Shunt error') + + + def _update_cb(self, now = True): + if now: self._update() + self.ctrl.ioloop.call_later(1, self._update_cb) + + + def _update(self): + update = {} + + try: + for i in range(len(self.regs)): + value = self.ctrl.i2c.read_word(self.i2c_addr + i) + if value is None: return # Handle lack of i2c port + + if i == TEMP_REG: value -= 273 + elif i == FLAGS_REG or i == VERSION_REG: pass + else: value /= 100.0 + + key = reg_names[i] + self.ctrl.state.set(key, value) + + if self.regs[i] != value: + update[key] = value + self.regs[i] = value + + if i == FLAGS_REG: self.check_faults() + + except Exception as e: + if i < 6: # Older pwr firmware does not have regs > 5 + self.failures += 1 + msg = 'Pwr communication failed at reg %d: %s' % (i, e) + if self.failures != 5: self.log.info(msg) + else: + self.log.warning(msg) + self.failures = 0 + return + + self.lcd_page.text('%3dC Tmp' % self.regs[TEMP_REG], 0, 0) + self.lcd_page.text('%5.1fV In' % self.regs[VIN_REG], 0, 1) + self.lcd_page.text('%5.1fV Out' % self.regs[VOUT_REG], 0, 2) + self.lcd_page.text(' %04x Flg' % self.regs[FLAGS_REG], 0, 3) + + self.lcd_page.text('%5.1fA Mot' % self.regs[MOTOR_REG], 10, 0) + self.lcd_page.text('%5.1fA Ld1' % self.regs[LOAD1_REG], 10, 1) + self.lcd_page.text('%5.1fA Ld2' % self.regs[LOAD2_REG], 10, 2) + self.lcd_page.text('%5.1fA Vdd' % self.regs[VDD_REG], 10, 3) + + if len(update): self.ctrl.state.update(update) + + self.failures = 0 diff --git a/src/py/bbctrl/RequestHandler.py b/src/py/bbctrl/RequestHandler.py new file mode 100644 index 0000000..771b4fd --- /dev/null +++ b/src/py/bbctrl/RequestHandler.py @@ -0,0 +1,63 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import traceback +import bbctrl + +from tornado.web import HTTPError +import tornado.web + + +class RequestHandler(tornado.web.RequestHandler): + def __init__(self, app, request, **kwargs): + super().__init__(app, request, **kwargs) + self.app = app + + + def get_ctrl(self): return self.app.get_ctrl(self.get_cookie('client-id')) + def get_log(self, name = 'API'): return self.get_ctrl().log.get(name) + + + def get_path(self, path = None, filename = None): + return self.get_ctrl().get_path(path, filename) + + + def get_upload(self, filename = None): + return self.get_ctrl().get_upload(filename) + + + # Override exception logging + def log_exception(self, typ, value, tb): + if (isinstance(value, HTTPError) and + 400 <= value.status_code and value.status_code < 500): return + + log = self.get_log() + log.set_level(bbctrl.log.DEBUG) + + log.error(str(value)) + trace = ''.join(traceback.format_exception(typ, value, tb)) + log.debug(trace) diff --git a/src/py/bbctrl/State.py b/src/py/bbctrl/State.py new file mode 100644 index 0000000..dfd46b9 --- /dev/null +++ b/src/py/bbctrl/State.py @@ -0,0 +1,450 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import traceback +import copy +import uuid +import os +import bbctrl + + +class State(object): + def __init__(self, ctrl): + self.ctrl = ctrl + self.log = ctrl.log.get('State') + + self.callbacks = {} + self.changes = {} + self.listeners = [] + self.timeout = None + self.machine_var_set = set() + self.message_id = 0 + + # Defaults + self.vars = { + 'line': -1, + 'messages': [], + 'tool': 0, + 'feed': 0, + 'speed': 0, + 'sid': str(uuid.uuid4()), + 'demo': ctrl.args.demo, + } + + # Add computed variable callbacks for each motor. + # + # NOTE, variable callbacks must return metric values only because + # the planner will scale returned values when in imperial mode. + for i in range(4): + self.set_callback(str(i) + 'home_position', + lambda name, i = i: self.motor_home_position(i)) + self.set_callback(str(i) + 'home_travel', + lambda name, i = i: self.motor_home_travel(i)) + self.set_callback(str(i) + 'latch_backoff', + lambda name, i = i: self.motor_latch_backoff(i)) + self.set_callback(str(i) + 'zero_backoff', + lambda name, i = i: self.motor_zero_backoff(i)) + self.set_callback(str(i) + 'search_velocity', + lambda name, i = i: self.motor_search_velocity(i)) + self.set_callback(str(i) + 'latch_velocity', + lambda name, i = i: self.motor_latch_velocity(i)) + + self.set_callback('metric', lambda name: 1 if self.is_metric() else 0) + self.set_callback('imperial', lambda name: 0 if self.is_metric() else 1) + + self.reset() + self.load_files() + + + def is_metric(self): return self.get('units', 'METRIC') == 'METRIC' + + + def reset(self): + # Unhome all motors + for i in range(4): self.set('%dhomed' % i, False) + + # Zero offsets and positions + for axis in 'xyzabc': + self.set(axis + 'p', 0) + self.set('offset_' + axis, 0) + + + def load_files(self): + self.files = [] + + upload = self.ctrl.get_upload() + + if not os.path.exists(upload): + os.mkdir(upload) + from shutil import copy + copy(bbctrl.get_resource('http/buildbotics.nc'), upload) + + for path in os.listdir(upload): + if os.path.isfile(upload + '/' + path): + self.files.append(path) + + self.files.sort() + self.set('files', self.files) + + if len(self.files): self.select_file(self.files[0]) + else: self.select_file('') + + + def clear_files(self): + self.select_file('') + self.files = [] + self.changes['files'] = self.files + + + def add_file(self, filename): + if not filename in self.files: + self.files.append(filename) + self.files.sort() + self.changes['files'] = self.files + + self.select_file(filename) + + + def remove_file(self, filename): + if filename in self.files: + self.files.remove(filename) + self.changes['files'] = self.files + + if self.get('selected', filename) == filename: + if len(self.files): self.select_file(self.files[0]) + else: self.select_file('') + + + def select_file(self, filename): + self.set('selected', filename) + time = os.path.getmtime(self.ctrl.get_upload(filename)) + self.set('selected_time', time) + + + def set_bounds(self, bounds): + for axis in 'xyzabc': + for name in ('min', 'max'): + var = '%s_%s' % (axis, name) + value = bounds[name][axis] if axis in bounds[name] else 0 + self.set(var, value) + + + def ack_message(self, id): + self.log.info('Message %d acknowledged' % id) + msgs = self.vars['messages'] + msgs = list(filter(lambda m: id < m['id'], msgs)) + self.set('messages', msgs) + + + def add_message(self, text): + msg = dict(text = text, id = self.message_id) + self.message_id += 1 + msgs = self.vars['messages'] + msgs = msgs + [msg] # It's important we make a new list here + self.set('messages', msgs) + + + def _notify(self): + if not self.changes: return + + for listener in self.listeners: + try: + listener(self.changes) + + except Exception as e: + self.log.warning('Updating state listener: %s', + traceback.format_exc()) + + self.changes = {} + self.timeout = None + + + def resolve(self, name): + # Resolve axis prefixes to motor numbers + if 2 < len(name) and name[1] == '_' and name[0] in 'xyzabc': + motor = self.find_motor(name[0]) + if motor is not None: return str(motor) + name[2:] + + return name + + + def has(self, name): return self.resolve(name) in self.vars + def set_callback(self, name, cb): self.callbacks[self.resolve(name)] = cb + + + def set(self, name, value): + name = self.resolve(name) + + if not name in self.vars or self.vars[name] != value: + self.vars[name] = value + self.changes[name] = value + + # Trigger listener notify + if self.timeout is None: + self.timeout = self.ctrl.ioloop.call_later(0.25, self._notify) + + + def update(self, update): + for name, value in update.items(): + self.set(name, value) + + + def get(self, name, default = None): + name = self.resolve(name) + + if name in self.vars: return self.vars[name] + if name in self.callbacks: return self.callbacks[name](name) + if default is None: + self.log.warning('State variable "%s" not found' % name) + + return default + + + def snapshot(self): + vars = copy.deepcopy(self.vars) + + for name in self.callbacks: + if not name in vars: + vars[name] = self.callbacks[name](name) + + axis_motors = {axis: self.find_motor(axis) for axis in 'xyzabc'} + axis_vars = {} + + for name, value in vars.items(): + if name[0] in '0123': + motor = int(name[0]) + + for axis in 'xyzabc': + if motor == axis_motors[axis]: + axis_vars[axis + '_' + name[1:]] = value + + vars.update(axis_vars) + + return vars + + + def config(self, code, value): + # Set machine variables via mach, others directly + if code in self.machine_var_set: self.ctrl.mach.set(code, value) + else: self.set(code, value) + + + def add_listener(self, listener): + self.listeners.append(listener) + listener(self.vars) + + + def remove_listener(self, listener): self.listeners.remove(listener) + + + def set_machine_vars(self, vars): + # Record all machine vars, indexed or otherwise + self.machine_var_set = set() + for code, spec in vars.items(): + if 'index' in spec: + for index in spec['index']: + self.machine_var_set.add(index + code) + else: self.machine_var_set.add(code) + + + def get_position(self): + position = {} + + for axis in 'xyzabc': + if self.is_axis_enabled(axis) and self.has(axis + 'p'): + position[axis] = self.get(axis + 'p') + + return position + + + def get_axis_vector(self, name, scale = 1): + v = {} + + for axis in 'xyzabc': + motor = self.find_motor(axis) + + if motor is not None and self.motor_enabled(motor): + value = self.get(str(motor) + name, None) + if value is not None: v[axis] = value * scale + + return v + + + def get_soft_limit_vector(self, var, default): + limit = self.get_axis_vector(var, 1) + + for axis in 'xyzabc': + if not axis in limit or not self.is_axis_homed(axis): + limit[axis] = default + + return limit + + + def find_motor(self, axis): + for motor in range(4): + if not ('%dan' % motor) in self.vars: continue + motor_axis = 'xyzabc'[self.vars['%dan' % motor]] + if motor_axis == axis.lower() and self.vars.get('%dme' % motor, 0): + return motor + + + def is_axis_homed(self, axis): return self.get('%s_homed' % axis, False) + + + def is_axis_enabled(self, axis): + motor = self.find_motor(axis) + return motor is not None and self.motor_enabled(motor) + + + def get_enabled_axes(self): + axes = [] + + for axis in 'xyzabc': + if self.is_axis_enabled(axis): + axes.append(axis) + + return axes + + + def is_motor_faulted(self, motor): + return self.get('%ddf' % motor, 0) & 0x1f + + + def is_axis_faulted(self, axis): + motor = self.find_motor(axis) + return motor is not None and self.is_motor_faulted(motor) + + + def axis_homing_mode(self, axis): + motor = self.find_motor(axis) + if motor is None: return 'disabled' + return self.motor_homing_mode(motor) + + + def axis_home_fail_reason(self, axis): + motor = self.find_motor(axis) + if motor is None: return 'Not mapped to motor' + if not self.motor_enabled(motor): return 'Motor disabled' + + mode = self.motor_homing_mode(motor) + + if mode != 'manual': + if mode == 'switch-min' and not int(self.get(axis + '_ls', 0)): + return 'Configured for min switch but switch is disabled' + + if mode == 'switch-max' and not int(self.get(axis + '_xs', 0)): + return 'Configured for max switch but switch is disabled' + + softMin = int(self.get(axis + '_tn', 0)) + softMax = int(self.get(axis + '_tm', 0)) + if softMax <= softMin + 1: + return 'max-soft-limit must be at least 1mm greater ' \ + 'than min-soft-limit' + + + def motor_enabled(self, motor): + return bool(int(self.vars.get('%dme' % motor, 0))) + + + def motor_homing_mode(self, motor): + mode = str(self.vars.get('%dho' % motor, 0)) + if mode == '0': return 'manual' + if mode == '1': return 'switch-min' + if mode == '2': return 'switch-max' + if mode == '3': return 'stall-min' + if mode == '4': return 'stall-max' + raise Exception('Unrecognized homing mode "%s"' % mode) + + + def motor_home_direction(self, motor): + mode = self.motor_homing_mode(motor) + if mode.endswith('-min'): return -1 + if mode.endswith('-max'): return 1 + return 0 # Disabled + + + def motor_home_position(self, motor): + mode = self.motor_homing_mode(motor) + # Return soft limit positions + if mode.endswith('-min'): return self.vars['%dtn' % motor] + if mode.endswith('-max'): return self.vars['%dtm' % motor] + return 0 # Disabled + + + def motor_home_travel(self, motor): + tmin = self.get(str(motor) + 'tm', 0) + tmax = self.get(str(motor) + 'tn', 0) + hdir = self.motor_home_direction(motor) + + # (travel_max - travel_min) * 1.5 * home_dir + return (tmin - tmax) * 1.5 * hdir + + + def motor_latch_backoff(self, motor): + lb = self.get(str(motor) + 'lb', 0) + hdir = self.motor_home_direction(motor) + return -(lb * hdir) # -latch_backoff * home_dir + + + def motor_zero_backoff(self, motor): + zb = self.get(str(motor) + 'zb', 0) + hdir = self.motor_home_direction(motor) + return -(zb * hdir) # -zero_backoff * home_dir + + + def motor_search_velocity(self, motor): + return 1000 * self.get(str(motor) + 'sv', 0) + + + def motor_latch_velocity(self, motor): + return 1000 * self.get(str(motor) + 'lv', 0) + + + def get_axis_switch(self, axis, side): + axis = axis.lower() + + if not axis in 'xyzabc': + raise Exception('Unsupported switch "%s-%s"' % (axis, side)) + + if not self.is_axis_enabled(axis): + raise Exception('Switch "%s-%s" axis not enabled' % (axis, side)) + + motor = self.find_motor(axis) + + # This must match the switch ID enum in avr/src/switch.h + hmode = self.motor_homing_mode(motor) + if hmode.startswith('stall-'): return motor + 10 + return 2 * motor + 2 + (0 if side.lower() == 'min' else 1) + + + def get_switch_id(self, switch): + # TODO Support other input switches in CAMotics gcode/machine/PortType.h + switch = switch.lower() + if switch == 'probe': return 1 + if switch[1:] == '-min': return self.get_axis_switch(switch[0], 'min') + if switch[1:] == '-max': return self.get_axis_switch(switch[0], 'max') + raise Exception('Unsupported switch "%s"' % switch) diff --git a/src/py/bbctrl/Web.py b/src/py/bbctrl/Web.py new file mode 100644 index 0000000..5b1a46c --- /dev/null +++ b/src/py/bbctrl/Web.py @@ -0,0 +1,595 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import sys +import json +import tornado +import sockjs.tornado +import datetime +import shutil +import tarfile +import subprocess +import socket +import time +from tornado.web import HTTPError +from tornado import web, gen + +import bbctrl + + + +def call_get_output(cmd): + p = subprocess.Popen(cmd, stdout = subprocess.PIPE) + s = p.communicate()[0].decode('utf-8') + if p.returncode: raise HTTPError(400, 'Command failed') + return s + + +def get_username(): + return call_get_output(['getent', 'passwd', '1001']).split(':')[0] + + +def set_username(username): + if subprocess.call(['usermod', '-l', username, get_username()]): + raise HTTPError(400, 'Failed to set username to "%s"' % username) + + +def check_password(password): + # Get current password + s = call_get_output(['getent', 'shadow', get_username()]) + current = s.split(':')[1].split('$') + + # Check password type + if len(current) < 2 or current[1] != '1': + raise HTTPError(401, "Password invalid") + + # Check current password + cmd = ['openssl', 'passwd', '-salt', current[2], '-1', password] + s = call_get_output(cmd).strip() + + if s.split('$') != current: raise HTTPError(401, 'Wrong password') + + + +class RebootHandler(bbctrl.APIHandler): + def put_ok(self): + self.get_ctrl().lcd.goodbye('Rebooting...') + subprocess.Popen('reboot') + + +class LogHandler(bbctrl.RequestHandler): + def get(self): + with open(self.get_ctrl().log.get_path(), 'r') as f: + self.write(f.read()) + + + def set_default_headers(self): + fmt = socket.gethostname() + '-%Y%m%d.log' + filename = datetime.date.today().strftime(fmt) + self.set_header('Content-Disposition', 'filename="%s"' % filename) + self.set_header('Content-Type', 'text/plain') + + +class MessageAckHandler(bbctrl.APIHandler): + def put_ok(self, id): + self.get_ctrl().state.ack_message(int(id)) + + +class BugReportHandler(bbctrl.RequestHandler): + def get(self): + import tarfile, io + + buf = io.BytesIO() + tar = tarfile.open(mode = 'w:bz2', fileobj = buf) + + def check_add(path, arcname = None): + if os.path.isfile(path): + if arcname is None: arcname = path + tar.add(path, self.basename + '/' + arcname) + + def check_add_basename(path): + check_add(path, os.path.basename(path)) + + ctrl = self.get_ctrl() + path = ctrl.log.get_path() + check_add_basename(path) + for i in range(1, 8): + check_add_basename('%s.%d' % (path, i)) + check_add_basename('/var/log/syslog') + check_add('config.json') + check_add(ctrl.get_upload(ctrl.state.get('selected', ''))) + + tar.close() + + self.write(buf.getvalue()) + + + def set_default_headers(self): + fmt = socket.gethostname() + '-%Y%m%d-%H%M%S' + self.basename = datetime.datetime.now().strftime(fmt) + filename = self.basename + '.tar.bz2' + self.set_header('Content-Disposition', 'filename="%s"' % filename) + self.set_header('Content-Type', 'application/x-bzip2') + + +class HostnameHandler(bbctrl.APIHandler): + def get(self): self.write_json(socket.gethostname()) + + def put(self): + if self.get_ctrl().args.demo: + raise HTTPError(400, 'Cannot set hostname in demo mode') + + if 'hostname' in self.json: + if subprocess.call(['/usr/local/bin/sethostname', + self.json['hostname'].strip()]) == 0: + self.write_json('ok') + return + + raise HTTPError(400, 'Failed to set hostname') + + +class WifiHandler(bbctrl.APIHandler): + def get(self): + data = {'ssid': '', 'channel': 0} + try: + data = json.loads(call_get_output(['config-wifi', '-j'])) + except: pass + self.write_json(data) + + + def put(self): + if self.get_ctrl().args.demo: + raise HTTPError(400, 'Cannot configure WiFi in demo mode') + + if 'mode' in self.json: + cmd = ['config-wifi', '-r'] + mode = self.json['mode'] + + if mode == 'disabled': cmd += ['-d'] + elif 'ssid' in self.json: + cmd += ['-s', self.json['ssid']] + + if mode == 'ap': + cmd += ['-a'] + if 'channel' in self.json: + cmd += ['-c', self.json['channel']] + + if 'pass' in self.json: + cmd += ['-p', self.json['pass']] + + if subprocess.call(cmd) == 0: + self.write_json('ok') + return + + raise HTTPError(400, 'Failed to configure wifi') + + +class UsernameHandler(bbctrl.APIHandler): + def get(self): self.write_json(get_username()) + + + def put_ok(self): + if self.get_ctrl().args.demo: + raise HTTPError(400, 'Cannot set username in demo mode') + + if 'username' in self.json: set_username(self.json['username']) + else: raise HTTPError(400, 'Missing "username"') + + +class PasswordHandler(bbctrl.APIHandler): + def put(self): + if self.get_ctrl().args.demo: + raise HTTPError(400, 'Cannot set password in demo mode') + + if 'current' in self.json and 'password' in self.json: + check_password(self.json['current']) + + # Set password + s = '%s:%s' % (get_username(), self.json['password']) + s = s.encode('utf-8') + + p = subprocess.Popen(['chpasswd', '-c', 'MD5'], + stdin = subprocess.PIPE) + p.communicate(input = s) + + if p.returncode == 0: + self.write_json('ok') + return + + raise HTTPError(401, 'Failed to set password') + + +class ConfigLoadHandler(bbctrl.APIHandler): + def get(self): self.write_json(self.get_ctrl().config.load()) + + +class ConfigDownloadHandler(bbctrl.APIHandler): + def set_default_headers(self): + fmt = socket.gethostname() + '-%Y%m%d.json' + filename = datetime.date.today().strftime(fmt) + self.set_header('Content-Type', 'application/octet-stream') + self.set_header('Content-Disposition', + 'attachment; filename="%s"' % filename) + + def get(self): + self.write_json(self.get_ctrl().config.load(), pretty = True) + + +class ConfigSaveHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().config.save(self.json) + + +class ConfigResetHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().config.reset() + + +class FirmwareUpdateHandler(bbctrl.APIHandler): + def prepare(self): pass + + + def put_ok(self): + if not 'password' in self.request.arguments: + raise HTTPError(401, 'Missing "password"') + + if not 'firmware' in self.request.files: + raise HTTPError(401, 'Missing "firmware"') + + check_password(self.request.arguments['password'][0]) + + firmware = self.request.files['firmware'][0] + + if not os.path.exists('firmware'): os.mkdir('firmware') + + with open('firmware/update.tar.bz2', 'wb') as f: + f.write(firmware['body']) + + self.get_ctrl().lcd.goodbye('Upgrading firmware') + subprocess.Popen(['/usr/local/bin/update-bbctrl']) + + +class UpgradeHandler(bbctrl.APIHandler): + def put_ok(self): + check_password(self.json['password']) + self.get_ctrl().lcd.goodbye('Upgrading firmware') + subprocess.Popen(['/usr/local/bin/upgrade-bbctrl']) + + +class PathHandler(bbctrl.APIHandler): + @gen.coroutine + def get(self, filename, dataType, *args): + if not os.path.exists(self.get_upload(filename)): + raise HTTPError(404, 'File not found') + + preplanner = self.get_ctrl().preplanner + future = preplanner.get_plan(filename) + + try: + delta = datetime.timedelta(seconds = 1) + data = yield gen.with_timeout(delta, future) + + except gen.TimeoutError: + progress = preplanner.get_plan_progress(filename) + self.write_json(dict(progress = progress)) + return + + try: + if data is None: return + meta, positions, speeds = data + + if dataType == '/positions': data = positions + elif dataType == '/speeds': data = speeds + else: + self.get_ctrl().state.set_bounds(meta['bounds']) + self.write_json(meta) + return + + filename = filename + '-' + dataType[1:] + '.gz' + self.set_header('Content-Disposition', 'filename="%s"' % filename) + self.set_header('Content-Type', 'application/octet-stream') + self.set_header('Content-Encoding', 'gzip') + self.set_header('Content-Length', str(len(data))) + + # Respond with chunks to avoid long delays + SIZE = 102400 + chunks = [data[i:i + SIZE] for i in range(0, len(data), SIZE)] + for chunk in chunks: + self.write(chunk) + yield self.flush() + + except tornado.iostream.StreamClosedError as e: pass + + +class HomeHandler(bbctrl.APIHandler): + def put_ok(self, axis, action, *args): + if axis is not None: axis = ord(axis[1:2].lower()) + + if action == '/set': + if not 'position' in self.json: + raise HTTPError(400, 'Missing "position"') + + self.get_ctrl().mach.home(axis, self.json['position']) + + elif action == '/clear': self.get_ctrl().mach.unhome(axis) + else: self.get_ctrl().mach.home(axis) + + +class StartHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().mach.start() + + +class EStopHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().mach.estop() + + +class ClearHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().mach.clear() + + +class StopHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().mach.stop() + + +class PauseHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().mach.pause() + + +class UnpauseHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().mach.unpause() + + +class OptionalPauseHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().mach.optional_pause() + + +class StepHandler(bbctrl.APIHandler): + def put_ok(self): self.get_ctrl().mach.step() + + +class PositionHandler(bbctrl.APIHandler): + def put_ok(self, axis): + self.get_ctrl().mach.set_position(axis, float(self.json['position'])) + + +class OverrideFeedHandler(bbctrl.APIHandler): + def put_ok(self, value): self.get_ctrl().mach.override_feed(float(value)) + + +class OverrideSpeedHandler(bbctrl.APIHandler): + def put_ok(self, value): self.get_ctrl().mach.override_speed(float(value)) + + +class ModbusReadHandler(bbctrl.APIHandler): + def put_ok(self): + self.get_ctrl().mach.modbus_read(int(self.json['address'])) + + +class ModbusWriteHandler(bbctrl.APIHandler): + def put_ok(self): + self.get_ctrl().mach.modbus_write(int(self.json['address']), + int(self.json['value'])) + + +class JogHandler(bbctrl.APIHandler): + def put_ok(self): + # Handle possible out of order jog command processing + if 'ts' in self.json: + ts = self.json['ts'] + id = self.get_cookie('client-id') + + if not hasattr(self.app, 'last_jog'): + self.app.last_jog = {} + + last = self.app.last_jog.get(id, 0) + self.app.last_jog[id] = ts + + if ts < last: return # Out of order + + self.get_ctrl().mach.jog(self.json) + + +# Base class for Web Socket connections +class ClientConnection(object): + def __init__(self, app): + self.app = app + self.count = 0 + + + def heartbeat(self): + self.timer = self.app.ioloop.call_later(3, self.heartbeat) + self.send({'heartbeat': self.count}) + self.count += 1 + + + def send(self, msg): raise HTTPError(400, 'Not implemented') + + + def on_open(self, id = None): + self.ctrl = self.app.get_ctrl(id) + + self.ctrl.state.add_listener(self.send) + self.ctrl.log.add_listener(self.send) + self.is_open = True + self.heartbeat() + self.app.opened(self.ctrl) + + + def on_close(self): + self.app.ioloop.remove_timeout(self.timer) + self.ctrl.state.remove_listener(self.send) + self.ctrl.log.remove_listener(self.send) + self.is_open = False + self.app.closed(self.ctrl) + + + def on_message(self, data): self.ctrl.mach.mdi(data) + + +# Used by CAMotics +class WSConnection(ClientConnection, tornado.websocket.WebSocketHandler): + def __init__(self, app, request, **kwargs): + ClientConnection.__init__(self, app) + tornado.websocket.WebSocketHandler.__init__( + self, app, request, **kwargs) + + def send(self, msg): self.write_message(msg) + def open(self): self.on_open() + + +# Used by Web frontend +class SockJSConnection(ClientConnection, sockjs.tornado.SockJSConnection): + def __init__(self, session): + ClientConnection.__init__(self, session.server.app) + sockjs.tornado.SockJSConnection.__init__(self, session) + + + def send(self, msg): + try: + sockjs.tornado.SockJSConnection.send(self, msg) + except: + self.close() + + + def on_open(self, info): + cookie = info.get_cookie('client-id') + if cookie is None: self.send(dict(sid = '')) # Trigger client reset + else: + id = cookie.value + + ip = info.ip + if 'X-Real-IP' in info.headers: ip = info.headers['X-Real-IP'] + self.app.get_ctrl(id).log.get('Web').info('Connection from %s' % ip) + super().on_open(id) + + +class StaticFileHandler(tornado.web.StaticFileHandler): + def set_extra_headers(self, path): + self.set_header('Cache-Control', + 'no-store, no-cache, must-revalidate, max-age=0') + + +class Web(tornado.web.Application): + def __init__(self, args, ioloop): + self.args = args + self.ioloop = ioloop + self.ctrls = {} + + # Init camera + if not args.disable_camera: + if self.args.demo: log = bbctrl.log.Log(args, ioloop, 'camera.log') + else: log = self.get_ctrl().log + self.camera = bbctrl.Camera(ioloop, args, log) + else: self.camera = None + + # Init controller + if not self.args.demo: + self.get_ctrl() + self.monitor = bbctrl.MonitorTemp(self) + + handlers = [ + (r'/websocket', WSConnection), + (r'/api/log', LogHandler), + (r'/api/message/(\d+)/ack', MessageAckHandler), + (r'/api/bugreport', BugReportHandler), + (r'/api/reboot', RebootHandler), + (r'/api/hostname', HostnameHandler), + (r'/api/wifi', WifiHandler), + (r'/api/remote/username', UsernameHandler), + (r'/api/remote/password', PasswordHandler), + (r'/api/config/load', ConfigLoadHandler), + (r'/api/config/download', ConfigDownloadHandler), + (r'/api/config/save', ConfigSaveHandler), + (r'/api/config/reset', ConfigResetHandler), + (r'/api/firmware/update', FirmwareUpdateHandler), + (r'/api/upgrade', UpgradeHandler), + (r'/api/file(/[^/]+)?', bbctrl.FileHandler), + (r'/api/path/([^/]+)((/positions)|(/speeds))?', PathHandler), + (r'/api/home(/[xyzabcXYZABC]((/set)|(/clear))?)?', HomeHandler), + (r'/api/start', StartHandler), + (r'/api/estop', EStopHandler), + (r'/api/clear', ClearHandler), + (r'/api/stop', StopHandler), + (r'/api/pause', PauseHandler), + (r'/api/unpause', UnpauseHandler), + (r'/api/pause/optional', OptionalPauseHandler), + (r'/api/step', StepHandler), + (r'/api/position/([xyzabcXYZABC])', PositionHandler), + (r'/api/override/feed/([\d.]+)', OverrideFeedHandler), + (r'/api/override/speed/([\d.]+)', OverrideSpeedHandler), + (r'/api/modbus/read', ModbusReadHandler), + (r'/api/modbus/write', ModbusWriteHandler), + (r'/api/jog', JogHandler), + (r'/api/video', bbctrl.VideoHandler), + (r'/(.*)', StaticFileHandler, + {'path': bbctrl.get_resource('http/'), + 'default_filename': 'index.html'}), + ] + + router = sockjs.tornado.SockJSRouter(SockJSConnection, '/sockjs') + router.app = self + + tornado.web.Application.__init__(self, router.urls + handlers) + + try: + self.listen(args.port, address = args.addr) + + except Exception as e: + raise Exception('Failed to bind %s:%d: %s' % ( + args.addr, args.port, e)) + + print('Listening on http://%s:%d/' % (args.addr, args.port)) + + + def opened(self, ctrl): ctrl.clear_timeout() + + + def closed(self, ctrl): + # Time out clients in demo mode + if self.args.demo: ctrl.set_timeout(self._reap_ctrl, ctrl) + + + def _reap_ctrl(self, ctrl): + ctrl.close() + del self.ctrls[ctrl.id] + + + def get_ctrl(self, id = None): + if not id or not self.args.demo: id = '' + + if not id in self.ctrls: + ctrl = bbctrl.Ctrl(self.args, self.ioloop, id) + self.ctrls[id] = ctrl + + else: ctrl = self.ctrls[id] + + return ctrl + + + # Override default logger + def log_request(self, handler): + log = self.get_ctrl(handler.get_cookie('client-id')).log.get('Web') + log.info("%d %s", handler.get_status(), handler._request_summary()) diff --git a/src/py/bbctrl/__init__.py b/src/py/bbctrl/__init__.py new file mode 100644 index 0000000..b1a6452 --- /dev/null +++ b/src/py/bbctrl/__init__.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 + +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import sys +import signal +import tornado +import argparse +import datetime + +from pkg_resources import Requirement, resource_filename + +from bbctrl.RequestHandler import RequestHandler +from bbctrl.APIHandler import APIHandler +from bbctrl.FileHandler import FileHandler +from bbctrl.Config import Config +from bbctrl.LCD import LCD, LCDPage +from bbctrl.Mach import Mach +from bbctrl.Web import Web +from bbctrl.Jog import Jog +from bbctrl.Ctrl import Ctrl +from bbctrl.Pwr import Pwr +from bbctrl.I2C import I2C +from bbctrl.Planner import Planner +from bbctrl.Preplanner import Preplanner +from bbctrl.State import State +from bbctrl.Comm import Comm +from bbctrl.CommandQueue import CommandQueue +from bbctrl.MainLCDPage import MainLCDPage +from bbctrl.IPLCDPage import IPLCDPage +from bbctrl.Camera import Camera, VideoHandler +from bbctrl.AVR import AVR +from bbctrl.AVREmu import AVREmu +from bbctrl.IOLoop import IOLoop +from bbctrl.MonitorTemp import MonitorTemp +import bbctrl.Cmd as Cmd +import bbctrl.v4l2 as v4l2 +import bbctrl.Log as log +import bbctrl.ObjGraph as ObjGraph + + +ctrl = None + + +def get_resource(path): + return resource_filename(Requirement.parse('bbctrl'), 'bbctrl/' + path) + + +def on_exit(sig = 0, func = None): + global ctrl + + print('Exit handler triggered: signal = %d', sig) + + if ctrl is not None: + ctrl.close() + ctrl = None + + sys.exit(0) + + +def time_str(): + return datetime.datetime.now().strftime('%Y%m%d-%H:%M:%S') + + + +class Debugger: + def __init__(self, ioloop, freq = 60 * 15, depth = 100): + self.ioloop = ioloop + self.freq = freq + self.depth = depth + self._callback() + + + def _callback(self): + with open('bbctrl-debug-%s.log' % time_str(), 'w') as log: + def line(name): + log.write('==== ' + name + ' ' + '=' * (74 - len(name)) + '\n') + + line('Common') + ObjGraph.show_most_common_types(limit = self.depth, file = log) + + log.write('\n') + line('Growth') + ObjGraph.show_growth(limit = self.depth, file = log) + + log.write('\n') + line('New IDs') + ObjGraph.get_new_ids(limit = self.depth, file = log) + + log.flush() + self.ioloop.call_later(self.freq, self._callback) + + + +def parse_args(): + parser = argparse.ArgumentParser( + description = 'Buildbotics Machine Controller') + + parser.add_argument('-p', '--port', default = 80, + type = int, help = 'HTTP port') + parser.add_argument('-a', '--addr', metavar = 'IP', default = '0.0.0.0', + help = 'HTTP address to bind') + parser.add_argument('-s', '--serial', default = '/dev/ttyAMA0', + help = 'Serial device') + parser.add_argument('-b', '--baud', default = 230400, type = int, + help = 'Serial baud rate') + parser.add_argument('--i2c-port', default = 1, type = int, + help = 'I2C port') + parser.add_argument('--lcd-addr', default = [0x27, 0x3f], type = int, + help = 'LCD I2C address') + parser.add_argument('--avr-addr', default = 0x2b, type = int, + help = 'AVR I2C address') + parser.add_argument('--pwr-addr', default = 0x60, type = int, + help = 'Power AVR I2C address') + parser.add_argument('-v', '--verbose', action = 'store_true', + help = 'Verbose output') + parser.add_argument('-l', '--log', metavar = "FILE", + help = 'Set a log file') + parser.add_argument('--disable-camera', action = 'store_true', + help = 'Disable the camera') + parser.add_argument('--width', default = 640, type = int, + help = 'Camera width') + parser.add_argument('--height', default = 480, type = int, + help = 'Camera height') + parser.add_argument('--fps', default = 15, type = int, + help = 'Camera frames per second') + parser.add_argument('--camera-clients', default = 4, + help = 'Maximum simultaneous camera clients') + parser.add_argument('--demo', action = 'store_true', + help = 'Enter demo mode') + parser.add_argument('--debug', default = 0, type = int, + help = 'Enable debug mode and set frequency in seconds') + parser.add_argument('--fast-emu', action = 'store_true', + help = 'Enter demo mode') + parser.add_argument('--client-timeout', default = 5 * 60, type = int, + help = 'Demo client timeout in seconds') + + return parser.parse_args() + + +def run(): + global ctrl + + args = parse_args() + + # Set signal handler + signal.signal(signal.SIGTERM, on_exit) + + # Create ioloop + ioloop = tornado.ioloop.IOLoop.current() + + # Set ObjGraph signal handler + if args.debug: Debugger(ioloop, args.debug) + + # Start server + web = Web(args, ioloop) + + try: + ioloop.start() + + except KeyboardInterrupt: on_exit() + + +if __name__ == '__main__': run() diff --git a/src/py/bbctrl/http b/src/py/bbctrl/http new file mode 100644 index 0000000..de7005b --- /dev/null +++ b/src/py/bbctrl/http @@ -0,0 +1 @@ +../../../build/http/ \ No newline at end of file diff --git a/src/py/bbctrl/plan.py b/src/py/bbctrl/plan.py new file mode 100644 index 0000000..0961970 --- /dev/null +++ b/src/py/bbctrl/plan.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python3 + +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import sys +import argparse +import json +import time +import math +import os +import re +import gzip +import struct +import math +import camotics.gplan as gplan # pylint: disable=no-name-in-module,import-error + + +reLogLine = re.compile( + r'^(?P[A-Z])[0-9 ]:' + r'((?P[^:]+):)?' + r'((?P\d+):)?' + r'((?P\d+):)?' + r'(?P.*)$') + + +def compute_unit(a, b): + unit = dict() + length = 0 + + for axis in 'xyz': + if axis in a and axis in b: + unit[axis] = b[axis] - a[axis] + length += unit[axis] * unit[axis] + + length = math.sqrt(length) + + if length: + for axis in 'xyz': + if axis in unit: unit[axis] /= length + + return unit + + +def compute_move(start, unit, dist): + move = dict() + + for axis in 'xyz': + if axis in unit and axis in start: + move[axis] = start[axis] + unit[axis] * dist + + return move + + +class Plan(object): + def __init__(self, path, state, config): + self.path = path + self.state = state + self.config = config + + self.lines = sum(1 for line in open(path, 'rb')) + + self.planner = gplan.Planner() + self.planner.set_resolver(self.get_var_cb) + self.planner.set_logger(self._log_cb, 1, 'LinePlanner:3') + self.planner.load(self.path, config) + + self.messages = [] + self.levels = dict(I = 'info', D = 'debug', W = 'warning', E = 'error', + C = 'critical') + + # Initialized axis states and bounds + self.bounds = dict(min = {}, max = {}) + for axis in 'xyz': + self.bounds['min'][axis] = math.inf + self.bounds['max'][axis] = -math.inf + + self.maxSpeed = 0 + self.currentSpeed = None + self.lastProgress = None + self.lastProgressTime = 0 + self.time = 0 + + + def add_to_bounds(self, axis, value): + if value < self.bounds['min'][axis]: self.bounds['min'][axis] = value + if self.bounds['max'][axis] < value: self.bounds['max'][axis] = value + + + def get_bounds(self): + # Remove infinity from bounds + for axis in 'xyz': + if self.bounds['min'][axis] == math.inf: + del self.bounds['min'][axis] + if self.bounds['max'][axis] == -math.inf: + del self.bounds['max'][axis] + + return self.bounds + + + def update_speed(self, s): + if self.currentSpeed == s: return False + self.currentSpeed = s + if self.maxSpeed < s: self.maxSpeed = s + + return True + + + def get_var_cb(self, name, units): + value = 0 + + if len(name) and name[0] == '_': + value = self.state.get(name[1:], 0) + if units == 'IMPERIAL': value /= 25.4 + + return value + + + def log_cb(self, level, msg, filename, line, column): + if level in self.levels: level = self.levels[level] + + # Ignore missing tool warning + if (level == 'warning' and + msg.startswith('Auto-creating missing tool')): + return + + self.messages.append( + dict(level = level, msg = msg, filename = filename, line = line, + column = column)) + + + def _log_cb(self, line): + line = line.strip() + m = reLogLine.match(line) + if not m: return + + level = m.group('level') + msg = m.group('msg') + filename = m.group('file') + line = m.group('line') + column = m.group('column') + + where = ':'.join(filter(None.__ne__, [filename, line, column])) + + if line is not None: line = int(line) + if column is not None: column = int(column) + + self.log_cb(level, msg, filename, line, column) + + + def progress(self, x): + if time.time() - self.lastProgressTime < 1 and x != 1: return + self.lastProgressTime = time.time() + + p = '%.4f\n' % x + + if self.lastProgress == p: return + self.lastProgress = p + + sys.stdout.write(p) + sys.stdout.flush() + + + def _run(self): + start = time.clock() + line = 0 + maxLine = 0 + maxLineTime = time.clock() + position = {axis: 0 for axis in 'xyz'} + rapid = False + + # Execute plan + try: + while self.planner.has_more(): + cmd = self.planner.next() + self.planner.set_active(cmd['id']) # Release plan + + # Cannot synchronize with actual machine so fake it + if self.planner.is_synchronizing(): self.planner.synchronize(0) + + if cmd['type'] == 'line': + if not (cmd.get('first', False) or + cmd.get('seeking', False)): + self.time += sum(cmd['times']) / 1000 + + target = cmd['target'] + move = {} + startPos = dict() + + for axis in 'xyz': + if axis in target: + startPos[axis] = position[axis] + position[axis] = target[axis] + move[axis] = target[axis] + self.add_to_bounds(axis, move[axis]) + + if 'rapid' in cmd: move['rapid'] = cmd['rapid'] + + if 'speeds' in cmd: + unit = compute_unit(startPos, target) + + for d, s in cmd['speeds']: + cur = self.currentSpeed + + if self.update_speed(s): + m = compute_move(startPos, unit, d) + + if cur is not None: + m['s'] = cur + yield m + + move['s'] = s + + yield move + + elif cmd['type'] == 'set': + if cmd['name'] == 'line': + line = cmd['value'] + if maxLine < line: + maxLine = line + maxLineTime = time.clock() + + elif cmd['name'] == 'speed': + s = cmd['value'] + if self.update_speed(s): yield {'s': s} + + elif cmd['type'] == 'dwell': self.time += cmd['seconds'] + + if args.max_time < time.clock() - start: + raise Exception('Max planning time (%d sec) exceeded.' % + args.max_time) + + if args.max_loop < time.clock() - maxLineTime: + raise Exception('Max loop time (%d sec) exceeded.' % + args.max_loop) + + if self.lines: self.progress(maxLine / self.lines) + + except Exception as e: + self.log_cb('error', str(e), os.path.basename(self.path), line, 0) + + + def run(self): + lastS = 0 + speed = 0 + first = True + x, y, z = 0, 0, 0 + + with gzip.open('positions.gz', 'wb') as f1: + with gzip.open('speeds.gz', 'wb') as f2: + for move in self._run(): + x = move.get('x', x) + y = move.get('y', y) + z = move.get('z', z) + rapid = move.get('rapid', False) + speed = move.get('s', speed) + s = struct.pack('= 0x1000 + + +class v4l2_queryctrl(ctypes.Structure): + _fields_ = [ + ('id', ctypes.c_uint32), + ('type', v4l2_ctrl_type), + ('name', ctypes.c_char * 32), + ('minimum', ctypes.c_int32), + ('maximum', ctypes.c_int32), + ('step', ctypes.c_int32), + ('default', ctypes.c_int32), + ('flags', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +class v4l2_querymenu(ctypes.Structure): + _fields_ = [ + ('id', ctypes.c_uint32), + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('reserved', ctypes.c_uint32), + ] + + +V4L2_CTRL_FLAG_DISABLED = 0x0001 +V4L2_CTRL_FLAG_GRABBED = 0x0002 +V4L2_CTRL_FLAG_READ_ONLY = 0x0004 +V4L2_CTRL_FLAG_UPDATE = 0x0008 +V4L2_CTRL_FLAG_INACTIVE = 0x0010 +V4L2_CTRL_FLAG_SLIDER = 0x0020 +V4L2_CTRL_FLAG_WRITE_ONLY = 0x0040 + +V4L2_CTRL_FLAG_NEXT_CTRL = 0x80000000 + +V4L2_CID_BASE = V4L2_CTRL_CLASS_USER | 0x900 +V4L2_CID_USER_BASE = V4L2_CID_BASE +V4L2_CID_PRIVATE_BASE = 0x08000000 + +V4L2_CID_USER_CLASS = V4L2_CTRL_CLASS_USER | 1 +V4L2_CID_BRIGHTNESS = V4L2_CID_BASE + 0 +V4L2_CID_CONTRAST = V4L2_CID_BASE + 1 +V4L2_CID_SATURATION = V4L2_CID_BASE + 2 +V4L2_CID_HUE = V4L2_CID_BASE + 3 +V4L2_CID_AUDIO_VOLUME = V4L2_CID_BASE + 5 +V4L2_CID_AUDIO_BALANCE = V4L2_CID_BASE + 6 +V4L2_CID_AUDIO_BASS = V4L2_CID_BASE + 7 +V4L2_CID_AUDIO_TREBLE = V4L2_CID_BASE + 8 +V4L2_CID_AUDIO_MUTE = V4L2_CID_BASE + 9 +V4L2_CID_AUDIO_LOUDNESS = V4L2_CID_BASE + 10 +V4L2_CID_BLACK_LEVEL = V4L2_CID_BASE + 11 # Deprecated +V4L2_CID_AUTO_WHITE_BALANCE = V4L2_CID_BASE + 12 +V4L2_CID_DO_WHITE_BALANCE = V4L2_CID_BASE + 13 +V4L2_CID_RED_BALANCE = V4L2_CID_BASE + 14 +V4L2_CID_BLUE_BALANCE = V4L2_CID_BASE + 15 +V4L2_CID_GAMMA = V4L2_CID_BASE + 16 +V4L2_CID_WHITENESS = V4L2_CID_GAMMA # Deprecated +V4L2_CID_EXPOSURE = V4L2_CID_BASE + 17 +V4L2_CID_AUTOGAIN = V4L2_CID_BASE + 18 +V4L2_CID_GAIN = V4L2_CID_BASE + 19 +V4L2_CID_HFLIP = V4L2_CID_BASE + 20 +V4L2_CID_VFLIP = V4L2_CID_BASE + 21 + +# Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET +V4L2_CID_HCENTER = V4L2_CID_BASE + 22 +V4L2_CID_VCENTER = V4L2_CID_BASE + 23 + +V4L2_CID_POWER_LINE_FREQUENCY = V4L2_CID_BASE + 24 + +v4l2_power_line_frequency = enum +( + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, +) = range(3) + +V4L2_CID_HUE_AUTO = V4L2_CID_BASE + 25 +V4L2_CID_WHITE_BALANCE_TEMPERATURE = V4L2_CID_BASE + 26 +V4L2_CID_SHARPNESS = V4L2_CID_BASE + 27 +V4L2_CID_BACKLIGHT_COMPENSATION = V4L2_CID_BASE + 28 +V4L2_CID_CHROMA_AGC = V4L2_CID_BASE + 29 +V4L2_CID_COLOR_KILLER = V4L2_CID_BASE + 30 +V4L2_CID_COLORFX = V4L2_CID_BASE + 31 + +v4l2_colorfx = enum +( + V4L2_COLORFX_NONE, + V4L2_COLORFX_BW, + V4L2_COLORFX_SEPIA, +) = range(3) + +V4L2_CID_AUTOBRIGHTNESS = V4L2_CID_BASE + 32 +V4L2_CID_BAND_STOP_FILTER = V4L2_CID_BASE + 33 + +V4L2_CID_ROTATE = V4L2_CID_BASE + 34 +V4L2_CID_BG_COLOR = V4L2_CID_BASE + 35 +V4L2_CID_LASTP1 = V4L2_CID_BASE + 36 + +V4L2_CID_MPEG_BASE = V4L2_CTRL_CLASS_MPEG | 0x900 +V4L2_CID_MPEG_CLASS = V4L2_CTRL_CLASS_MPEG | 1 + +# MPEG streams +V4L2_CID_MPEG_STREAM_TYPE = V4L2_CID_MPEG_BASE + 0 + +v4l2_mpeg_stream_type = enum +( + V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS, + V4L2_MPEG_STREAM_TYPE_MPEG1_SS, + V4L2_MPEG_STREAM_TYPE_MPEG2_DVD, + V4L2_MPEG_STREAM_TYPE_MPEG1_VCD, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, +) = range(6) + +V4L2_CID_MPEG_STREAM_PID_PMT = V4L2_CID_MPEG_BASE + 1 +V4L2_CID_MPEG_STREAM_PID_AUDIO = V4L2_CID_MPEG_BASE + 2 +V4L2_CID_MPEG_STREAM_PID_VIDEO = V4L2_CID_MPEG_BASE + 3 +V4L2_CID_MPEG_STREAM_PID_PCR = V4L2_CID_MPEG_BASE + 4 +V4L2_CID_MPEG_STREAM_PES_ID_AUDIO = V4L2_CID_MPEG_BASE + 5 +V4L2_CID_MPEG_STREAM_PES_ID_VIDEO = V4L2_CID_MPEG_BASE + 6 +V4L2_CID_MPEG_STREAM_VBI_FMT = V4L2_CID_MPEG_BASE + 7 + +v4l2_mpeg_stream_vbi_fmt = enum +( + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, +) = range(2) + +V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ = V4L2_CID_MPEG_BASE + 100 + +v4l2_mpeg_audio_sampling_freq = enum +( + V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, +) = range(3) + +V4L2_CID_MPEG_AUDIO_ENCODING = V4L2_CID_MPEG_BASE + 101 + +v4l2_mpeg_audio_encoding = enum +( + V4L2_MPEG_AUDIO_ENCODING_LAYER_1, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_3, + V4L2_MPEG_AUDIO_ENCODING_AAC, + V4L2_MPEG_AUDIO_ENCODING_AC3, +) = range(5) + +V4L2_CID_MPEG_AUDIO_L1_BITRATE = V4L2_CID_MPEG_BASE + 102 + +v4l2_mpeg_audio_l1_bitrate = enum +( + V4L2_MPEG_AUDIO_L1_BITRATE_32K, + V4L2_MPEG_AUDIO_L1_BITRATE_64K, + V4L2_MPEG_AUDIO_L1_BITRATE_96K, + V4L2_MPEG_AUDIO_L1_BITRATE_128K, + V4L2_MPEG_AUDIO_L1_BITRATE_160K, + V4L2_MPEG_AUDIO_L1_BITRATE_192K, + V4L2_MPEG_AUDIO_L1_BITRATE_224K, + V4L2_MPEG_AUDIO_L1_BITRATE_256K, + V4L2_MPEG_AUDIO_L1_BITRATE_288K, + V4L2_MPEG_AUDIO_L1_BITRATE_320K, + V4L2_MPEG_AUDIO_L1_BITRATE_352K, + V4L2_MPEG_AUDIO_L1_BITRATE_384K, + V4L2_MPEG_AUDIO_L1_BITRATE_416K, + V4L2_MPEG_AUDIO_L1_BITRATE_448K, +) = range(14) + +V4L2_CID_MPEG_AUDIO_L2_BITRATE = V4L2_CID_MPEG_BASE + 103 + +v4l2_mpeg_audio_l2_bitrate = enum +( + V4L2_MPEG_AUDIO_L2_BITRATE_32K, + V4L2_MPEG_AUDIO_L2_BITRATE_48K, + V4L2_MPEG_AUDIO_L2_BITRATE_56K, + V4L2_MPEG_AUDIO_L2_BITRATE_64K, + V4L2_MPEG_AUDIO_L2_BITRATE_80K, + V4L2_MPEG_AUDIO_L2_BITRATE_96K, + V4L2_MPEG_AUDIO_L2_BITRATE_112K, + V4L2_MPEG_AUDIO_L2_BITRATE_128K, + V4L2_MPEG_AUDIO_L2_BITRATE_160K, + V4L2_MPEG_AUDIO_L2_BITRATE_192K, + V4L2_MPEG_AUDIO_L2_BITRATE_224K, + V4L2_MPEG_AUDIO_L2_BITRATE_256K, + V4L2_MPEG_AUDIO_L2_BITRATE_320K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, +) = range(14) + +V4L2_CID_MPEG_AUDIO_L3_BITRATE = V4L2_CID_MPEG_BASE + 104 + +v4l2_mpeg_audio_l3_bitrate = enum +( + V4L2_MPEG_AUDIO_L3_BITRATE_32K, + V4L2_MPEG_AUDIO_L3_BITRATE_40K, + V4L2_MPEG_AUDIO_L3_BITRATE_48K, + V4L2_MPEG_AUDIO_L3_BITRATE_56K, + V4L2_MPEG_AUDIO_L3_BITRATE_64K, + V4L2_MPEG_AUDIO_L3_BITRATE_80K, + V4L2_MPEG_AUDIO_L3_BITRATE_96K, + V4L2_MPEG_AUDIO_L3_BITRATE_112K, + V4L2_MPEG_AUDIO_L3_BITRATE_128K, + V4L2_MPEG_AUDIO_L3_BITRATE_160K, + V4L2_MPEG_AUDIO_L3_BITRATE_192K, + V4L2_MPEG_AUDIO_L3_BITRATE_224K, + V4L2_MPEG_AUDIO_L3_BITRATE_256K, + V4L2_MPEG_AUDIO_L3_BITRATE_320K, +) = range(14) + +V4L2_CID_MPEG_AUDIO_MODE = V4L2_CID_MPEG_BASE + 105 + +v4l2_mpeg_audio_mode = enum +( + V4L2_MPEG_AUDIO_MODE_STEREO, + V4L2_MPEG_AUDIO_MODE_JOINT_STEREO, + V4L2_MPEG_AUDIO_MODE_DUAL, + V4L2_MPEG_AUDIO_MODE_MONO, +) = range(4) + +V4L2_CID_MPEG_AUDIO_MODE_EXTENSION = V4L2_CID_MPEG_BASE + 106 + +v4l2_mpeg_audio_mode_extension = enum +( + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, +) = range(4) + +V4L2_CID_MPEG_AUDIO_EMPHASIS = V4L2_CID_MPEG_BASE + 107 + +v4l2_mpeg_audio_emphasis = enum +( + V4L2_MPEG_AUDIO_EMPHASIS_NONE, + V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, +) = range(3) + +V4L2_CID_MPEG_AUDIO_CRC = V4L2_CID_MPEG_BASE + 108 + +v4l2_mpeg_audio_crc = enum +( + V4L2_MPEG_AUDIO_CRC_NONE, + V4L2_MPEG_AUDIO_CRC_CRC16, +) = range(2) + +V4L2_CID_MPEG_AUDIO_MUTE = V4L2_CID_MPEG_BASE + 109 +V4L2_CID_MPEG_AUDIO_AAC_BITRATE = V4L2_CID_MPEG_BASE + 110 +V4L2_CID_MPEG_AUDIO_AC3_BITRATE = V4L2_CID_MPEG_BASE + 111 + +v4l2_mpeg_audio_ac3_bitrate = enum +( + V4L2_MPEG_AUDIO_AC3_BITRATE_32K, + V4L2_MPEG_AUDIO_AC3_BITRATE_40K, + V4L2_MPEG_AUDIO_AC3_BITRATE_48K, + V4L2_MPEG_AUDIO_AC3_BITRATE_56K, + V4L2_MPEG_AUDIO_AC3_BITRATE_64K, + V4L2_MPEG_AUDIO_AC3_BITRATE_80K, + V4L2_MPEG_AUDIO_AC3_BITRATE_96K, + V4L2_MPEG_AUDIO_AC3_BITRATE_112K, + V4L2_MPEG_AUDIO_AC3_BITRATE_128K, + V4L2_MPEG_AUDIO_AC3_BITRATE_160K, + V4L2_MPEG_AUDIO_AC3_BITRATE_192K, + V4L2_MPEG_AUDIO_AC3_BITRATE_224K, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K, + V4L2_MPEG_AUDIO_AC3_BITRATE_320K, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, + V4L2_MPEG_AUDIO_AC3_BITRATE_512K, + V4L2_MPEG_AUDIO_AC3_BITRATE_576K, + V4L2_MPEG_AUDIO_AC3_BITRATE_640K, +) = range(19) + +V4L2_CID_MPEG_VIDEO_ENCODING = V4L2_CID_MPEG_BASE + 200 + +v4l2_mpeg_video_encoding = enum +( + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, +) = range(3) + +V4L2_CID_MPEG_VIDEO_ASPECT = V4L2_CID_MPEG_BASE + 201 + +v4l2_mpeg_video_aspect = enum +( + V4L2_MPEG_VIDEO_ASPECT_1x1, + V4L2_MPEG_VIDEO_ASPECT_4x3, + V4L2_MPEG_VIDEO_ASPECT_16x9, + V4L2_MPEG_VIDEO_ASPECT_221x100, +) = range(4) + +V4L2_CID_MPEG_VIDEO_B_FRAMES = V4L2_CID_MPEG_BASE + 202 +V4L2_CID_MPEG_VIDEO_GOP_SIZE = V4L2_CID_MPEG_BASE + 203 +V4L2_CID_MPEG_VIDEO_GOP_CLOSURE = V4L2_CID_MPEG_BASE + 204 +V4L2_CID_MPEG_VIDEO_PULLDOWN = V4L2_CID_MPEG_BASE + 205 +V4L2_CID_MPEG_VIDEO_BITRATE_MODE = V4L2_CID_MPEG_BASE + 206 + +v4l2_mpeg_video_bitrate_mode = enum +( + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, +) = range(2) + +V4L2_CID_MPEG_VIDEO_BITRATE = V4L2_CID_MPEG_BASE + 207 +V4L2_CID_MPEG_VIDEO_BITRATE_PEAK = V4L2_CID_MPEG_BASE + 208 +V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION = V4L2_CID_MPEG_BASE + 209 +V4L2_CID_MPEG_VIDEO_MUTE = V4L2_CID_MPEG_BASE + 210 +V4L2_CID_MPEG_VIDEO_MUTE_YUV = V4L2_CID_MPEG_BASE + 211 + +V4L2_CID_MPEG_CX2341X_BASE = V4L2_CTRL_CLASS_MPEG | 0x1000 +V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE = V4L2_CID_MPEG_CX2341X_BASE + 0 + +v4l2_mpeg_cx2341x_video_spatial_filter_mode = enum +( + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, +) = range(2) + +V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER = V4L2_CID_MPEG_CX2341X_BASE + 1 +V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 2 + +v4l2_mpeg_cx2341x_video_luma_spatial_filter_type = enum +( + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, +) = range(5) + +V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 3 + +v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type = enum +( + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, +) = range(2) + +V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE = V4L2_CID_MPEG_CX2341X_BASE + 4 + +v4l2_mpeg_cx2341x_video_temporal_filter_mode = enum +( + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, +) = range(2) + +V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER = V4L2_CID_MPEG_CX2341X_BASE + 5 +V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 6 + +v4l2_mpeg_cx2341x_video_median_filter_type = enum +( + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, +) = range(5) + +V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM = V4L2_CID_MPEG_CX2341X_BASE + 7 +V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP = V4L2_CID_MPEG_CX2341X_BASE + 8 +V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM = V4L2_CID_MPEG_CX2341X_BASE + 9 +V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP = V4L2_CID_MPEG_CX2341X_BASE + 10 +V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS = V4L2_CID_MPEG_CX2341X_BASE + 11 + +V4L2_CID_CAMERA_CLASS_BASE = V4L2_CTRL_CLASS_CAMERA | 0x900 +V4L2_CID_CAMERA_CLASS = V4L2_CTRL_CLASS_CAMERA | 1 + +V4L2_CID_EXPOSURE_AUTO = V4L2_CID_CAMERA_CLASS_BASE + 1 + +v4l2_exposure_auto_type = enum +( + V4L2_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, + V4L2_EXPOSURE_SHUTTER_PRIORITY, + V4L2_EXPOSURE_APERTURE_PRIORITY, +) = range(4) + +V4L2_CID_EXPOSURE_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 2 +V4L2_CID_EXPOSURE_AUTO_PRIORITY = V4L2_CID_CAMERA_CLASS_BASE + 3 + +V4L2_CID_PAN_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 4 +V4L2_CID_TILT_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 5 +V4L2_CID_PAN_RESET = V4L2_CID_CAMERA_CLASS_BASE + 6 +V4L2_CID_TILT_RESET = V4L2_CID_CAMERA_CLASS_BASE + 7 + +V4L2_CID_PAN_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 8 +V4L2_CID_TILT_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 9 + +V4L2_CID_FOCUS_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 10 +V4L2_CID_FOCUS_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 11 +V4L2_CID_FOCUS_AUTO = V4L2_CID_CAMERA_CLASS_BASE + 12 + +V4L2_CID_ZOOM_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 13 +V4L2_CID_ZOOM_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 14 +V4L2_CID_ZOOM_CONTINUOUS = V4L2_CID_CAMERA_CLASS_BASE + 15 + +V4L2_CID_PRIVACY = V4L2_CID_CAMERA_CLASS_BASE + 16 + +V4L2_CID_FM_TX_CLASS_BASE = V4L2_CTRL_CLASS_FM_TX | 0x900 +V4L2_CID_FM_TX_CLASS = V4L2_CTRL_CLASS_FM_TX | 1 + +V4L2_CID_RDS_TX_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 1 +V4L2_CID_RDS_TX_PI = V4L2_CID_FM_TX_CLASS_BASE + 2 +V4L2_CID_RDS_TX_PTY = V4L2_CID_FM_TX_CLASS_BASE + 3 +V4L2_CID_RDS_TX_PS_NAME = V4L2_CID_FM_TX_CLASS_BASE + 5 +V4L2_CID_RDS_TX_RADIO_TEXT = V4L2_CID_FM_TX_CLASS_BASE + 6 + +V4L2_CID_AUDIO_LIMITER_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 64 +V4L2_CID_AUDIO_LIMITER_RELEASE_TIME = V4L2_CID_FM_TX_CLASS_BASE + 65 +V4L2_CID_AUDIO_LIMITER_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 66 + +V4L2_CID_AUDIO_COMPRESSION_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 80 +V4L2_CID_AUDIO_COMPRESSION_GAIN = V4L2_CID_FM_TX_CLASS_BASE + 81 +V4L2_CID_AUDIO_COMPRESSION_THRESHOLD = V4L2_CID_FM_TX_CLASS_BASE + 82 +V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME = V4L2_CID_FM_TX_CLASS_BASE + 83 +V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME = V4L2_CID_FM_TX_CLASS_BASE + 84 + +V4L2_CID_PILOT_TONE_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 96 +V4L2_CID_PILOT_TONE_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 97 +V4L2_CID_PILOT_TONE_FREQUENCY = V4L2_CID_FM_TX_CLASS_BASE + 98 + +V4L2_CID_TUNE_PREEMPHASIS = V4L2_CID_FM_TX_CLASS_BASE + 112 + +v4l2_preemphasis = enum +( + V4L2_PREEMPHASIS_DISABLED, + V4L2_PREEMPHASIS_50_uS, + V4L2_PREEMPHASIS_75_uS, +) = range(3) + +V4L2_CID_TUNE_POWER_LEVEL = V4L2_CID_FM_TX_CLASS_BASE + 113 +V4L2_CID_TUNE_ANTENNA_CAPACITOR = V4L2_CID_FM_TX_CLASS_BASE + 114 + + +# JPEG-class control IDs + +V4L2_CID_JPEG_CLASS_BASE = V4L2_CTRL_CLASS_JPEG | 0x900 +V4L2_CID_JPEG_CLASS = V4L2_CTRL_CLASS_JPEG | 1 + +V4L2_CID_JPEG_CHROMA_SUBSAMPLING = V4L2_CID_JPEG_CLASS_BASE + 1 + +v4l2_jpeg_chroma_subsampling = enum +( + V4L2_JPEG_CHROMA_SUBSAMPLING_444, + V4L2_JPEG_CHROMA_SUBSAMPLING_422, + V4L2_JPEG_CHROMA_SUBSAMPLING_420, + V4L2_JPEG_CHROMA_SUBSAMPLING_411, + V4L2_JPEG_CHROMA_SUBSAMPLING_410, + V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, +) = range(6) + +V4L2_CID_JPEG_RESTART_INTERVAL = V4L2_CID_JPEG_CLASS_BASE + 2 +V4L2_CID_JPEG_COMPRESSION_QUALITY = V4L2_CID_JPEG_CLASS_BASE + 3 + +V4L2_CID_JPEG_ACTIVE_MARKER = V4L2_CID_JPEG_CLASS_BASE + 4 +V4L2_JPEG_ACTIVE_MARKER_APP0 = 1 << 0 +V4L2_JPEG_ACTIVE_MARKER_APP1 = 1 << 1 +V4L2_JPEG_ACTIVE_MARKER_COM = 1 << 16 +V4L2_JPEG_ACTIVE_MARKER_DQT = 1 << 17 +V4L2_JPEG_ACTIVE_MARKER_DHT = 1 << 18 + + +# +# Tuning +# + +class v4l2_tuner(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('type', v4l2_tuner_type), + ('capability', ctypes.c_uint32), + ('rangelow', ctypes.c_uint32), + ('rangehigh', ctypes.c_uint32), + ('rxsubchans', ctypes.c_uint32), + ('audmode', ctypes.c_uint32), + ('signal', ctypes.c_int32), + ('afc', ctypes.c_int32), + ('reserved', ctypes.c_uint32 * 4), + ] + + +class v4l2_modulator(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('capability', ctypes.c_uint32), + ('rangelow', ctypes.c_uint32), + ('rangehigh', ctypes.c_uint32), + ('txsubchans', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ] + + +V4L2_TUNER_CAP_LOW = 0x0001 +V4L2_TUNER_CAP_NORM = 0x0002 +V4L2_TUNER_CAP_STEREO = 0x0010 +V4L2_TUNER_CAP_LANG2 = 0x0020 +V4L2_TUNER_CAP_SAP = 0x0020 +V4L2_TUNER_CAP_LANG1 = 0x0040 +V4L2_TUNER_CAP_RDS = 0x0080 + +V4L2_TUNER_SUB_MONO = 0x0001 +V4L2_TUNER_SUB_STEREO = 0x0002 +V4L2_TUNER_SUB_LANG2 = 0x0004 +V4L2_TUNER_SUB_SAP = 0x0004 +V4L2_TUNER_SUB_LANG1 = 0x0008 +V4L2_TUNER_SUB_RDS = 0x0010 + +V4L2_TUNER_MODE_MONO = 0x0000 +V4L2_TUNER_MODE_STEREO = 0x0001 +V4L2_TUNER_MODE_LANG2 = 0x0002 +V4L2_TUNER_MODE_SAP = 0x0002 +V4L2_TUNER_MODE_LANG1 = 0x0003 +V4L2_TUNER_MODE_LANG1_LANG2 = 0x0004 + + +class v4l2_frequency(ctypes.Structure): + _fields_ = [ + ('tuner', ctypes.c_uint32), + ('type', v4l2_tuner_type), + ('frequency', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 8), + ] + + +class v4l2_hw_freq_seek(ctypes.Structure): + _fields_ = [ + ('tuner', ctypes.c_uint32), + ('type', v4l2_tuner_type), + ('seek_upward', ctypes.c_uint32), + ('wrap_around', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 8), + ] + + +# +# RDS +# + +class v4l2_rds_data(ctypes.Structure): + _fields_ = [ + ('lsb', ctypes.c_char), + ('msb', ctypes.c_char), + ('block', ctypes.c_char), + ] + + _pack_ = True + + +V4L2_RDS_BLOCK_MSK = 0x7 +V4L2_RDS_BLOCK_A = 0 +V4L2_RDS_BLOCK_B = 1 +V4L2_RDS_BLOCK_C = 2 +V4L2_RDS_BLOCK_D = 3 +V4L2_RDS_BLOCK_C_ALT = 4 +V4L2_RDS_BLOCK_INVALID = 7 + +V4L2_RDS_BLOCK_CORRECTED = 0x40 +V4L2_RDS_BLOCK_ERROR = 0x80 + + +# +# Audio +# + +class v4l2_audio(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('capability', ctypes.c_uint32), + ('mode', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +V4L2_AUDCAP_STEREO = 0x00001 +V4L2_AUDCAP_AVL = 0x00002 + +V4L2_AUDMODE_AVL = 0x00001 + + +class v4l2_audioout(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('capability', ctypes.c_uint32), + ('mode', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +# +# Mpeg services (experimental) +# + +V4L2_ENC_IDX_FRAME_I = 0 +V4L2_ENC_IDX_FRAME_P = 1 +V4L2_ENC_IDX_FRAME_B = 2 +V4L2_ENC_IDX_FRAME_MASK = 0xf + + +class v4l2_enc_idx_entry(ctypes.Structure): + _fields_ = [ + ('offset', ctypes.c_uint64), + ('pts', ctypes.c_uint64), + ('length', ctypes.c_uint32), + ('flags', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +V4L2_ENC_IDX_ENTRIES = 64 + + +class v4l2_enc_idx(ctypes.Structure): + _fields_ = [ + ('entries', ctypes.c_uint32), + ('entries_cap', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ('entry', v4l2_enc_idx_entry * V4L2_ENC_IDX_ENTRIES), + ] + + +V4L2_ENC_CMD_START = 0 +V4L2_ENC_CMD_STOP = 1 +V4L2_ENC_CMD_PAUSE = 2 +V4L2_ENC_CMD_RESUME = 3 + +V4L2_ENC_CMD_STOP_AT_GOP_END = 1 << 0 + + +class v4l2_encoder_cmd(ctypes.Structure): + class _u(ctypes.Union): + class _s(ctypes.Structure): + _fields_ = [ + ('data', ctypes.c_uint32 * 8), + ] + + _fields_ = [ + ('raw', _s), + ] + + _fields_ = [ + ('cmd', ctypes.c_uint32), + ('flags', ctypes.c_uint32), + ('_u', _u), + ] + + _anonymous_ = ('_u',) + + +# +# Data services (VBI) +# + +class v4l2_vbi_format(ctypes.Structure): + _fields_ = [ + ('sampling_rate', ctypes.c_uint32), + ('offset', ctypes.c_uint32), + ('samples_per_line', ctypes.c_uint32), + ('sample_format', ctypes.c_uint32), + ('start', ctypes.c_int32 * 2), + ('count', ctypes.c_uint32 * 2), + ('flags', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +V4L2_VBI_UNSYNC = 1 << 0 +V4L2_VBI_INTERLACED = 1 << 1 + + +class v4l2_sliced_vbi_format(ctypes.Structure): + _fields_ = [ + ('service_set', ctypes.c_uint16), + ('service_lines', ctypes.c_uint16 * 2 * 24), + ('io_size', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +V4L2_SLICED_TELETEXT_B = 0x0001 +V4L2_SLICED_VPS = 0x0400 +V4L2_SLICED_CAPTION_525 = 0x1000 +V4L2_SLICED_WSS_625 = 0x4000 +V4L2_SLICED_VBI_525 = V4L2_SLICED_CAPTION_525 +V4L2_SLICED_VBI_625 = ( + V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625) + + +class v4l2_sliced_vbi_cap(ctypes.Structure): + _fields_ = [ + ('service_set', ctypes.c_uint16), + ('service_lines', ctypes.c_uint16 * 2 * 24), + ('type', v4l2_buf_type), + ('reserved', ctypes.c_uint32 * 3), + ] + + +class v4l2_sliced_vbi_data(ctypes.Structure): + _fields_ = [ + ('id', ctypes.c_uint32), + ('field', ctypes.c_uint32), + ('line', ctypes.c_uint32), + ('reserved', ctypes.c_uint32), + ('data', ctypes.c_char * 48), + ] + + +# +# Sliced VBI data inserted into MPEG Streams +# + + +V4L2_MPEG_VBI_IVTV_TELETEXT_B = 1 +V4L2_MPEG_VBI_IVTV_CAPTION_525 = 4 +V4L2_MPEG_VBI_IVTV_WSS_625 = 5 +V4L2_MPEG_VBI_IVTV_VPS = 7 + + +class v4l2_mpeg_vbi_itv0_line(ctypes.Structure): + _fields_ = [ + ('id', ctypes.c_char), + ('data', ctypes.c_char * 42), + ] + + _pack_ = True + + +class v4l2_mpeg_vbi_itv0(ctypes.Structure): + _fields_ = [ + ('linemask', ctypes.c_uint32 * 2), # how to define __le32 in ctypes? + ('line', v4l2_mpeg_vbi_itv0_line * 35), + ] + + _pack_ = True + + +class v4l2_mpeg_vbi_ITV0(ctypes.Structure): + _fields_ = [ + ('line', v4l2_mpeg_vbi_itv0_line * 36), + ] + + _pack_ = True + + +V4L2_MPEG_VBI_IVTV_MAGIC0 = "itv0" +V4L2_MPEG_VBI_IVTV_MAGIC1 = "ITV0" + + +class v4l2_mpeg_vbi_fmt_ivtv(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('itv0', v4l2_mpeg_vbi_itv0), + ('ITV0', v4l2_mpeg_vbi_ITV0), + ] + + _fields_ = [ + ('magic', ctypes.c_char * 4), + ('_u', _u) + ] + + _anonymous_ = ('_u',) + _pack_ = True + + +# +# Aggregate structures +# + +class v4l2_format(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('pix', v4l2_pix_format), + ('win', v4l2_window), + ('vbi', v4l2_vbi_format), + ('sliced', v4l2_sliced_vbi_format), + ('raw_data', ctypes.c_char * 200), + ] + + _fields_ = [ + ('type', v4l2_buf_type), + ('fmt', _u), + ] + + +class v4l2_streamparm(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('capture', v4l2_captureparm), + ('output', v4l2_outputparm), + ('raw_data', ctypes.c_char * 200), + ] + + _fields_ = [ + ('type', v4l2_buf_type), + ('parm', _u) + ] + + +# +# Advanced debugging +# + +V4L2_CHIP_MATCH_HOST = 0 +V4L2_CHIP_MATCH_I2C_DRIVER = 1 +V4L2_CHIP_MATCH_I2C_ADDR = 2 +V4L2_CHIP_MATCH_AC97 = 3 + + +class v4l2_dbg_match(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('addr', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ] + + _fields_ = [ + ('type', ctypes.c_uint32), + ('_u', _u), + ] + + _anonymous_ = ('_u',) + _pack_ = True + + +class v4l2_dbg_register(ctypes.Structure): + _fields_ = [ + ('match', v4l2_dbg_match), + ('size', ctypes.c_uint32), + ('reg', ctypes.c_uint64), + ('val', ctypes.c_uint64), + ] + + _pack_ = True + + +class v4l2_dbg_chip_ident(ctypes.Structure): + _fields_ = [ + ('match', v4l2_dbg_match), + ('ident', ctypes.c_uint32), + ('revision', ctypes.c_uint32), + ] + + _pack_ = True + + +# +# ioctl codes for video devices +# + +VIDIOC_QUERYCAP = _IOR('V', 0, v4l2_capability) +VIDIOC_RESERVED = _IO('V', 1) +VIDIOC_ENUM_FMT = _IOWR('V', 2, v4l2_fmtdesc) +VIDIOC_G_FMT = _IOWR('V', 4, v4l2_format) +VIDIOC_S_FMT = _IOWR('V', 5, v4l2_format) +VIDIOC_REQBUFS = _IOWR('V', 8, v4l2_requestbuffers) +VIDIOC_QUERYBUF = _IOWR('V', 9, v4l2_buffer) +VIDIOC_G_FBUF = _IOR('V', 10, v4l2_framebuffer) +VIDIOC_S_FBUF = _IOW('V', 11, v4l2_framebuffer) +VIDIOC_OVERLAY = _IOW('V', 14, ctypes.c_int) +VIDIOC_QBUF = _IOWR('V', 15, v4l2_buffer) +VIDIOC_DQBUF = _IOWR('V', 17, v4l2_buffer) +VIDIOC_STREAMON = _IOW('V', 18, ctypes.c_int) +VIDIOC_STREAMOFF = _IOW('V', 19, ctypes.c_int) +VIDIOC_G_PARM = _IOWR('V', 21, v4l2_streamparm) +VIDIOC_S_PARM = _IOWR('V', 22, v4l2_streamparm) +VIDIOC_G_STD = _IOR('V', 23, v4l2_std_id) +VIDIOC_S_STD = _IOW('V', 24, v4l2_std_id) +VIDIOC_ENUMSTD = _IOWR('V', 25, v4l2_standard) +VIDIOC_ENUMINPUT = _IOWR('V', 26, v4l2_input) +VIDIOC_G_CTRL = _IOWR('V', 27, v4l2_control) +VIDIOC_S_CTRL = _IOWR('V', 28, v4l2_control) +VIDIOC_G_TUNER = _IOWR('V', 29, v4l2_tuner) +VIDIOC_S_TUNER = _IOW('V', 30, v4l2_tuner) +VIDIOC_G_AUDIO = _IOR('V', 33, v4l2_audio) +VIDIOC_S_AUDIO = _IOW('V', 34, v4l2_audio) +VIDIOC_QUERYCTRL = _IOWR('V', 36, v4l2_queryctrl) +VIDIOC_QUERYMENU = _IOWR('V', 37, v4l2_querymenu) +VIDIOC_G_INPUT = _IOR('V', 38, ctypes.c_int) +VIDIOC_S_INPUT = _IOWR('V', 39, ctypes.c_int) +VIDIOC_G_OUTPUT = _IOR('V', 46, ctypes.c_int) +VIDIOC_S_OUTPUT = _IOWR('V', 47, ctypes.c_int) +VIDIOC_ENUMOUTPUT = _IOWR('V', 48, v4l2_output) +VIDIOC_G_AUDOUT = _IOR('V', 49, v4l2_audioout) +VIDIOC_S_AUDOUT = _IOW('V', 50, v4l2_audioout) +VIDIOC_G_MODULATOR = _IOWR('V', 54, v4l2_modulator) +VIDIOC_S_MODULATOR = _IOW('V', 55, v4l2_modulator) +VIDIOC_G_FREQUENCY = _IOWR('V', 56, v4l2_frequency) +VIDIOC_S_FREQUENCY = _IOW('V', 57, v4l2_frequency) +VIDIOC_CROPCAP = _IOWR('V', 58, v4l2_cropcap) +VIDIOC_G_CROP = _IOWR('V', 59, v4l2_crop) +VIDIOC_S_CROP = _IOW('V', 60, v4l2_crop) +VIDIOC_G_JPEGCOMP = _IOR('V', 61, v4l2_jpegcompression) +VIDIOC_S_JPEGCOMP = _IOW('V', 62, v4l2_jpegcompression) +VIDIOC_QUERYSTD = _IOR('V', 63, v4l2_std_id) +VIDIOC_TRY_FMT = _IOWR('V', 64, v4l2_format) +VIDIOC_ENUMAUDIO = _IOWR('V', 65, v4l2_audio) +VIDIOC_ENUMAUDOUT = _IOWR('V', 66, v4l2_audioout) +VIDIOC_G_PRIORITY = _IOR('V', 67, v4l2_priority) +VIDIOC_S_PRIORITY = _IOW('V', 68, v4l2_priority) +VIDIOC_G_SLICED_VBI_CAP = _IOWR('V', 69, v4l2_sliced_vbi_cap) +VIDIOC_LOG_STATUS = _IO('V', 70) +VIDIOC_G_EXT_CTRLS = _IOWR('V', 71, v4l2_ext_controls) +VIDIOC_S_EXT_CTRLS = _IOWR('V', 72, v4l2_ext_controls) +VIDIOC_TRY_EXT_CTRLS = _IOWR('V', 73, v4l2_ext_controls) + +VIDIOC_ENUM_FRAMESIZES = _IOWR('V', 74, v4l2_frmsizeenum) +VIDIOC_ENUM_FRAMEINTERVALS = _IOWR('V', 75, v4l2_frmivalenum) +VIDIOC_G_ENC_INDEX = _IOR('V', 76, v4l2_enc_idx) +VIDIOC_ENCODER_CMD = _IOWR('V', 77, v4l2_encoder_cmd) +VIDIOC_TRY_ENCODER_CMD = _IOWR('V', 78, v4l2_encoder_cmd) + +VIDIOC_DBG_S_REGISTER = _IOW('V', 79, v4l2_dbg_register) +VIDIOC_DBG_G_REGISTER = _IOWR('V', 80, v4l2_dbg_register) + +VIDIOC_DBG_G_CHIP_IDENT = _IOWR('V', 81, v4l2_dbg_chip_ident) + +VIDIOC_S_HW_FREQ_SEEK = _IOW('V', 82, v4l2_hw_freq_seek) +VIDIOC_ENUM_DV_PRESETS = _IOWR('V', 83, v4l2_dv_enum_preset) +VIDIOC_S_DV_PRESET = _IOWR('V', 84, v4l2_dv_preset) +VIDIOC_G_DV_PRESET = _IOWR('V', 85, v4l2_dv_preset) +VIDIOC_QUERY_DV_PRESET = _IOR('V', 86, v4l2_dv_preset) +VIDIOC_S_DV_TIMINGS = _IOWR('V', 87, v4l2_dv_timings) +VIDIOC_G_DV_TIMINGS = _IOWR('V', 88, v4l2_dv_timings) + +VIDIOC_OVERLAY_OLD = _IOWR('V', 14, ctypes.c_int) +VIDIOC_S_PARM_OLD = _IOW('V', 22, v4l2_streamparm) +VIDIOC_S_CTRL_OLD = _IOW('V', 28, v4l2_control) +VIDIOC_G_AUDIO_OLD = _IOWR('V', 33, v4l2_audio) +VIDIOC_G_AUDOUT_OLD = _IOWR('V', 49, v4l2_audioout) +VIDIOC_CROPCAP_OLD = _IOR('V', 58, v4l2_cropcap) + +BASE_VIDIOC_PRIVATE = 192 diff --git a/src/py/camotics/.gitignore b/src/py/camotics/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/py/inevent/AbsAxisScaling.py b/src/py/inevent/AbsAxisScaling.py new file mode 100644 index 0000000..4b00721 --- /dev/null +++ b/src/py/inevent/AbsAxisScaling.py @@ -0,0 +1,106 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import array +import fcntl +import struct +from inevent import ioctl + + +def EVIOCGABS(axis): + return ioctl._IOR(ord('E'), 0x40 + axis, "ffffff") # get abs value/limits + + + +class AbsAxisScaling(object): + """ + Fetches and implements the EV_ABS axis scaling. + + The constructor fetches the scaling values from the given stream for the + given axis using an ioctl. + + There is a scale method, which scales a given value to the range -1..+1. + """ + + def __init__(self, stream, axis): + """ + Fetch the scale values for this stream and fill in the instance + variables accordingly. + """ + s = array.array("i", [1, 2, 3, 4, 5, 6]) + try: + fcntl.ioctl(stream.filehandle, EVIOCGABS(axis), s) + + except IOError: + self.value = self.minimum = self.maximum = self.fuzz = self.flat = \ + self.resolution = 1 + + else: + self.value, self.minimum, self.maximum, self.fuzz, self.flat, \ + self.resolution = struct.unpack("iiiiii", s) + + + def __str__(self): + return "Value {0} Min {1}, Max {2}, Fuzz {3}, Flat {4}, Res {5}".format( + self.value, self.minimum, self.maximum, self.fuzz, self.flat, + self.resolution) + + + def scale(self, value): + """ + scales the given value into the range -1..+1 + """ + return (float(value) - float(self.minimum)) / \ + float(self.maximum - self.minimum) * 2.0 - 1.0 diff --git a/src/py/inevent/Constants.py b/src/py/inevent/Constants.py new file mode 100644 index 0000000..0f3a2ed --- /dev/null +++ b/src/py/inevent/Constants.py @@ -0,0 +1,124 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +EV_SYN = 0x00 +EV_KEY = 0x01 +EV_REL = 0x02 +EV_ABS = 0x03 +EV_MSC = 0x04 +EV_SW = 0x05 +EV_LED = 0x11 +EV_SND = 0x12 +EV_REP = 0x14 +EV_FF = 0x15 +EV_PWR = 0x16 +EV_FF_STATUS = 0x17 + +ev_type_name = {} +ev_type_name[EV_SYN] = "SYN" +ev_type_name[EV_KEY] = "KEY" +ev_type_name[EV_REL] = "REL" +ev_type_name[EV_ABS] = "ABS" +ev_type_name[EV_MSC] = "MSC" +ev_type_name[EV_SW] = "SW" +ev_type_name[EV_LED] = "LED" +ev_type_name[EV_SND] = "SND" +ev_type_name[EV_REP] = "REP" +ev_type_name[EV_FF] = "FF" +ev_type_name[EV_PWR] = "PWR" +ev_type_name[EV_FF_STATUS] = "FF_STATUS" + +SYN_REPORT = 0 +SYN_CONFIG = 1 + +REL_X = 0x00 +REL_Y = 0x01 +REL_Z = 0x02 +REL_RX = 0x03 +REL_RY = 0x04 +REL_RZ = 0x05 +REL_HWHEEL = 0x06 +REL_DIAL = 0x07 +REL_WHEEL = 0x08 +REL_MISC = 0x09 +REL_MAX = 0x0f + +ABS_X = 0x00 +ABS_Y = 0x01 +ABS_Z = 0x02 +ABS_RX = 0x03 +ABS_RY = 0x04 +ABS_RZ = 0x05 +ABS_THROTTLE = 0x06 +ABS_RUDDER = 0x07 +ABS_WHEEL = 0x08 +ABS_GAS = 0x09 +ABS_BRAKE = 0x0a +ABS_HAT0X = 0x10 +ABS_HAT0Y = 0x11 +ABS_HAT1X = 0x12 +ABS_HAT1Y = 0x13 +ABS_HAT2X = 0x14 +ABS_HAT2Y = 0x15 +ABS_HAT3X = 0x16 +ABS_HAT3Y = 0x17 +ABS_PRESSURE = 0x18 +ABS_DISTANCE = 0x19 +ABS_TILT_X = 0x1a +ABS_TILT_Y = 0x1b +ABS_TOOL_WIDTH = 0x1c +ABS_VOLUME = 0x20 +ABS_MISC = 0x28 +ABS_MAX = 0x3f diff --git a/src/py/inevent/Event.py b/src/py/inevent/Event.py new file mode 100644 index 0000000..bf8f2e1 --- /dev/null +++ b/src/py/inevent/Event.py @@ -0,0 +1,142 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import struct + +from inevent.Constants import * + + +_format = 'llHHi' +size = struct.calcsize(_format) + + +class Event(object): + """ + A single event from the linux input event system. + + Events are tuples: (Time, Type, Code, Value) + In addition we remember the stream it came from. + + Externally, only the unhandled event handler gets passed the whole event, + but the SYN handler gets the code and value. (Also the keyboard handler, but + those are renamed to key and value.) + + This class is responsible for converting the Linux input event structure into + one of these objects and back again. + """ + def __init__(self, stream, time = None, type = None, code = None, + value = None): + """ + Create a new event. + + Generally all but the stream parameter are left out; we will want to + populate the object from a Linux input event using decode. + """ + self.stream = stream + self.time = time + self.type = type + self.code = code + self.value = value + + + def get_type_name(self): + if self.type not in ev_type_name: return '0x%x' % self.type + return ev_type_name[self.type] + + + def get_source(self): + return "%s[%d]" % (self.stream.devType, self.stream.devIndex) + + + def __str__(self): + """ + Uses the stream to give the device type and whether it is currently grabbed. + """ + grabbed = "grabbed" if self.stream.grabbed else "ungrabbed" + + return "Event %s %s @%f: %s 0x%x=0x%x" % ( + self.get_source(), grabbed, self.time, self.get_type_name(), self.code, + self.value) + + + def __repr__(self): + return "Event(%s, %f, 0x%x, 0x%x, 0x%x)" % ( + repr(self.stream), self.time, self.type, self.code, self.value) + + + def encode(self): + """ + Encode this event into a Linux input event structure. + + The output is packed into a string. It is unlikely that this function + will be required, but it might as well be here. + """ + tsec = int(self.time) + tfrac = int((self.time - tsec) * 1000000) + + return struct.pack(_format, tsec, tfrac, self.type, self.code, self.value) + + + def decode(self, s): + """ + Decode a Linux input event into the fields of this object. + + Arguments: + *s* + A binary structure packed into a string. + """ + tsec, tfrac, self.type, self.code, self.value = struct.unpack(_format, s) + + self.time = tsec + tfrac / 1000000.0 diff --git a/src/py/inevent/EventHandler.py b/src/py/inevent/EventHandler.py new file mode 100644 index 0000000..ccc9d7c --- /dev/null +++ b/src/py/inevent/EventHandler.py @@ -0,0 +1,146 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from inevent.Constants import * +from inevent.EventStream import EventStream + + +class EventHandler(object): + """ + A class to handle events. + + Four types of events are handled: REL (mouse movement), KEY (keybaord keys and + other device buttons), ABS (joysticks and gamepad analogue sticks) and SYN + (delimits simultaneous events such as mouse movements) + """ + def __init__(self): + self.buttons = dict() + + + def event(self, event, handler, name): + """ + Handles the given event. + + If the event is passed to a handler or otherwise handled then returns None, + else returns the event. All handlers are optional. + + All key events are handled by putting them in the self.buttons dict, and + optionally by calling the supplied handler. + + REL X, Y and wheel V and H events are all accumulated internally and + also optionally passed to the supplied handler. All these events are + handled. + + ABS X, Y, Z, RX, RY, RZ, Hat0X, Hat0Y are all accumulated internally and + also optionally passed to the supplied handler. Other ABS events are not + handled. + + All SYN events are passed to the supplied handler. + + There are several ABS events that we do not handle. In particular: + THROTTLE, RUDDER, WHEEL, GAS, BRAKE, HAT1, HAT2, HAT3, PRESSURE, + DISTANCE, TILT, TOOL_WIDTH. Implementing these is left as an exercise + for the interested reader. + + Likewise, since one handler is handling all events for all devices, we + may get the situation where two devices return the same button. The only + way to handle that would seem to be to have a key dict for every device, + which seems needlessly profligate for a situation that may never arise. + """ + + state = event.stream.state + + if event.type == EV_KEY: self.buttons[event.code] = event.value + elif event.type == EV_REL: state.rel[event.code] += event.value + elif event.type == EV_ABS: + state.abs[event.code] = event.stream.scale(event.code, event.value) + + if handler: handler.event(event, state, name) + + + def key_state(self, code): + """ + Returns the last event value for the given key code. + + Key names can be converted to key codes using codeOf[str]. + If the key is pressed the returned value will be 1 (pressed) or 2 (held). + If the key is not pressed, the returned value will be 0. + """ + return self.buttons.get(code, 0) + + + def clear_key(self, code): + """ + Clears the event value for the given key code. + + Key names can be converted to key codes using codeOf[str]. + This emulates a key-up but does not generate any events. + """ + self.buttons[code] = 0 + + + def get_keys(self): + """ + Returns the first of whichever keys have been pressed. + + Key names can be converted to key codes using codeOf[str]. + This emulates a key-up but does not generate any events. + """ + k_list = [] + + for k in self.buttons: + if self.buttons[k] != 0: k_list.append(k) + + return k_list diff --git a/src/py/inevent/EventState.py b/src/py/inevent/EventState.py new file mode 100644 index 0000000..d1443bf --- /dev/null +++ b/src/py/inevent/EventState.py @@ -0,0 +1,145 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from inevent.Constants import * + + +class EventState: + def __init__(self): + self.abs = [0.0] * ABS_MAX + self.rel = [0.0] * REL_MAX + + + def __str__(self): + return ("({:6.3f}, {:6.3f}, {:6.3f}) ".format(*self.get_joystick3d()) + + "({:6.3f}, {:6.3f}, {:6.3f}) ".format(*self.get_joystickR3d()) + + "({:2.0f}, {:2.0f}) ".format(*self.get_hat()) + + "({:0.2f}, {:0.2f}) ".format(*self.get_mouse()) + + "({:0.2f}, {:0.2f})".format(*self.get_wheel())) + + + def get_joystick(self): + """ + Returns the x,y coordinates for a joystick or left gamepad analogue stick. + + The values are returned as a tuple. All values are -1.0 to +1.0 with + 0.0 being centred. + """ + return self.abs[ABS_X], self.abs[ABS_Y] + + + def get_joystick3d(self): + """ + Returns the x,y,z coordinates for a joystick or left gamepad analogue stick + + The values are returned as a tuple. All values are -1.0 to +1.0 with + 0.0 being centred. + """ + return self.abs[ABS_X], self.abs[ABS_Y], self.abs[ABS_Z] + + + def get_joystickR(self): + """ + Returns the x,y coordinates for a right gamepad analogue stick. + + The values are returned as a tuple. For some odd reason, the gamepad + returns values in the Z axes of both joysticks, with y being the first. + + All values are -1.0 to +1.0 with 0.0 being centred. + """ + return self.abs[ABS_RZ], self.abs[ABS_Z] + + + def get_joystickR3d(self): + """ + Returns the x,y,z coordinates for a 2nd joystick control + + The values are returned as a tuple. All values are -1.0 to +1.0 with + 0.0 being centred. + """ + return self.abs[ABS_RX], self.abs[ABS_RY], self.abs[ABS_RZ] + + + def get_hat(self): + """ + Returns the x,y coordinates for a joystick hat or gamepad direction pad + + The values are returned as a tuple. All values are -1.0 to +1.0 with + 0.0 being centred. + """ + return self.abs[ABS_HAT0X], self.abs[ABS_HAT0Y] + + + def get_mouse(self): + return self.rel[REL_X], self.rel[REL_Y] + + + def get_wheel(self): + return self.rel[REL_WHEEL], self.rel[REL_HWHEEL] + + + def get_mouse_movement(self): + """ + Returns the accumulated REL (mouse or other relative device) movements + since the last call. + + The returned value is a tuple: (X, Y, WHEEL, H-WHEEL) + """ + ret = self.get_mouse() + self.get_wheel() + + self.rel[REL_X] = self.rel[REL_Y] = 0 + self.rel[REL_WHEEL] = self.rel[REL_HWHEEL] = 0 + + return ret diff --git a/src/py/inevent/EventStream.py b/src/py/inevent/EventStream.py new file mode 100644 index 0000000..aac648d --- /dev/null +++ b/src/py/inevent/EventStream.py @@ -0,0 +1,218 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import fcntl +import os +import select +import logging + +from inevent.Constants import * +from inevent import ioctl +from inevent.AbsAxisScaling import AbsAxisScaling +from inevent import Event +from inevent.EventState import EventState + + +log = logging.getLogger('inevent') + +EVIOCGRAB = ioctl._IOW(ord('E'), 0x90, "i") # Grab/Release device + + + +class EventStream(object): + """ + encapsulates the event* file handling + + Each device is represented by a file in /dev/input called eventN, where N is + a small number. (Actually, a keybaord can be represented by two such files.) + Instances of this class open one of these files and provide means to read + events from them. + + Class methods also exist to read from multiple files simultaneously, and + also to grab and ungrab all instances of a given type. + """ + axisX = 0 + axisY = 1 + axisZ = 2 + axisRX = 3 + axisRY = 4 + axisRZ = 5 + axisHat0X = 6 + axisHat0Y = 7 + axisHat1X = 8 + axisHat1Y = 9 + axisHat2X = 10 + axisHat2Y = 11 + axisHat3X = 12 + axisHat3Y = 13 + axisThrottle = 14 + axisRudder = 15 + axisWheel = 16 + axisGas = 17 + axisBrake = 18 + axisPressure = 19 + axisDistance = 20 + axisTiltX = 21 + axisTiltY = 22 + axisToolWidth = 23 + numAxes = 24 + + axisToEvent = [ + ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ, ABS_HAT0X, ABS_HAT0Y, + ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y, ABS_HAT3X, ABS_HAT3Y, + ABS_THROTTLE, ABS_RUDDER, ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_PRESSURE, + ABS_DISTANCE, ABS_TILT_X, ABS_TILT_Y, ABS_TOOL_WIDTH] + + + def __init__(self, devIndex, devType, devName): + """ + Opens the given /dev/input/event file and grabs it. + + Also adds it to a class-global list of all existing streams. + """ + self.devIndex = devIndex + self.devType = devType + self.devName = devName + self.filename = "/dev/input/event" + str(devIndex) + self.filehandle = os.open(self.filename, os.O_RDWR) + self.state = EventState() + self.grab(True) + self.absInfo = [None] * ABS_MAX + + if devType == "js": + for axis in range(ABS_MAX): + self.absInfo[axis] = AbsAxisScaling(self, axis) + + + def scale(self, axis, value): + """ + Scale the given value according to the given axis. + + acquire_abs_info must have been previously called to acquire the data to + do the scaling. + """ + assert axis < ABS_MAX, "Axis number out of range" + + if self.absInfo[axis]: return self.absInfo[axis].scale(value) + else: return value + + + def grab(self, grab = True): + """ + Grab (or release) exclusive access to all devices of the given type. + + The devices are grabbed if grab is True and released if grab is False. + + All devices are grabbed to begin with. We might want to ungrab the + keyboard for example to use it for text entry. While not grabbed, all + key-down and key-hold events are filtered out. + """ + fcntl.ioctl(self.filehandle, EVIOCGRAB, 1 if grab else 0) + self.grabbed = grab + + + def __iter__(self): + """s + Required to make this class an iterator + """ + return self + + + def next(self): return self.__next__() + + + def __next__(self): + """ + Returns the next waiting event. + + If no event is waiting, returns None. + """ + ready = select.select([self.filehandle], [], [], 0)[0] + if ready: return self.read() + + + def read(self): + """ + Read and return the next waiting event. + """ + try: + s = os.read(self.filehandle, Event.size) + if s: + event = Event.Event(self) + event.decode(s) + return event + + except Exception as e: + log.info('Reading event: %s' % e) + + + def __enter__(self): return self + + + def release(self): + "Ungrabs the file and closes it." + + try: + self.grab(False) + os.close(self.filehandle) + + except: + pass + + + def __exit__(self, type, value, traceback): + "Ungrabs the file and closes it." + + self.release() diff --git a/src/py/inevent/FindDevices.py b/src/py/inevent/FindDevices.py new file mode 100644 index 0000000..2f3970a --- /dev/null +++ b/src/py/inevent/FindDevices.py @@ -0,0 +1,245 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import re +import logging +from inevent.Constants import * + +log = logging.getLogger('inevent') + + +def test_bit(nlst, b): + index = b / 32 + bit = b % 32 + return index < len(nlst) and nlst[index] & (1 << bit) + + +def EvToStr(events): + s = [] + + if test_bit(events, EV_SYN): s.append("EV_SYN") + if test_bit(events, EV_KEY): s.append("EV_KEY") + if test_bit(events, EV_REL): s.append("EV_REL") + if test_bit(events, EV_ABS): s.append("EV_ABS") + if test_bit(events, EV_MSC): s.append("EV_MSC") + if test_bit(events, EV_LED): s.append("EV_LED") + if test_bit(events, EV_SND): s.append("EV_SND") + if test_bit(events, EV_REP): s.append("EV_REP") + if test_bit(events, EV_FF): s.append("EV_FF" ) + if test_bit(events, EV_PWR): s.append("EV_PWR") + if test_bit(events, EV_FF_STATUS): s.append("EV_FF_STATUS") + + return s + + +class DeviceCapabilities(object): + def __init__(self, firstLine, filehandle): + self.EV_SYNevents = [] + self.EV_KEYevents = [] + self.EV_RELevents = [] + self.EV_ABSevents = [] + self.EV_MSCevents = [] + self.EV_LEDevents = [] + self.EV_SNDevents = [] + self.EV_REPevents = [] + self.EV_FFevents = [] + self.EV_PWRevents = [] + self.EV_FF_STATUSevents = [] + self.eventTypes = [] + + match = re.search(".*Bus=([0-9A-Fa-f]+).*Vendor=([0-9A-Fa-f]+).*" + "Product=([0-9A-Fa-f]+).*Version=([0-9A-Fa-f]+).*", + firstLine) + + if not match: + log.warning("Do not understand device ID: %s", firstLine) + self.bus = 0 + self.vendor = 0 + self.product = 0 + self.version = 0 + + else: + self.bus = int(match.group(1), base = 16) + self.vendor = int(match.group(2), base = 16) + self.product = int(match.group(3), base = 16) + self.version = int(match.group(4), base = 16) + + for line in filehandle: + if not line.strip(): break + + if line[0] == "N": + match = re.search('Name="([^"]+)"', line) + if match: self.name = match.group(1) + else: self.name = "UNKNOWN" + + elif line[0] == "P": + match = re.search('Phys=(.+)', line) + if match: self.phys = match.group(1) + else: self.phys = "UNKNOWN" + + elif line[0] == "S": + match = re.search('Sysfs=(.+)', line) + if match: self.sysfs = match.group(1) + else: self.sysfs = "UNKNOWN" + + elif line[0] == "U": + match = re.search('Uniq=(.*)', line) + if match: self.uniq = match.group(1) + else: self.uniq = "UNKNOWN" + + elif line[0] == "H": + match = re.search('Handlers=(.+)', line) + if match: self.handlers = match.group(1).split() + else: self.handlers = [] + + elif line[:5] == "B: EV": + eventsNums = [int(x, base = 16) for x in line[6:].split()] + eventsNums.reverse() + self.eventTypes = eventsNums + + elif line[:6] == "B: KEY": + eventsNums = [int(x, base = 16) for x in line[7:].split()] + eventsNums.reverse() + self.EV_KEYevents = eventsNums + + elif line[:6] == "B: ABS": + eventsNums = [int(x, base = 16) for x in line[7:].split()] + eventsNums.reverse() + self.EV_ABSevents = eventsNums + + elif line[:6] == "B: MSC": + eventsNums = [int(x, base = 16) for x in line[7:].split()] + eventsNums.reverse() + self.EV_MSCevents = eventsNums + + elif line[:6] == "B: REL": + eventsNums = [int(x, base = 16) for x in line[7:].split()] + eventsNums.reverse() + self.EV_RELevents = eventsNums + + elif line[:6] == "B: LED": + eventsNums = [int(x, base = 16) for x in line[7:].split()] + eventsNums.reverse() + self.EV_LEDevents = eventsNums + + for handler in self.handlers: + if handler[:5] == "event": self.eventIndex = int(handler[5:]) + + self.isMouse = False + self.isKeyboard = False + self.isJoystick = False + + + def doesProduce(self, eventType, eventCode): + return test_bit(self.eventTypes, eventType) and ( + (eventType == EV_SYN and test_bit(self.EV_SYNevents, eventCode)) or + (eventType == EV_KEY and test_bit(self.EV_KEYevents, eventCode)) or + (eventType == EV_REL and test_bit(self.EV_RELevents, eventCode)) or + (eventType == EV_ABS and test_bit(self.EV_ABSevents, eventCode)) or + (eventType == EV_MSC and test_bit(self.EV_MSCevents, eventCode)) or + (eventType == EV_LED and test_bit(self.EV_LEDevents, eventCode)) or + (eventType == EV_SND and test_bit(self.EV_SNDevents, eventCode)) or + (eventType == EV_REP and test_bit(self.EV_REPevents, eventCode)) or + (eventType == EV_FF and test_bit(self.EV_FFevents, eventCode)) or + (eventType == EV_PWR and test_bit(self.EV_PWRevents, eventCode)) or + (eventType == EV_FF_STATUS and + test_bit(self.EV_FF_STATUSevents, eventCode))) + + + def __str__(self): + return ( + ("%s\n" + "Bus: %s Vendor: %s Product: %s Version: %s\n" + "Phys: %s\n" + "Sysfs: %s\n" + "Uniq: %s\n" + "Handlers: %s Event Index: %s\n" + "Keyboard: %s Mouse: %s Joystick: %s\n" + "Events: %s") % ( + self.name, self.bus, self.vendor, self.product, self.version, self.phys, + self.sysfs, self.uniq, self.handlers, self.eventIndex, self.isKeyboard, + self.isMouse, self.isJoystick, EvToStr(self.eventTypes))) + + +deviceCapabilities = [] + + +def get_devices(filename = "/proc/bus/input/devices"): + global deviceCapabilities + + with open("/proc/bus/input/devices", "r") as filehandle: + for line in filehandle: + if line[0] == "I": + deviceCapabilities.append(DeviceCapabilities(line, filehandle)) + + return deviceCapabilities + + +def print_devices(): + devs = get_devices() + + for dev in devs: + print(str(dev)) + print(" ABS: {}" + .format([x for x in range(64) if test_bit(dev.EV_ABSevents, x)])) + print(" REL: {}" + .format([x for x in range(64) if test_bit(dev.EV_RELevents, x)])) + print(" MSC: {}" + .format([x for x in range(64) if test_bit(dev.EV_MSCevents, x)])) + print(" KEY: {}" + .format([x for x in range(512) if test_bit(dev.EV_KEYevents, x)])) + print(" LED: {}" + .format([x for x in range(64) if test_bit(dev.EV_LEDevents, x)])) + print() diff --git a/src/py/inevent/InEvent.py b/src/py/inevent/InEvent.py new file mode 100644 index 0000000..a85ea44 --- /dev/null +++ b/src/py/inevent/InEvent.py @@ -0,0 +1,288 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import pyudev +import re +import select +import errno +import functools +import logging + +from inevent.EventHandler import EventHandler +from inevent import Keys +from inevent.Constants import * +from inevent.EventStream import EventStream + + +log = logging.getLogger('inevent') + +_KEYS = (k for k in vars(Keys) if not k.startswith('_')) +KEY_CODE = dict((k, getattr(Keys, k)) for k in _KEYS) +CODE_KEY = {} +for v in KEY_CODE: CODE_KEY[KEY_CODE[v]] = v + + +def key_to_code(key): + return KEY_CODE.get(str(key), -1) \ + if isinstance(key, str) else key + + +def code_to_key(code): return CODE_KEY.get(code, '') + + +class InEvent(object): + """Encapsulates the entire InEvent subsystem. + + This is generally all you need to import. + + On instantiation, we open all devices that are keyboards, mice or joysticks. + That means we might have two of one sort of another, and that might be a + problem, but it would be rather rare. + + There are several ABS (joystick, touch) events that we do not handle, + specifically THROTTLE, RUDDER, WHEEL, GAS, BRAKE, HAT1, HAT2, HAT3, PRESSURE, + DISTANCE, TILT, TOOL_WIDTH. Implementing these is left as an exercise + for the interested reader. Similarly, we make no attempt to handle + multi-touch. + + Handlers can be supplied, in which case they are called for each event, but + it isn't necessary; API exists for all the events. + + The handler signature is: + + def handler_func(event, state) + + where: + event: + An Event object describing the event. + + state: + An EventState object describing the current state. + + Use key_to_code() to convert from the name of a key to its code, + and code_to_key() to convert a code to a name. + + The keys are listed in inevent.Constants.py or /usr/include/linux/input.h + Note that the key names refer to a US keyboard. + """ + def __init__(self, ioloop, cb, types = 'kbd mouse js'.split()): + self.ioloop = ioloop + self.cb = cb + self.streams = [] + self.handler = EventHandler() + self.types = types + + self.udevCtx = pyudev.Context() + self.udevMon = pyudev.Monitor.from_netlink(self.udevCtx) + self.udevMon.filter_by(subsystem = 'input') + + devs = list(self.find_devices(types)) + for index, type, name in devs: + self.add_stream(index, type, name) + + self.udevMon.start() + ioloop.add_handler(self.udevMon.fileno(), self.udev_handler, ioloop.READ) + + + def get_dev(self, index): + return pyudev.Device.from_name(self.udevCtx, 'input', 'event%s' % index) + + + def get_dev_name(self, index): + try: + dev = self.get_dev(index) + return dev.parent.attributes.asstring('name').decode('utf-8') + except: pass + + + def find_devices(self, types): + """Finds the event indices of all devices of the specified types. + + A type is a string on the handlers line of /proc/bus/input/devices. + Keyboards use "kbd", mice use "mouse" and joysticks (and gamepads) use "js". + + Returns a list of integer indexes N, where /dev/input/eventN is the event + stream for each device. + + If butNot is given it holds a list of tuples which the returned values + should not match. + + All devices of each type are returned; if you have two mice, they will both + be used. + """ + with open("/proc/bus/input/devices", "r") as filehandle: + for line in filehandle: + if line[0] == "H": + for type in types: + if type in line: + match = re.search("event([0-9]+)", line) + index = match and match.group(1) + if index: + yield int(index), type, self.get_dev_name(index) + break + + + def process_udev_event(self): + action, device = self.udevMon.receive_device() + if device is None: return + + match = re.search(r"/dev/input/event([0-9]+)", str(device.device_node)) + devIndex = match and match.group(1) + if not devIndex: return + devIndex = int(devIndex) + + if action == 'add': + for index, devType, devName in self.find_devices(self.types): + if index == devIndex: + self.add_stream(devIndex, devType, devName) + break + + if action == 'remove': self.remove_stream(devIndex) + + + def stream_handler(self, fd, events): + for stream in self.streams: + if stream.filehandle == fd: + while True: + event = stream.next() + if event: self.handler.event(event, self.cb, stream.devName) + else: break + + + def udev_handler(self, fd, events): + self.process_udev_event() + + + def add_stream(self, devIndex, devType, devName): + try: + stream = EventStream(devIndex, devType, devName) + self.streams.append(stream) + + self.ioloop.add_handler(stream.filehandle, self.stream_handler, + self.ioloop.READ) + + log.info('Added %s[%d] %s', devType, devIndex, devName) + + except OSError as e: + log.warning('Failed to add %s[%d]: %s', devType, devIndex, e) + + + def remove_stream(self, devIndex): + for stream in self.streams: + if stream.devIndex == devIndex: + self.streams.remove(stream) + self.ioloop.remove_handler(stream.filehandle) + stream.release() + self.cb.clear() + + log.info('Removed %s[%d]', stream.devType, devIndex) + + + def key_state(self, key): + """ + Returns the state of the given key. + + The returned value will be 0 for key-up, or 1 for key-down. This method + returns a key-held(2) as 1 to aid in using the returned value as a + movement distance. + + This function accepts either the key code or the string name of the key. + It would be more efficient to look-up and store the code of + the key with KEY_CODE[], rather than using the string every time. (Which + involves a dict look-up keyed with a string for every key_state call, every + time around the loop.) + + Gamepad keys are: + Select = BTN_BASE3, Start = BTN_BASE4 + L1 = BTN_TOP R1 = BTN_BASE + L2 = BTN_PINKIE R2 = BTN_BASE2 + + The action buttons are: + BTN_THUMB + BTN_TRIGGER + BTN_TOP + BTN_THUMB2 + + Analogue Left Button = BTN_BASE5 + Analogue Right Button = BTN_BASE6 + + Some of those may clash with extended mouse buttons, so if you are using + both at once, you'll see some overlap. + + The direction pad is hat0 (see get_hat) + """ + return self.handler.key_state(key_to_code(key)) + + + def clear_key(self, key): + """ + Clears the state of the given key. + + Emulates a key-up, but does not call any handlers. + """ + return self.handler.clear_key(key_to_code(key)) + + + def get_keys(self): + return [code_to_key(k) for k in self.handler.get_keys()] + + + def release(self): + """ + Ungrabs all streams and closes all files. + + Only do this when you're finished with this object. You can't use it again. + """ + for s in self.streams: s.release() diff --git a/src/py/inevent/JogHandler.py b/src/py/inevent/JogHandler.py new file mode 100644 index 0000000..f5fad5b --- /dev/null +++ b/src/py/inevent/JogHandler.py @@ -0,0 +1,153 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import logging + +from inevent.Constants import * + + +log = logging.getLogger('inevent') +log.setLevel(logging.INFO) + + +def axes_to_string(axes): + s = '' + for axis in axes: + if s: s += ', ' + else: s = '(' + s += '{:6.3f}'.format(axis) + return s + ')' + + +def event_to_string(event, state): + s = '{} {}: '.format(event.get_source(), event.get_type_name()) + + if event.type == EV_ABS: + s += axes_to_string(state.get_joystick3d()) + ' ' + \ + axes_to_string(state.get_joystickR3d()) + ' ' + \ + '({:2.0f}, {:2.0f}) '.format(*state.get_hat()) + + if event.type == EV_REL: + s += '({:d}, {:d}) '.format(*state.get_mouse()) + \ + '({:d}, {:d})'.format(*state.get_wheel()) + + if event.type == EV_KEY: + state = 'pressed' if event.value else 'released' + s += '0x{:x} {}'.format(event.code, state) + + return s + + +class JogHandler: + def __init__(self, config): + self.config = config + self.reset() + + + def changed(self): + log.info(axes_to_string(self.axes) + ' x {:d}'.format(self.speed)) + + + def up(self): log.debug('up') + def down(self): log.debug('down') + def left(self): log.debug('left') + def right(self): log.debug('right') + + + def reset(self): + self.axes = [0.0, 0.0, 0.0, 0.0] + self.speed = 3 + self.vertical_lock = 0 + self.horizontal_lock = 0 + + + def clear(self): + self.reset() + self.changed() + + + def get_config(self, name): + if name in self.config: return self.config[name] + return self.config['default'] + + + def event(self, event, state, dev_name): + if event.type not in [EV_ABS, EV_REL, EV_KEY]: return + + config = self.get_config(dev_name) + changed = False + + # Process event + if event.type == EV_ABS and event.code in config['axes']: + pass + + elif event.type == EV_ABS and event.code in config['arrows']: + axis = config['arrows'].index(event.code) + + if event.value < 0: + if axis == 1: self.up() + else: self.left() + + elif 0 < event.value: + if axis == 1: self.down() + else: self.right() + + elif event.type == EV_KEY and event.code in config['speed']: + old_speed = self.speed + self.speed = config['speed'].index(event.code) + 1 + if self.speed != old_speed: changed = True + + elif event.type == EV_KEY and event.code in config['lock']: + index = config['lock'].index(event.code) + + self.horizontal_lock, self.vertical_lock = False, False + + if event.value: + if index == 0: self.horizontal_lock = True + if index == 1: self.vertical_lock = True + + log.debug(event_to_string(event, state)) + + # Update axes + old_axes = list(self.axes) + + for axis in range(4): + self.axes[axis] = event.stream.state.abs[config['axes'][axis]] + self.axes[axis] *= config['dir'][axis] + + if abs(self.axes[axis]) < config['deadband']: + self.axes[axis] = 0 + + if self.horizontal_lock and axis not in [0, 3]: + self.axes[axis] = 0 + + if self.vertical_lock and axis not in [1, 2]: + self.axes[axis] = 0 + + if old_axes != self.axes: changed = True + + if changed: self.changed() diff --git a/src/py/inevent/Keys.py b/src/py/inevent/Keys.py new file mode 100644 index 0000000..449aa97 --- /dev/null +++ b/src/py/inevent/Keys.py @@ -0,0 +1,445 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +KEY_ESC = 1 +KEY_1 = 2 +KEY_2 = 3 +KEY_3 = 4 +KEY_4 = 5 +KEY_5 = 6 +KEY_6 = 7 +KEY_7 = 8 +KEY_8 = 9 +KEY_9 = 10 +KEY_0 = 11 +KEY_MINUS = 12 +KEY_EQUAL = 13 +KEY_BACKSPACE = 14 +KEY_TAB = 15 +KEY_Q = 16 +KEY_W = 17 +KEY_E = 18 +KEY_R = 19 +KEY_T = 20 +KEY_Y = 21 +KEY_U = 22 +KEY_I = 23 +KEY_O = 24 +KEY_P = 25 +KEY_LEFTBRACE = 26 +KEY_RIGHTBRACE = 27 +KEY_ENTER = 28 +KEY_LEFTCTRL = 29 +KEY_A = 30 +KEY_S = 31 +KEY_D = 32 +KEY_F = 33 +KEY_G = 34 +KEY_H = 35 +KEY_J = 36 +KEY_K = 37 +KEY_L = 38 +KEY_SEMICOLON = 39 +KEY_APOSTROPHE = 40 +KEY_GRAVE = 41 +KEY_LEFTSHIFT = 42 +KEY_BACKSLASH = 43 +KEY_Z = 44 +KEY_X = 45 +KEY_C = 46 +KEY_V = 47 +KEY_B = 48 +KEY_N = 49 +KEY_M = 50 +KEY_COMMA = 51 +KEY_DOT = 52 +KEY_SLASH = 53 +KEY_RIGHTSHIFT = 54 +KEY_KPASTERISK = 55 +KEY_LEFTALT = 56 +KEY_SPACE = 57 +KEY_CAPSLOCK = 58 +KEY_F1 = 59 +KEY_F2 = 60 +KEY_F3 = 61 +KEY_F4 = 62 +KEY_F5 = 63 +KEY_F6 = 64 +KEY_F7 = 65 +KEY_F8 = 66 +KEY_F9 = 67 +KEY_F10 = 68 +KEY_NUMLOCK = 69 +KEY_SCROLLLOCK = 70 +KEY_KP7 = 71 +KEY_KP8 = 72 +KEY_KP9 = 73 +KEY_KPMINUS = 74 +KEY_KP4 = 75 +KEY_KP5 = 76 +KEY_KP6 = 77 +KEY_KPPLUS = 78 +KEY_KP1 = 79 +KEY_KP2 = 80 +KEY_KP3 = 81 +KEY_KP0 = 82 +KEY_KPDOT = 83 + +KEY_ZENKAKUHANKAKU = 85 +KEY_102ND = 86 +KEY_F11 = 87 +KEY_F12 = 88 +KEY_RO = 89 +KEY_KATAKANA = 90 +KEY_HIRAGANA = 91 +KEY_HENKAN = 92 +KEY_KATAKANAHIRAGANA = 93 +KEY_MUHENKAN = 94 +KEY_KPJPCOMMA = 95 +KEY_KPENTER = 96 +KEY_RIGHTCTRL = 97 +KEY_KPSLASH = 98 +KEY_SYSRQ = 99 +KEY_RIGHTALT = 100 +KEY_LINEFEED = 101 +KEY_HOME = 102 +KEY_UP = 103 +KEY_PAGEUP = 104 +KEY_LEFT = 105 +KEY_RIGHT = 106 +KEY_END = 107 +KEY_DOWN = 108 +KEY_PAGEDOWN = 109 +KEY_INSERT = 110 +KEY_DELETE = 111 +KEY_MACRO = 112 +KEY_MUTE = 113 +KEY_VOLUMEDOWN = 114 +KEY_VOLUMEUP = 115 +KEY_POWER = 116 +KEY_KPEQUAL = 117 +KEY_KPPLUSMINUS = 118 +KEY_PAUSE = 119 + +KEY_KPCOMMA = 121 +KEY_HANGUEL = 122 +KEY_HANJA = 123 +KEY_YEN = 124 +KEY_LEFTMETA = 125 +KEY_RIGHTMETA = 126 +KEY_COMPOSE = 127 + +KEY_STOP = 128 +KEY_AGAIN = 129 +KEY_PROPS = 130 +KEY_UNDO = 131 +KEY_FRONT = 132 +KEY_COPY = 133 +KEY_OPEN = 134 +KEY_PASTE = 135 +KEY_FIND = 136 +KEY_CUT = 137 +KEY_HELP = 138 +KEY_MENU = 139 +KEY_CALC = 140 +KEY_SETUP = 141 +KEY_SLEEP = 142 +KEY_WAKEUP = 143 +KEY_FILE = 144 +KEY_SENDFILE = 145 +KEY_DELETEFILE = 146 +KEY_XFER = 147 +KEY_PROG1 = 148 +KEY_PROG2 = 149 +KEY_WWW = 150 +KEY_MSDOS = 151 +KEY_COFFEE = 152 +KEY_DIRECTION = 153 +KEY_CYCLEWINDOWS = 154 +KEY_MAIL = 155 +KEY_BOOKMARKS = 156 +KEY_COMPUTER = 157 +KEY_BACK = 158 +KEY_FORWARD = 159 +KEY_CLOSECD = 160 +KEY_EJECTCD = 161 +KEY_EJECTCLOSECD = 162 +KEY_NEXTSONG = 163 +KEY_PLAYPAUSE = 164 +KEY_PREVIOUSSONG = 165 +KEY_STOPCD = 166 +KEY_RECORD = 167 +KEY_REWIND = 168 +KEY_PHONE = 169 +KEY_ISO = 170 +KEY_CONFIG = 171 +KEY_HOMEPAGE = 172 +KEY_REFRESH = 173 +KEY_EXIT = 174 +KEY_MOVE = 175 +KEY_EDIT = 176 +KEY_SCROLLUP = 177 +KEY_SCROLLDOWN = 178 +KEY_KPLEFTPAREN = 179 +KEY_KPRIGHTPAREN = 180 + +KEY_F13 = 183 +KEY_F14 = 184 +KEY_F15 = 185 +KEY_F16 = 186 +KEY_F17 = 187 +KEY_F18 = 188 +KEY_F19 = 189 +KEY_F20 = 190 +KEY_F21 = 191 +KEY_F22 = 192 +KEY_F23 = 193 +KEY_F24 = 194 + +KEY_PLAYCD = 200 +KEY_PAUSECD = 201 +KEY_PROG3 = 202 +KEY_PROG4 = 203 +KEY_SUSPEND = 205 +KEY_CLOSE = 206 +KEY_PLAY = 207 +KEY_FASTFORWARD = 208 +KEY_BASSBOOST = 209 +KEY_PRINT = 210 +KEY_HP = 211 +KEY_CAMERA = 212 +KEY_SOUND = 213 +KEY_QUESTION = 214 +KEY_EMAIL = 215 +KEY_CHAT = 216 +KEY_SEARCH = 217 +KEY_CONNECT = 218 +KEY_FINANCE = 219 +KEY_SPORT = 220 +KEY_SHOP = 221 +KEY_ALTERASE = 222 +KEY_CANCEL = 223 +KEY_BRIGHTNESSDOWN = 224 +KEY_BRIGHTNESSUP = 225 +KEY_MEDIA = 226 + +KEY_UNKNOWN = 240 + +BTN_MISC = 0x100 +BTN_0 = 0x100 +BTN_1 = 0x101 +BTN_2 = 0x102 +BTN_3 = 0x103 +BTN_4 = 0x104 +BTN_5 = 0x105 +BTN_6 = 0x106 +BTN_7 = 0x107 +BTN_8 = 0x108 +BTN_9 = 0x109 + +BTN_MOUSE = 0x110 +BTN_LEFT = 0x110 +BTN_RIGHT = 0x111 +BTN_MIDDLE = 0x112 +BTN_SIDE = 0x113 +BTN_EXTRA = 0x114 +BTN_FORWARD = 0x115 +BTN_BACK = 0x116 +BTN_TASK = 0x117 + +BTN_JOYSTICK = 0x120 +BTN_TRIGGER = 0x120 +BTN_THUMB = 0x121 +BTN_THUMB2 = 0x122 +BTN_TOP = 0x123 +BTN_TOP2 = 0x124 +BTN_PINKIE = 0x125 +BTN_BASE = 0x126 +BTN_BASE2 = 0x127 +BTN_BASE3 = 0x128 +BTN_BASE4 = 0x129 +BTN_BASE5 = 0x12a +BTN_BASE6 = 0x12b +BTN_DEAD = 0x12f + +BTN_GAMEPAD = 0x130 +BTN_A = 0x130 +BTN_B = 0x131 +BTN_C = 0x132 +BTN_X = 0x133 +BTN_Y = 0x134 +BTN_Z = 0x135 +BTN_TL = 0x136 +BTN_TR = 0x137 +BTN_TL2 = 0x138 +BTN_TR2 = 0x139 +BTN_SELECT = 0x13a +BTN_START = 0x13b +BTN_MODE = 0x13c +BTN_THUMBL = 0x13d +BTN_THUMBR = 0x13e + +BTN_DIGI = 0x140 +BTN_TOOL_PEN = 0x140 +BTN_TOOL_RUBBER = 0x141 +BTN_TOOL_BRUSH = 0x142 +BTN_TOOL_PENCIL = 0x143 +BTN_TOOL_AIRBRUSH = 0x144 +BTN_TOOL_FINGER = 0x145 +BTN_TOOL_MOUSE = 0x146 +BTN_TOOL_LENS = 0x147 +BTN_TOUCH = 0x14a +BTN_STYLUS = 0x14b +BTN_STYLUS2 = 0x14c +BTN_TOOL_DOUBLETAP = 0x14d +BTN_TOOL_TRIPLETAP = 0x14e + +BTN_WHEEL = 0x150 +BTN_GEAR_DOWN = 0x150 +BTN_GEAR_UP = 0x151 + +KEY_OK = 0x160 +KEY_SELECT = 0x161 +KEY_GOTO = 0x162 +KEY_CLEAR = 0x163 +KEY_POWER2 = 0x164 +KEY_OPTION = 0x165 +KEY_INFO = 0x166 +KEY_TIME = 0x167 +KEY_VENDOR = 0x168 +KEY_ARCHIVE = 0x169 +KEY_PROGRAM = 0x16a +KEY_CHANNEL = 0x16b +KEY_FAVORITES = 0x16c +KEY_EPG = 0x16d +KEY_PVR = 0x16e +KEY_MHP = 0x16f +KEY_LANGUAGE = 0x170 +KEY_TITLE = 0x171 +KEY_SUBTITLE = 0x172 +KEY_ANGLE = 0x173 +KEY_ZOOM = 0x174 +KEY_MODE = 0x175 +KEY_KEYBOARD = 0x176 +KEY_SCREEN = 0x177 +KEY_PC = 0x178 +KEY_TV = 0x179 +KEY_TV2 = 0x17a +KEY_VCR = 0x17b +KEY_VCR2 = 0x17c +KEY_SAT = 0x17d +KEY_SAT2 = 0x17e +KEY_CD = 0x17f +KEY_TAPE = 0x180 +KEY_RADIO = 0x181 +KEY_TUNER = 0x182 +KEY_PLAYER = 0x183 +KEY_TEXT = 0x184 +KEY_DVD = 0x185 +KEY_AUX = 0x186 +KEY_MP3 = 0x187 +KEY_AUDIO = 0x188 +KEY_VIDEO = 0x189 +KEY_DIRECTORY = 0x18a +KEY_LIST = 0x18b +KEY_MEMO = 0x18c +KEY_CALENDAR = 0x18d +KEY_RED = 0x18e +KEY_GREEN = 0x18f +KEY_YELLOW = 0x190 +KEY_BLUE = 0x191 +KEY_CHANNELUP = 0x192 +KEY_CHANNELDOWN = 0x193 +KEY_FIRST = 0x194 +KEY_LAST = 0x195 +KEY_AB = 0x196 +KEY_NEXT = 0x197 +KEY_RESTART = 0x198 +KEY_SLOW = 0x199 +KEY_SHUFFLE = 0x19a +KEY_BREAK = 0x19b +KEY_PREVIOUS = 0x19c +KEY_DIGITS = 0x19d +KEY_TEEN = 0x19e +KEY_TWEN = 0x19f + +KEY_DEL_EOL = 0x1c0 +KEY_DEL_EOS = 0x1c1 +KEY_INS_LINE = 0x1c2 +KEY_DEL_LINE = 0x1c3 + +KEY_FN = 0x1d0 +KEY_FN_ESC = 0x1d1 +KEY_FN_F1 = 0x1d2 +KEY_FN_F2 = 0x1d3 +KEY_FN_F3 = 0x1d4 +KEY_FN_F4 = 0x1d5 +KEY_FN_F5 = 0x1d6 +KEY_FN_F6 = 0x1d7 +KEY_FN_F7 = 0x1d8 +KEY_FN_F8 = 0x1d9 +KEY_FN_F9 = 0x1da +KEY_FN_F10 = 0x1db +KEY_FN_F11 = 0x1dc +KEY_FN_F12 = 0x1dd +KEY_FN_1 = 0x1de +KEY_FN_2 = 0x1df +KEY_FN_D = 0x1e0 +KEY_FN_E = 0x1e1 +KEY_FN_F = 0x1e2 +KEY_FN_S = 0x1e3 +KEY_FN_B = 0x1e4 + +KEY_MAX = 0x1ff diff --git a/src/py/inevent/__init__.py b/src/py/inevent/__init__.py new file mode 100644 index 0000000..5e6780d --- /dev/null +++ b/src/py/inevent/__init__.py @@ -0,0 +1,57 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from .InEvent import InEvent +from .JogHandler import JogHandler diff --git a/src/py/inevent/ioctl.py b/src/py/inevent/ioctl.py new file mode 100644 index 0000000..652ef68 --- /dev/null +++ b/src/py/inevent/ioctl.py @@ -0,0 +1,128 @@ +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +# The inevent Python module was adapted from pi3d.event from the pi3d +# project. +# +# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC. +# Copyright (c) 2015, Tim Skillman. +# Copyright (c) 2015, Paddy Gaunt. +# Copyright (c) 2015, Tom Ritchford. +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# IOCTL macros +# +# ioctl command encoding: 32 bits total, command in lower 16 bits, +# size of the parameter structure in the lower 14 bits of the +# upper 16 bits. +# +# Encoding the size of the parameter structure in the ioctl request +# is useful for catching programs compiled with old versions +# and to avoid overwriting user space outside the user buffer area. +# The highest 2 bits are reserved for indicating the ``access mode''. +# NOTE: This limits the max parameter size to 16kB - 1 +# +# The following is for compatibility across the various Linux +# platforms. The generic ioctl numbering scheme doesn't really enforce +# a type field. De facto, however, the top 8 bits of the lower 16 +# bits are indeed used as a type field, so we might just as well make +# this explicit here. + +import struct + + +sizeof = struct.calcsize + +_IOC_NRBITS = 8 +_IOC_TYPEBITS = 8 +_IOC_SIZEBITS = 14 +_IOC_DIRBITS = 2 + +_IOC_NRMASK = (1 << _IOC_NRBITS) - 1 +_IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1 +_IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1 +_IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1 + +_IOC_NRSHIFT = 0 +_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS +_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS +_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS + +_IOC_NONE = 0 +_IOC_WRITE = 1 +_IOC_READ = 2 +_IOC_RW = _IOC_READ | _IOC_WRITE + + +def _IOC(dir, type, nr, size): + return int( + (dir << _IOC_DIRSHIFT) | + (type << _IOC_TYPESHIFT) | + (nr << _IOC_NRSHIFT) | + (size << _IOC_SIZESHIFT)) + +# encode ioctl numbers +def _IO(type, nr): return _IOC(_IOC_NONE, type, nr, 0) +def _IOR(type, nr, fmt): return _IOC(_IOC_READ, type, nr, sizeof(fmt)) +def _IOW(type, nr, fmt): return _IOC(_IOC_WRITE, type, nr, sizeof(fmt)) +def _IOWR(type, nr, fmt): return _IOC(_IOC_RW, type, nr, sizeof(fmt)) +def _IOR_BAD(type, nr, fmt): return _IOC(_IOC_READ, type, nr, sizeof(fmt)) +def _IOW_BAD(type, nr, fmt): return _IOC(_IOC_WRITE, type, nr, sizeof(fmt)) +def _IOWR_BAD(type, nr, fmt): return _IOC(_IOC_RW, type, nr, sizeof(fmt)) + +# decode ioctl numbers +def _IOC_DIR(nr): return (nr >> _IOC_DIRSHIFT) & _IOC_DIRMASK +def _IOC_TYPE(nr): return (nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK +def _IOC_NR(nr): return (nr >> _IOC_NRSHIFT) & _IOC_NRMASK +def _IOC_SIZE(nr): return (nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK + +# for drivers/sound files +IOC_IN = _IOC_WRITE << _IOC_DIRSHIFT +IOC_OUT = _IOC_READ << _IOC_DIRSHIFT +IOC_INOUT = _IOC_RW << _IOC_DIRSHIFT +IOCSIZE_MASK = _IOC_SIZEMASK << _IOC_SIZESHIFT +IOCSIZE_SHIFT = _IOC_SIZESHIFT + diff --git a/src/py/lcd/__init__.py b/src/py/lcd/__init__.py new file mode 100644 index 0000000..238a6e1 --- /dev/null +++ b/src/py/lcd/__init__.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 + +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import time +import logging + + +log = logging.getLogger('LCD') + + +# Control flags +REG_SELECT_BIT = 1 << 0 +READ_BIT = 1 << 1 +ENABLE_BIT = 1 << 2 +BACKLIGHT_BIT = 1 << 3 + +# Commands +LCD_CLEAR_DISPLAY = 1 << 0 +LCD_RETURN_HOME = 1 << 1 +LCD_ENTRY_MODE_SET = 1 << 2 +LCD_DISPLAY_CONTROL = 1 << 3 +LCD_CURSOR_SHIFT = 1 << 4 +LCD_FUNCTION_SET = 1 << 5 +LCD_SET_CGRAM_ADDR = 1 << 6 +LCD_SET_DDRAM_ADDR = 1 << 7 + +# Entry Mode Set flags +LCD_ENTRY_SHIFT_DISPLAY = 1 << 0 +LCD_ENTRY_SHIFT_INC = 1 << 1 +LCD_ENTRY_SHIFT_DEC = 0 << 1 + +# Display Control flags +LCD_BLINK_ON = 1 << 0 +LCD_BLINK_OFF = 0 << 0 +LCD_CURSOR_ON = 1 << 1 +LCD_CURSOR_OFF = 0 << 1 +LCD_DISPLAY_ON = 1 << 2 +LCD_DISPLAY_OFF = 0 << 2 + +# Cursor Shift flags +LCD_SHIFT_RIGHT = 1 << 2 +LCD_SHIFT_LEFT = 0 << 2 +LCD_SHIFT_DISPLAY = 1 << 3 +LCD_SHIFT_CURSOR = 0 << 3 + +# Function Set flags +LCD_5x11_DOTS = 1 << 2 +LCD_5x8_DOTS = 0 << 2 +LCD_2_LINE = 1 << 3 +LCD_1_LINE = 0 << 3 +LCD_8_BIT_MODE = 1 << 4 +LCD_4_BIT_MODE = 0 << 4 + +# Text justification flags +JUSTIFY_LEFT = 0 +JUSTIFY_RIGHT = 1 +JUSTIFY_CENTER = 2 + + + +class LCD: + def __init__(self, i2c, addr, height = 4, width = 20): + self.addr = addr + self.height = height + self.width = width + self.i2c = i2c + self.backlight = True + + self.reset() + + + def reset(self): + self.clear() + time.sleep(0.050) + self.write_nibble(3 << 4) # Home + time.sleep(0.050) + self.write_nibble(3 << 4) # Home + time.sleep(0.050) + self.write_nibble(3 << 4) # Home + self.write_nibble(2 << 4) # 4-bit + + self.write(LCD_FUNCTION_SET | LCD_2_LINE | LCD_5x8_DOTS | + LCD_4_BIT_MODE) + self.write(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON) + self.write(LCD_ENTRY_MODE_SET | LCD_ENTRY_SHIFT_INC) + + + def write_i2c(self, data): + if self.backlight: data |= BACKLIGHT_BIT + + self.i2c.write(self.addr, data) + time.sleep(0.0001) + + + # Write half of a command to LCD + def write_nibble(self, data): + self.write_i2c(data) + + # Strobe + self.write_i2c(data | ENABLE_BIT) + time.sleep(0.0005) + + self.write_i2c(data & ~ENABLE_BIT) + time.sleep(0.0001) + + + # Write an 8-bit command to LCD + def write(self, cmd, flags = 0): + self.write_nibble(flags | (cmd & 0xf0)) + self.write_nibble(flags | ((cmd << 4) & 0xf0)) + + + def set_cursor(self, on, blink): + data = LCD_DISPLAY_CONTROL + + if on: data |= LCD_CURSOR_ON + if blink: data |= LCD_BLINK_ON + + self.write(data) + + + def set_backlight(self, enable): + self.backlight = enable + self.write_i2c(0) + + + def program_char(self, addr, data): + if addr < 0 or 8 <= addr: return + + self.write(LCD_SET_CGRAM_ADDR | (addr << 3)) + for x in data: + self.write(x, REG_SELECT_BIT) + + + def goto(self, x, y): + if x < 0 or self.width <= x or y < 0 or self.height <= y: return + self.write(LCD_SET_DDRAM_ADDR | (0, 64, 20, 84)[y] + int(x)) + + + def put_char(self, c): + self.write(ord(c), REG_SELECT_BIT) + + + def text(self, msg, x = None, y = None): + if x is not None and y is not None: self.goto(x, y) + + for c in msg: self.put_char(c) + + + def display(self, line, msg, justify = JUSTIFY_LEFT): + if justify == JUSTIFY_RIGHT: x = self.width - len(msg) + elif justify == JUSTIFY_CENTER: x = (self.width - len(msg)) / 2 + else: x = 0 + + if x < 0: x = 0 + + self.text(msg, x, line) + + + def shift(self, count = 1, right = True, display = True): + cmd = LCD_CURSOR_SHIFT + if right: cmd |= LCD_SHIFT_RIGHT + if display: cmd |= LCD_SHIFT_DISPLAY + + for i in range(count): self.write(cmd) + + + # Clear LCD and move cursor home + def clear(self): + self.write(LCD_CLEAR_DISPLAY) + self.write(LCD_RETURN_HOME) + + + +if __name__ == "__main__": + lcd = LCD(1, 0x27) + + lcd.clear() + + lcd.program_char(0, (0b11011, + 0b11011, + 0b00000, + 0b01100, + 0b01100, + 0b00000, + 0b11011, + 0b11011)) + + lcd.program_char(1, (0b11000, + 0b01100, + 0b00110, + 0b00011, + 0b00011, + 0b00110, + 0b01100, + 0b11000)) + + lcd.program_char(2, (0b00011, + 0b00110, + 0b01100, + 0b11000, + 0b11000, + 0b01100, + 0b00110, + 0b00011)) + + lcd.display(0, '\0' * lcd.width) + lcd.display(1, 'Hello world!', JUSTIFY_CENTER) + lcd.display(2, '\1\2' * (lcd.width / 2)) + lcd.display(3, '12345678901234567890') diff --git a/src/py/lcd/splash.py b/src/py/lcd/splash.py new file mode 100644 index 0000000..203f60a --- /dev/null +++ b/src/py/lcd/splash.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software 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 # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import lcd + +if __name__ == "__main__": + screen = lcd.LCD(1, 0x27) + + screen.clear() + screen.display(0, 'Buildbotics', lcd.JUSTIFY_CENTER) + screen.display(1, 'Controller', lcd.JUSTIFY_CENTER) + screen.display(3, 'Booting...', lcd.JUSTIFY_CENTER) diff --git a/src/resources/buildbotics.nc b/src/resources/buildbotics.nc new file mode 100644 index 0000000..8c5ddb0 --- /dev/null +++ b/src/resources/buildbotics.nc @@ -0,0 +1,403 @@ +G21 +(File: 'buildbotics_logo.tpl') +G0 Z3 +F1600 +M3 S10000 +M6 T2 +G0 X59.25 Y5.85 +G1 Z-1.5 +G1 X61.68 Y6.7 +G1 X63.86 Y8.07 +G1 X65.68 Y9.89 +G1 X67.05 Y12.07 +G1 X67.9 Y14.5 +G1 X68.2 Y17.09 +G1 Y56.6 +G1 X67.73 Y59.04 +G1 X50.8 +G1 Y34.9 +G1 X50.65 Y34.55 +G1 X50.3 Y34.4 +G1 X23.46 +G1 X23.1 Y34.55 +G1 X22.96 Y34.9 +G1 X22.98 Y49.88 +G1 X22.96 Y59.05 +G1 X22.41 +G1 X19.26 +G1 X6.04 +G1 X5.56 Y56.53 +G1 Y17.09 +G1 X5.85 Y14.5 +G1 X6.7 Y12.07 +G1 X8.07 Y9.89 +G1 X9.89 Y8.07 +G1 X12.07 Y6.7 +G1 X14.5 Y5.85 +G1 X17.09 Y5.56 +G1 X56.67 +G1 X59.25 Y5.85 +G0 Z3 +G0 X64.26 Y64.72 +G1 Z-1.5 +G1 X61.78 Y66.52 +G1 X58.91 Y67.68 +G1 X56.54 Y68.08 +G1 X17.22 +G1 X14.84 Y67.68 +G1 X11.97 Y66.52 +G1 X9.49 Y64.72 +G1 X8.08 Y63.16 +G1 X27.35 +G1 X27.89 Y63.45 +G1 X27.96 Y63.48 +G1 X31.48 Y64.75 +G1 X31.52 Y64.76 +G1 X31.56 Y64.77 +G1 X35.19 Y65.41 +G1 X35.26 +G1 X35.97 Y65.44 +G1 X36.04 Y65.45 +G1 X36.07 +G1 X36.82 Y65.44 +G1 X36.83 +G1 X36.89 +G1 X36.95 +G1 X36.97 +G1 X37.72 Y65.43 +G1 X37.74 +G1 X37.8 +G1 X37.81 +G1 X37.88 +G1 X37.89 +G1 X38.65 Y65.38 +G1 X38.68 +G1 X38.75 Y65.37 +G1 X39.38 Y65.32 +G1 X39.44 Y65.31 +G1 X42.68 Y64.64 +G1 X42.76 Y64.62 +G1 X45.87 Y63.44 +G1 X45.93 Y63.41 +G1 X46.4 Y63.16 +G1 X65.67 +G1 X64.26 Y64.72 +G0 Z3 +G0 X36.88 Y9.4 +G1 Z-1.5 +G1 X37.31 Y9.64 +G1 X39.58 Y13.48 +G1 X39.63 Y13.6 +G1 X39.65 Y13.73 +G1 Y27.54 +G1 X41.67 +G1 Y25.39 +G1 X41.75 Y25.12 +G1 X41.97 Y24.93 +G1 X46.41 Y22.92 +G1 Y19.97 +G1 X45.44 +G1 X45.08 Y19.82 +G1 X44.94 Y19.47 +G1 Y13.73 +G1 X45.08 Y13.38 +G1 X45.44 Y13.23 +G1 X49.94 Y13.24 +G1 X50.29 Y13.39 +G1 X50.44 Y13.74 +G1 Y19.47 +G1 X50.29 Y19.83 +G1 X49.93 Y19.97 +G1 X48.92 +G1 Y23.61 +G1 X48.84 Y23.88 +G1 X48.63 Y24.06 +G1 X44.19 Y26.12 +G1 Y27.54 +G1 X49.22 +G1 X49.33 Y27.56 +G1 X49.44 Y27.6 +G1 X50.13 Y27.94 +G1 X50.25 Y28.02 +G1 X50.34 Y28.13 +G1 X50.73 Y28.77 +G1 X50.78 Y28.89 +G1 X50.8 Y29.03 +G1 Y33.05 +G1 Y34.25 +G1 Y34.65 +G1 X50.66 Y35.01 +G1 X50.3 Y35.15 +G1 X23.46 +G1 X23.1 Y35.01 +G1 X22.96 Y34.65 +G1 Y29.07 +G1 X22.97 Y28.94 +G1 X23.02 Y28.82 +G1 X23.4 Y28.17 +G1 X23.49 Y28.06 +G1 X23.6 Y27.98 +G1 X24.29 Y27.6 +G1 X24.4 Y27.56 +G1 X24.52 Y27.54 +G1 X25.55 +G1 Y26.4 +G1 X23.4 Y25.52 +G1 X23.17 Y25.33 +G1 X23.09 Y25.06 +G1 Y17.54 +G1 X23.23 Y17.19 +G1 X23.59 Y17.04 +G1 X24.6 +G1 Y10.36 +G1 X24.62 Y10.23 +G1 X24.66 Y10.11 +G1 X24.8 Y9.88 +G1 X24.88 Y9.77 +G1 X24.99 Y9.68 +G1 X25.25 Y9.54 +G1 X25.37 Y9.5 +G1 X25.49 Y9.48 +G1 X26.53 +G1 X26.65 Y9.49 +G1 X26.76 Y9.54 +G1 X27.01 Y9.66 +G1 X27.12 Y9.74 +G1 X27.21 Y9.85 +G1 X27.35 Y10.09 +G1 X27.41 Y10.22 +G1 X27.43 Y10.35 +G1 Y10.43 +G1 X27.47 Y17.04 +G1 X28.57 +G1 X28.92 Y17.19 +G1 X29.07 Y17.54 +G1 Y24.64 +G1 X30.72 Y25.3 +G1 X30.95 Y25.49 +G1 X31.03 Y25.77 +G1 X31.02 Y27.54 +G1 X34.03 +G1 Y13.73 +G1 X34.05 Y13.59 +G1 X34.1 Y13.47 +G1 X36.45 Y9.64 +G1 X36.88 Y9.4 +G0 Z3 +G0 X49.94 Y10.82 +G1 Z-1.5 +G1 X50.29 Y10.97 +G1 X50.44 Y11.32 +G1 Y13.34 +G1 X50.29 Y13.69 +G1 X49.94 Y13.84 +G1 X45.44 +G1 X45.08 Y13.69 +G1 X44.94 Y13.34 +G1 Y11.32 +G1 X45.08 Y10.97 +G1 X45.44 Y10.82 +G1 X49.94 +G0 Z3 +G0 X48.46 Y9.7 +G1 Z-1.5 +G1 X48.59 Y9.72 +G1 X48.71 Y9.77 +G1 X50.03 Y10.53 +G1 X50.21 Y10.71 +G1 X50.28 Y10.96 +G1 X50.14 Y11.31 +G1 X49.78 Y11.46 +G1 X45.62 +G1 X45.14 Y11.09 +G1 X45.37 Y10.53 +G1 X46.69 Y9.77 +G1 X46.81 Y9.72 +G1 X46.94 Y9.7 +G1 X48.46 +G0 Z3 +G0 X50.3 Y34.4 +G1 Z-1.5 +G1 X50.66 Y34.55 +G1 X50.8 Y34.9 +G1 Y49.88 +G1 X50.79 Y59.52 +G1 X50.76 Y59.69 +G1 X50.67 Y59.84 +G1 X50.09 Y60.52 +G1 X50.06 Y60.55 +G1 X50.02 Y60.58 +G1 X47.89 Y62.26 +G1 X47.85 Y62.28 +G1 X47.81 Y62.31 +G1 X44.46 Y64.03 +G1 X44.41 Y64.05 +G1 X44.37 Y64.06 +G1 X40.69 Y65.1 +G1 X40.62 Y65.12 +G1 X37.86 Y65.45 +G1 X37.8 Y65.46 +G1 X36.89 Y65.44 +G1 X36.83 +G1 X36.82 +G1 X36.07 Y65.45 +G1 X36.04 Y65.44 +G1 X35.97 +G1 X35.1 Y65.41 +G1 X35.04 Y65.4 +G1 X32.44 Y64.99 +G1 X32.37 Y64.97 +G1 X28.94 Y63.9 +G1 X28.89 Y63.88 +G1 X28.85 Y63.86 +G1 X25.74 Y62.2 +G1 X25.7 Y62.18 +G1 X25.66 Y62.15 +G1 X23.68 Y60.56 +G1 X23.65 Y60.53 +G1 X23.62 Y60.5 +G1 X23.08 Y59.87 +G1 X22.99 Y59.71 +G1 X22.96 Y59.54 +G1 X22.98 Y49.88 +G1 X22.96 Y34.9 +G1 X23.1 Y34.55 +G1 X23.46 Y34.4 +G1 X50.3 +G0 Z3 +G0 X55.2 Y43.67 +G1 Z-1.5 +G1 Y51.34 +G1 X55.11 Y51.94 +G1 X54.83 Y52.88 +G1 X54.39 Y53.88 +G1 X53.8 Y54.85 +G1 X53.09 Y55.74 +G1 X52.28 Y56.47 +G1 X51.41 Y56.98 +G1 X51.07 Y57.09 +G1 Y43.67 +G1 X55.2 +G0 Z3 +G0 X22.69 Y43.63 +G1 Z-1.5 +G1 Y57.09 +G1 X22.35 Y56.98 +G1 X21.47 Y56.47 +G1 X20.67 Y55.74 +G1 X19.95 Y54.85 +G1 X19.36 Y53.88 +G1 X18.92 Y52.88 +G1 X18.64 Y51.94 +G1 X18.55 Y51.34 +G1 Y43.63 +G1 X22.69 +G0 Z3 +G0 X28.55 Y35.84 +G1 Z-0.99 +G1 X30.11 Y36.15 +G1 X31.43 Y37.03 +G1 X32.32 Y38.35 +G1 X32.63 Y39.91 +G1 X32.32 Y41.47 +G1 X31.43 Y42.79 +G1 X30.11 Y43.68 +G1 X28.55 Y43.99 +G1 X26.99 Y43.68 +G1 X25.67 Y42.79 +G1 X24.79 Y41.47 +G1 X24.48 Y39.91 +G1 X24.79 Y38.35 +G1 X25.67 Y37.03 +G1 X26.99 Y36.15 +G1 X28.55 Y35.84 +G0 Z3 +G0 X45.33 Y35.93 +G1 Z-0.99 +G1 X46.88 Y36.24 +G1 X48.21 Y37.12 +G1 X49.09 Y38.45 +G1 X49.4 Y40 +G1 X49.09 Y41.56 +G1 X48.21 Y42.88 +G1 X46.88 Y43.77 +G1 X45.33 Y44.08 +G1 X43.77 Y43.77 +G1 X42.45 Y42.88 +G1 X41.56 Y41.56 +G1 X41.25 Y40 +G1 X41.56 Y38.45 +G1 X42.45 Y37.12 +G1 X43.77 Y36.24 +G1 X45.33 Y35.93 +G0 Z3 +G0 X45.2 Y39.12 +G1 Z-0.99 +G1 X45.7 Y39.19 +G1 X46.07 Y39.52 +G1 X46.22 Y40 +G1 X46.07 Y40.49 +G1 X45.7 Y40.81 +G1 X45.2 Y40.89 +G1 X44.74 Y40.68 +G1 X44.47 Y40.26 +G1 Y39.75 +G1 X44.74 Y39.33 +G1 X45.2 Y39.12 +G0 Z3 +G0 X28.43 Y39.03 +G1 Z-0.99 +G1 X28.92 Y39.1 +G1 X29.3 Y39.43 +G1 X29.44 Y39.91 +G1 X29.3 Y40.4 +G1 X28.92 Y40.72 +G1 X28.43 Y40.8 +G1 X27.97 Y40.59 +G1 X27.7 Y40.16 +G1 Y39.66 +G1 X27.97 Y39.24 +G1 X28.43 Y39.03 +G0 Z3 +G0 X55.76 Y0 +G1 Z-1.5 +G1 X59.27 Y0.35 +G1 X62.65 Y1.37 +G1 X65.76 Y3.03 +G1 X68.49 Y5.27 +G1 X70.73 Y8 +G1 X72.39 Y11.11 +G1 X73.42 Y14.49 +G1 X73.76 Y18 +G1 Y55.69 +G1 X73.42 Y59.2 +G1 X72.39 Y62.57 +G1 X70.73 Y65.69 +G1 X68.49 Y68.41 +G1 X65.76 Y70.65 +G1 X62.65 Y72.31 +G1 X59.27 Y73.34 +G1 X55.76 Y73.69 +G1 X18 +G1 X14.49 Y73.34 +G1 X11.11 Y72.31 +G1 X8 Y70.65 +G1 X5.27 Y68.41 +G1 X3.03 Y65.69 +G1 X1.37 Y62.57 +G1 X0.35 Y59.2 +G1 X0 Y55.69 +G1 Y18 +G1 X0.35 Y14.49 +G1 X1.37 Y11.11 +G1 X3.03 Y8 +G1 X5.27 Y5.27 +G1 X8 Y3.03 +G1 X11.11 Y1.37 +G1 X14.49 Y0.35 +G1 X18 Y0 +G1 X55.76 +G0 Z3 +M5 +G0 X40 Y75 +M2 diff --git a/src/resources/config-template.json b/src/resources/config-template.json new file mode 100644 index 0000000..4820da5 --- /dev/null +++ b/src/resources/config-template.json @@ -0,0 +1,523 @@ +{ + "settings": { + "units": { + "type": "enum", + "values": ["METRIC", "IMPERIAL"], + "default": "METRIC" + }, + "max-deviation": { + "help": + "Default allowed deviation from programmed path. Also see G64 & G61.", + "type": "float", + "min": 0.001, + "max": 100, + "unit": "mm", + "iunit": "in", + "scale": 25.4, + "default": 0.1 + }, + "junction-accel": { + "help": + "Higher values will increasing cornering speed but may cause stalls.", + "type": "float", + "min": 10000, + "max": 100000000, + "unit": "mm/min²", + "iunit": "in/min²", + "scale": 25.4, + "default": 200000 + } + }, + "motors": { + "type": "list", + "index": "0123", + "default": [ + {"axis": "X"}, + {"axis": "Y"}, + {"axis": "Z"}, + {"axis": "A"} + ], + "template": { + "general": { + "axis": { + "type": "enum", + "values": ["X", "Y", "Z", "A", "B", "C"], + "default": "X", + "code": "an" + } + }, + + "power": { + "enabled": { + "type": "bool", + "default": true, + "code": "me" + }, + "drive-current": { + "type": "float", + "min": 0, + "max": 6, + "unit": "amps", + "default": 1.5, + "code": "dc" + }, + "idle-current": { + "type": "float", + "min": 0, + "max": 2, + "unit": "amps", + "default": 0, + "code": "ic" + } + }, + + "motion": { + "reverse": { + "type": "bool", + "default": false, + "code": "rv" + }, + "microsteps": { + "type": "int", + "values": [1, 2, 4, 8, 16, 32, 64, 128, 256], + "unit": "per full step", + "default": 32, + "code": "mi" + }, + "max-velocity": { + "type": "float", + "min": 0, + "unit": "m/min", + "iunit": "IPM", + "scale": 0.0254, + "default": 5, + "code": "vm" + }, + "max-accel": { + "type": "float", + "min": 0, + "unit": "km/min²", + "iunit": "g-force", + "scale": 35.304, + "default": 10, + "code": "am" + }, + "max-jerk": { + "type": "float", + "min": 0, + "unit": "km/min³", + "iunit": "g/min", + "scale": 35.304, + "default": 50, + "code": "jm" + }, + "step-angle": { + "type": "float", + "min": 0, + "max": 360, + "step": 0.1, + "unit": "degrees", + "default": 1.8, + "code": "sa" + }, + "travel-per-rev": { + "type": "float", + "unit": "mm", + "iunit": "in", + "scale": 25.4, + "default": 5, + "code": "tr" + } + }, + + "limits": { + "min-soft-limit": { + "type": "float", + "unit": "mm", + "iunit": "in", + "scale": 25.4, + "default": 0, + "code": "tn" + }, + "max-soft-limit": { + "type": "float", + "unit": "mm", + "iunit": "in", + "scale": 25.4, + "default": 0, + "code": "tm" + }, + "min-switch": { + "type": "enum", + "values": ["disabled", "normally-open", "normally-closed"], + "default": "disabled", + "code": "ls", + "pins": [3, 5, 9, 11] + }, + "max-switch": { + "type": "enum", + "values": ["disabled", "normally-open", "normally-closed"], + "default": "disabled", + "code": "xs", + "pins": [4, 8, 10, 12] + } + }, + + "homing": { + "homing-mode": { + "type": "enum", + "values": ["manual", "switch-min", "switch-max","stall-min","stall-max"], + "default": "manual", + "code": "ho" + }, + "stall-microstep": { + "type": "int", + "values": [2, 4, 8, 16, 32, 64, 128, 256], + "unit": "per full step", + "default": 8, + "code": "lm", + "hmodes": ["stall-min","stall-max"] + }, + "search-velocity": { + "type": "float", + "min": 0, + "unit": "m/min", + "iunit": "IPM", + "scale": 0.0254, + "default": 0.5, + "code": "sv", + "hmodes": ["switch-min", "switch-max", "stall-min","stall-max"] + }, + "latch-velocity": { + "type": "float", + "min": 0, + "unit": "m/min", + "iunit": "IPM", + "scale": 0.0254, + "default": 0.1, + "code": "lv", + "hmodes": ["switch-min", "switch-max"] + }, + "latch-backoff": { + "type": "float", + "min": 0, + "unit": "mm", + "iunit": "in", + "scale": 25.4, + "default": 100, + "code": "lb", + "hmodes": ["switch-min", "switch-max"] + }, + "stall-volts": { + "type": "float", + "min": 0, + "unit": "v", + "default": 6, + "code": "tv", + "hmodes": ["stall-min","stall-max"] + }, + "stall-sample-time": { + "type": "int", + "values": [50, 100, 200, 300, 400, 600, 800, 1000], + "default": 50, + "unit": "µsec", + "code": "sp", + "hmodes": ["stall-min","stall-max"] + }, + "stall-current": { + "type": "float", + "min": 0, + "unit": "amps", + "default": 1.5, + "code": "tc", + "hmodes": ["stall-min","stall-max"] + }, + "zero-backoff": { + "type": "float", + "min": 0, + "unit": "mm", + "iunit": "in", + "scale": 25.4, + "default": 5, + "code": "zb", + "hmodes": ["switch-min", "switch-max", "stall-min","stall-max"] + } + } + } + }, + + "tool": { + "tool-type": { + "type": "enum", + "values": ["Disabled", "PWM Spindle", "Huanyang VFD", "Custom Modbus VFD", + "AC-Tech VFD", "Nowforever VFD", "Delta VFD015M21A (Beta)", + "YL600, YL620, YL620-A VFD (Beta)", "FR-D700 (Beta)", + "Sunfar E300 (Beta)", "OMRON MX2"], + "default": "Disabled", + "code": "st" + }, + "tool-reversed": { + "type": "bool", + "default": false, + "code": "sr" + }, + "max-spin": { + "type": "float", + "unit": "RPM", + "min": 0, + "default": 10000, + "code": "sx" + }, + "min-spin": { + "type": "float", + "unit": "RPM", + "min": 0, + "default": 0, + "code": "sm" + }, + "tool-enable-mode": { + "type": "enum", + "values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri", + "hi-tri"], + "default": "lo-hi", + "code": "eom", + "pin": 15 + }, + "tool-direction-mode": { + "type": "enum", + "values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri", + "hi-tri"], + "default": "lo-hi", + "code": "dom", + "pin": 16 + } + }, + + "modbus-spindle": { + "bus-id": { + "type": "int", + "default": "1", + "code": "hi" + }, + "baud": { + "type": "enum", + "values": ["9600", "19200", "38400", "57600", "115200"], + "default": "9600", + "code": "mb" + }, + "parity": { + "type": "enum", + "values": ["None", "Even", "Odd"], + "default": "None", + "code": "ma" + }, + "multi-write": { + "help": "Use Modbus multi register write. Function 16 vs. 6.", + "type": "bool", + "default": false, + "code": "mw" + }, + "regs": { + "type": "list", + "index": "0123456789abcdefghijklmnopqrstuv", + "default": [], + "template": { + "reg-type": { + "type": "enum", + "values": [ + "disabled", + "connect-write", + "max-freq-read", "max-freq-fixed", + "freq-set", "freq-signed-set", + "stop-write", "forward-write", "reverse-write", + "freq-read", "freq-signed-read", "freq-actech-read", + "status-read", + "disconnect-write"], + "default": "disabled", + "code": "vt" + }, + "reg-addr": { + "type": "int", + "min": 0, + "max": 65535, + "default": 0, + "code": "va" + }, + "reg-value": { + "type": "int", + "min": 0, + "max": 65535, + "default": 0, + "code": "vv" + } + } + } + }, + + "pwm-spindle": { + "pwm-inverted": { + "help": "Invert the PWM signal output.", + "type": "bool", + "default": false, + "code": "pi" + }, + "pwm-min-duty": { + "type": "float", + "unit": "%", + "min": 0, + "max": 100, + "default": 1, + "code": "nd" + }, + "pwm-max-duty": { + "type": "float", + "unit": "%", + "min": 0, + "max": 100, + "default": 99.99, + "code": "md" + }, + "pwm-freq": { + "type": "int", + "unit": "Hz", + "min": 8, + "max": 320000, + "default": 1000, + "code": "sf" + }, + "rapid-auto-off": { + "help": "Turn tool off during rapid moves. Useful for LASERs.", + "type": "bool", + "default": false + }, + "dynamic-power": { + "help": + "Adjust tool power based on velocity and feed rate. Useful for LASERs.", + "type": "bool", + "default": false, + "code": "dp" + } + }, + + "switches": { + "estop": { + "type": "enum", + "values": ["disabled", "normally-open", "normally-closed"], + "default": "disabled", + "code": "et", + "pin": 23 + }, + "probe": { + "type": "enum", + "values": ["disabled", "normally-open", "normally-closed"], + "default": "normally-open", + "code": "pt", + "pin": 22 + }, + "switch-debounce": { + "type": "int", + "min": 1, + "max": 5000, + "unit": "ms", + "default": 5, + "code": "sd", + "help": "Minimum time in ms before a switch change is acknowledged." + }, + "switch-lockout": { + "type": "int", + "min": 0, + "max": 60000, + "unit": "ms", + "default": 250, + "code": "sc", + "help": "Time in ms to ignore switch changes after an acknowledge change." + } + }, + + "outputs": { + "load-1": { + "type": "enum", + "values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri", + "hi-tri"], + "default": "lo-hi", + "code": "1om", + "pin": 2 + }, + "load-2": { + "type": "enum", + "values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri", + "hi-tri"], + "default": "lo-hi", + "code": "2om", + "pin": 1 + }, + "fault": { + "type": "enum", + "values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri", + "hi-tri"], + "default": "lo-hi", + "code": "fom", + "pin": 21 + } + }, + + "probe": { + "probe-xdim": { + "type": "float", + "unit": "mm", + "min": 0, + "max": 1000, + "default": 53.975 + }, + "probe-ydim": { + "type": "float", + "unit": "mm", + "min": 0, + "max": 1000, + "default": 53.975 + }, + "probe-zdim": { + "type": "float", + "unit": "mm", + "min": 0, + "max": 1000, + "default": 15.4 + }, + "probe-fast-seek": { + "type": "float", + "unit": "mm/s", + "min": 0, + "max": 1000, + "default": 200 + }, + "probe-slow-seek": { + "type": "float", + "unit": "mm/s", + "min": 0, + "max": 1000, + "default": 25 + } + }, + + "gcode": { + "program-start": { + "type": "text", + "default": "(Runs at program start)\nG90 (Absolute distance mode)\nG17 (Select XY plane)\n" + }, + "tool-change": { + "type": "text", + "default": "(Runs on M6, tool change)\nM0 M6 (MSG, Change tool)" + }, + "program-end": { + "type": "text", + "default": "(Runs on M2, program end)\nM2" + } + }, + + "admin": { + "auto-check-upgrade": { + "type": "bool", + "default": true + } + } +} diff --git a/src/resources/favicon.ico b/src/resources/favicon.ico new file mode 100644 index 0000000..3dfcaae Binary files /dev/null and b/src/resources/favicon.ico differ diff --git a/src/resources/favicon_old.ico b/src/resources/favicon_old.ico new file mode 100644 index 0000000..f71dc1e Binary files /dev/null and b/src/resources/favicon_old.ico differ diff --git a/src/resources/fonts/fontawesome-webfont.ttf b/src/resources/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/src/resources/fonts/fontawesome-webfont.ttf differ diff --git a/src/resources/fonts/fontawesome-webfont.woff b/src/resources/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/src/resources/fonts/fontawesome-webfont.woff differ diff --git a/src/resources/fonts/fontawesome-webfont.woff2 b/src/resources/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/src/resources/fonts/fontawesome-webfont.woff2 differ diff --git a/src/resources/images/DB25-M2_breakout.png b/src/resources/images/DB25-M2_breakout.png new file mode 100644 index 0000000..02929ff Binary files /dev/null and b/src/resources/images/DB25-M2_breakout.png differ diff --git a/src/resources/images/DB25_breakout_box.png b/src/resources/images/DB25_breakout_box.png new file mode 100644 index 0000000..b426d37 Binary files /dev/null and b/src/resources/images/DB25_breakout_box.png differ diff --git a/src/resources/images/axes.png b/src/resources/images/axes.png new file mode 100644 index 0000000..c5539d4 Binary files /dev/null and b/src/resources/images/axes.png differ diff --git a/src/resources/images/bbox.png b/src/resources/images/bbox.png new file mode 100644 index 0000000..7f2e0d9 Binary files /dev/null and b/src/resources/images/bbox.png differ diff --git a/src/resources/images/buildbotics_logo.png b/src/resources/images/buildbotics_logo.png new file mode 100644 index 0000000..c6026d7 Binary files /dev/null and b/src/resources/images/buildbotics_logo.png differ diff --git a/src/resources/images/front.png b/src/resources/images/front.png new file mode 100644 index 0000000..af3e052 Binary files /dev/null and b/src/resources/images/front.png differ diff --git a/src/resources/images/in-use.jpg b/src/resources/images/in-use.jpg new file mode 100644 index 0000000..00cd9b1 Binary files /dev/null and b/src/resources/images/in-use.jpg differ diff --git a/src/resources/images/intensity.png b/src/resources/images/intensity.png new file mode 100644 index 0000000..be9972d Binary files /dev/null and b/src/resources/images/intensity.png differ diff --git a/src/resources/images/isometric.png b/src/resources/images/isometric.png new file mode 100644 index 0000000..760e276 Binary files /dev/null and b/src/resources/images/isometric.png differ diff --git a/src/resources/images/offline.jpg b/src/resources/images/offline.jpg new file mode 100644 index 0000000..23dab13 Binary files /dev/null and b/src/resources/images/offline.jpg differ diff --git a/src/resources/images/onefinity_logo.png b/src/resources/images/onefinity_logo.png new file mode 100644 index 0000000..9910e87 Binary files /dev/null and b/src/resources/images/onefinity_logo.png differ diff --git a/src/resources/images/overtemp.jpg b/src/resources/images/overtemp.jpg new file mode 100644 index 0000000..d39b31a Binary files /dev/null and b/src/resources/images/overtemp.jpg differ diff --git a/src/resources/images/reload.png b/src/resources/images/reload.png new file mode 100644 index 0000000..9f417cd Binary files /dev/null and b/src/resources/images/reload.png differ diff --git a/src/resources/images/tool.png b/src/resources/images/tool.png new file mode 100644 index 0000000..e715ef2 Binary files /dev/null and b/src/resources/images/tool.png differ diff --git a/src/resources/images/top.png b/src/resources/images/top.png new file mode 100644 index 0000000..89d3a56 Binary files /dev/null and b/src/resources/images/top.png differ diff --git a/src/splash/buildbotics.plymouth b/src/splash/buildbotics.plymouth new file mode 100644 index 0000000..5ce2ae8 --- /dev/null +++ b/src/splash/buildbotics.plymouth @@ -0,0 +1,8 @@ +[Plymouth Theme] +Name=buildbotics +Description=Buildbotics boot splash +ModuleName=script + +[script] +ImageDir=/usr/share/plymouth/themes/buildbotics +ScriptFile=/usr/share/plymouth/themes/buildbotics/buildbotics.script diff --git a/src/splash/buildbotics.script b/src/splash/buildbotics.script new file mode 100644 index 0000000..51fb51d --- /dev/null +++ b/src/splash/buildbotics.script @@ -0,0 +1,32 @@ +screenW = Window.GetWidth(); +screenH = Window.GetHeight(); + +image = Image("splash.png"); +imageW = image.GetWidth(); +imageH = image.GetHeight(); + +scaleX = imageW / screenW; +scaleY = imageH / screenH; + +flag = 1; + +if (scaleX > 1 || scaleY > 1) { + if (scaleX > scaleY) { + resized = image.Scale(screenW, imageH / scaleX); + imageX = 0; + imageY = (screenH - ((imageH * screenW) / imageW)) / 2; + + } else { + resized = image.Scale(imageW / scaleY, screenH); + imageX = (screenW - ((imageW * screenH) / imageH)) / 2; + imageY = 0; + } + +} else { + resized = image.Scale(imageW, imageH); + imageX = (screenW - imageW) / 2; + imageY = (screenH - imageH) / 2; +} + +sprite = Sprite(resized); +sprite.SetPosition(imageX, imageY, -100); diff --git a/src/splash/splash.png b/src/splash/splash.png new file mode 100644 index 0000000..274371e Binary files /dev/null and b/src/splash/splash.png differ diff --git a/src/static/css/Audiowide.css b/src/static/css/Audiowide.css new file mode 100644 index 0000000..a73be84 --- /dev/null +++ b/src/static/css/Audiowide.css @@ -0,0 +1,6 @@ +@font-face { + font-family: 'Audiowide'; + font-style: normal; + font-weight: 400; + src: local('Audiowide'), local('Audiowide-Regular'), url(http://fonts.gstatic.com/s/audiowide/v4/8XtYtNKEyyZh481XVWfVOqCWcynf_cDxXwCLxiixG1c.ttf) format('truetype'); +} diff --git a/src/static/css/clusterize.css b/src/static/css/clusterize.css new file mode 100644 index 0000000..dc7f878 --- /dev/null +++ b/src/static/css/clusterize.css @@ -0,0 +1,38 @@ +/* max-height - the only parameter in this file that needs to be edited. + * Change it to suit your needs. The rest is recommended to leave as is. + */ +.clusterize-scroll{ + max-height: 200px; + overflow: auto; +} + +/** + * Avoid vertical margins for extra tags + * Necessary for correct calculations when rows have nonzero vertical margins + */ +.clusterize-extra-row{ + margin-top: 0 !important; + margin-bottom: 0 !important; +} + +/* By default extra tag .clusterize-keep-parity added to keep parity of rows. + * Useful when used :nth-child(even/odd) + */ +.clusterize-extra-row.clusterize-keep-parity{ + display: none; +} + +/* During initialization clusterize adds tabindex to force the browser to keep focus + * on the scrolling list, see issue #11 + * Outline removes default browser's borders for focused elements. + */ +.clusterize-content{ + outline: 0; + counter-reset: clusterize-counter; +} + +/* Centering message that appears when no data provided + */ +.clusterize-no-data td{ + text-align: center; +} \ No newline at end of file diff --git a/src/static/css/font-awesome.min.css b/src/static/css/font-awesome.min.css new file mode 100644 index 0000000..540440c --- /dev/null +++ b/src/static/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/src/static/css/pure-min.css b/src/static/css/pure-min.css new file mode 100644 index 0000000..f0aa374 --- /dev/null +++ b/src/static/css/pure-min.css @@ -0,0 +1,11 @@ +/*! +Pure v0.6.0 +Copyright 2014 Yahoo! Inc. All rights reserved. +Licensed under the BSD License. +https://github.com/yahoo/pure/blob/master/LICENSE.md +*/ +/*! +normalize.css v^3.0 | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;*letter-spacing:normal;*word-spacing:-.43em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-flex;-webkit-flex-flow:row wrap;display:-ms-flexbox;-ms-flex-flow:row wrap;-ms-align-content:flex-start;-webkit-align-content:flex-start;align-content:flex-start}.opera-only :-o-prefocus,.pure-g{word-spacing:-.43em}.pure-u{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class *="pure-u"]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-2,.pure-u-1-3,.pure-u-2-3,.pure-u-1-4,.pure-u-3-4,.pure-u-1-5,.pure-u-2-5,.pure-u-3-5,.pure-u-4-5,.pure-u-5-5,.pure-u-1-6,.pure-u-5-6,.pure-u-1-8,.pure-u-3-8,.pure-u-5-8,.pure-u-7-8,.pure-u-1-12,.pure-u-5-12,.pure-u-7-12,.pure-u-11-12,.pure-u-1-24,.pure-u-2-24,.pure-u-3-24,.pure-u-4-24,.pure-u-5-24,.pure-u-6-24,.pure-u-7-24,.pure-u-8-24,.pure-u-9-24,.pure-u-10-24,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%;*width:4.1357%}.pure-u-1-12,.pure-u-2-24{width:8.3333%;*width:8.3023%}.pure-u-1-8,.pure-u-3-24{width:12.5%;*width:12.469%}.pure-u-1-6,.pure-u-4-24{width:16.6667%;*width:16.6357%}.pure-u-1-5{width:20%;*width:19.969%}.pure-u-5-24{width:20.8333%;*width:20.8023%}.pure-u-1-4,.pure-u-6-24{width:25%;*width:24.969%}.pure-u-7-24{width:29.1667%;*width:29.1357%}.pure-u-1-3,.pure-u-8-24{width:33.3333%;*width:33.3023%}.pure-u-3-8,.pure-u-9-24{width:37.5%;*width:37.469%}.pure-u-2-5{width:40%;*width:39.969%}.pure-u-5-12,.pure-u-10-24{width:41.6667%;*width:41.6357%}.pure-u-11-24{width:45.8333%;*width:45.8023%}.pure-u-1-2,.pure-u-12-24{width:50%;*width:49.969%}.pure-u-13-24{width:54.1667%;*width:54.1357%}.pure-u-7-12,.pure-u-14-24{width:58.3333%;*width:58.3023%}.pure-u-3-5{width:60%;*width:59.969%}.pure-u-5-8,.pure-u-15-24{width:62.5%;*width:62.469%}.pure-u-2-3,.pure-u-16-24{width:66.6667%;*width:66.6357%}.pure-u-17-24{width:70.8333%;*width:70.8023%}.pure-u-3-4,.pure-u-18-24{width:75%;*width:74.969%}.pure-u-19-24{width:79.1667%;*width:79.1357%}.pure-u-4-5{width:80%;*width:79.969%}.pure-u-5-6,.pure-u-20-24{width:83.3333%;*width:83.3023%}.pure-u-7-8,.pure-u-21-24{width:87.5%;*width:87.469%}.pure-u-11-12,.pure-u-22-24{width:91.6667%;*width:91.6357%}.pure-u-23-24{width:95.8333%;*width:95.8023%}.pure-u-1,.pure-u-1-1,.pure-u-5-5,.pure-u-24-24{width:100%}.pure-button{display:inline-block;zoom:1;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:#444;color:rgba(0,0,0,.8);border:1px solid #999;border:0 rgba(0,0,0,0);background-color:#E6E6E6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:hover,.pure-button:focus{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);background-image:-webkit-gradient(linear,0 0,0 100%,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:-moz-linear-gradient(top,rgba(0,0,0,.05) 0,rgba(0,0,0,.1));background-image:-o-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000\9}.pure-button[disabled],.pure-button-disabled,.pure-button-disabled:hover,.pure-button-disabled:focus,.pure-button-disabled:active{border:0;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);filter:alpha(opacity=40);-khtml-opacity:.4;-moz-opacity:.4;opacity:.4;cursor:not-allowed;box-shadow:none}.pure-button-hidden{display:none}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=text]:focus,.pure-form input[type=password]:focus,.pure-form input[type=email]:focus,.pure-form input[type=url]:focus,.pure-form input[type=date]:focus,.pure-form input[type=month]:focus,.pure-form input[type=time]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=week]:focus,.pure-form input[type=number]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=color]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129FEA}.pure-form input:not([type]):focus{outline:0;border-color:#129FEA}.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus,.pure-form input[type=checkbox]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=text][disabled],.pure-form input[type=password][disabled],.pure-form input[type=email][disabled],.pure-form input[type=url][disabled],.pure-form input[type=date][disabled],.pure-form input[type=month][disabled],.pure-form input[type=time][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=week][disabled],.pure-form input[type=number][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=color][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form textarea:focus:invalid,.pure-form select:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus,.pure-form input[type=checkbox]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=text],.pure-form-stacked input[type=password],.pure-form-stacked input[type=email],.pure-form-stacked input[type=url],.pure-form-stacked input[type=date],.pure-form-stacked input[type=month],.pure-form-stacked input[type=time],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=week],.pure-form-stacked input[type=number],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=color],.pure-form-stacked input[type=file],.pure-form-stacked select,.pure-form-stacked label,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned textarea,.pure-form-aligned select,.pure-form-aligned .pure-help-inline,.pure-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form input.pure-input-rounded,.pure-form .pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form .pure-help-inline,.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=text],.pure-group input[type=password],.pure-group input[type=email],.pure-group input[type=url],.pure-group input[type=date],.pure-group input[type=month],.pure-group input[type=time],.pure-group input[type=datetime],.pure-group input[type=datetime-local],.pure-group input[type=week],.pure-group input[type=number],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=color]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0}.pure-form .pure-help-inline,.pure-form-message-inline,.pure-form-message{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-list,.pure-menu-item{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-link,.pure-menu-heading{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-separator{display:inline-block;*display:inline;zoom:1;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-allow-hover:hover>.pure-menu-children,.pure-menu-active>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;padding:.5em 0}.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar{display:none}.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-link,.pure-menu-disabled,.pure-menu-heading{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent}.pure-menu-active>.pure-menu-link,.pure-menu-link:hover,.pure-menu-link:focus{background-color:#eee}.pure-menu-selected .pure-menu-link,.pure-menu-selected .pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table td:first-child,.pure-table th:first-child{border-left-width:0}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} \ No newline at end of file diff --git a/src/static/css/side-menu.css b/src/static/css/side-menu.css new file mode 100644 index 0000000..4f3c26d --- /dev/null +++ b/src/static/css/side-menu.css @@ -0,0 +1,247 @@ +body { + color: #777; +} + +.pure-img-responsive { + max-width: 100%; + height: auto; +} + +/* +Add transition to containers so they can push in and out. +*/ +#layout, +#menu, +.menu-link { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + -ms-transition: all 0.2s ease-out; + -o-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} + +/* +This is the parent `
    ` that contains the menu and the content area. +*/ +#layout { + position: relative; + padding-left: 0; +} + #layout.active #menu { + left: 150px; + width: 150px; + } + + #layout.active .menu-link { + left: 150px; + } +/* +The content `
    ` is where all your content goes. +*/ +.content { + margin: 0 auto; + padding: 0 2em; + max-width: 800px; + margin-bottom: 50px; + line-height: 1.6em; +} + +.header { + margin: 0; + color: #333; + text-align: center; + padding: 2.5em 2em 0; + border-bottom: 1px solid #eee; + } + .header h1 { + margin: 0.2em 0; + font-size: 3em; + font-weight: 300; + } + .header h2 { + font-weight: 300; + color: #ccc; + padding: 0; + margin-top: 0; + } + +.content-subhead { + margin: 50px 0 20px 0; + font-weight: 300; + color: #888; +} + + + +/* +The `#menu` `
    ` is the parent `
    ` that contains the `.pure-menu` that +appears on the left side of the page. +*/ + +#menu { + margin-left: -150px; /* "#menu" width */ + width: 150px; + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: 1000; /* so the menu or its navicon stays above all content */ + background: #191818; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + /* + All anchors inside the menu should be styled like this. + */ + #menu a { + color: #999; + border: none; + padding: 0.6em 0 0.6em 0.6em; + } + + /* + Remove all background/borders, since we are applying them to #menu. + */ + #menu .pure-menu, + #menu .pure-menu ul { + border: none; + background: transparent; + } + + /* + Add that light border to separate items into groups. + */ + #menu .pure-menu ul, + #menu .pure-menu .menu-item-divided { + border-top: 1px solid #333; + } + /* + Change color of the anchor links on hover/focus. + */ + #menu .pure-menu li a:hover, + #menu .pure-menu li a:focus { + background: #333; + } + + /* + This styles the selected menu item `
  • `. + */ + #menu .pure-menu-selected, + #menu .pure-menu-heading { + background: #1f8dd6; + } + /* + This styles a link within a selected menu item `
  • `. + */ + #menu .pure-menu-selected a { + color: #fff; + } + + /* + This styles the menu heading. + */ + #menu .pure-menu-heading { + font-size: 110%; + color: #fff; + margin: 0; + } + +/* -- Dynamic Button For Responsive Menu -------------------------------------*/ + +/* +The button to open/close the Menu is custom-made and not part of Pure. Here's +how it works: +*/ + +/* +`.menu-link` represents the responsive menu toggle that shows/hides on +small screens. +*/ +.menu-link { + position: fixed; + display: block; /* show this only on small screens */ + top: 0; + left: 0; /* "#menu width" */ + background: #000; + background: rgba(0,0,0,0.7); + font-size: 10px; /* change this value to increase/decrease button size */ + z-index: 10; + width: 2em; + height: auto; + padding: 2.1em 1.6em; +} + + .menu-link:hover, + .menu-link:focus { + background: #000; + } + + .menu-link span { + position: relative; + display: block; + } + + .menu-link span, + .menu-link span:before, + .menu-link span:after { + background-color: #fff; + width: 100%; + height: 0.2em; + } + + .menu-link span:before, + .menu-link span:after { + position: absolute; + margin-top: -0.6em; + content: " "; + } + + .menu-link span:after { + margin-top: 0.6em; + } + + +/* -- Responsive Styles (Media Queries) ------------------------------------- */ + +/* +Hides the menu at `48em`, but modify this based on your app's needs. +*/ +@media (min-width: 1000em) { + + .header, + .content { + padding-left: 2em; + padding-right: 2em; + } + + #layout { + padding-left: 150px; /* left col width "#menu" */ + left: 0; + } + #menu { + left: 150px; + } + + .menu-link { + position: fixed; + left: 150px; + display: none; + } + + #layout.active .menu-link { + left: 150px; + } +} + +@media (max-width: 48em) { + /* Only apply this when the window is small. Otherwise, the following + case results in extra padding on the left: + * Make the window small. + * Tap the menu to trigger the active state. + * Make the window large again. + */ + #layout.active { + position: relative; + left: 150px; + } +} diff --git a/src/static/js/clusterize.min.js b/src/static/js/clusterize.min.js new file mode 100644 index 0000000..a97f921 --- /dev/null +++ b/src/static/js/clusterize.min.js @@ -0,0 +1,17 @@ +/* Clusterize.js - v0.18.1 - 2018-01-02 + http://NeXTs.github.com/Clusterize.js/ + Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */ + +;(function(q,n){"undefined"!=typeof module?module.exports=n():"function"==typeof define&&"object"==typeof define.amd?define(n):this[q]=n()})("Clusterize",function(){function q(b,a,c){return a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)}function n(b,a,c){return a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent("on"+b,c)}function r(b){return"[object Array]"===Object.prototype.toString.call(b)}function m(b,a){return window.getComputedStyle?window.getComputedStyle(a)[b]: +a.currentStyle[b]}var l=function(){for(var b=3,a=document.createElement("b"),c=a.all||[];a.innerHTML="\x3c!--[if gt IE "+ ++b+"]>=l&&!c.tag&&(c.tag=b[0].match(/<([^>\s/]*)/)[1].toLowerCase()),1>=this.content_elem.children.length&&(a.data=this.html(b[0]+b[0]+b[0])),c.tag||(c.tag=this.content_elem.children[0].tagName.toLowerCase()), +this.getRowsHeight(b))},getRowsHeight:function(b){var a=this.options,c=a.item_height;a.cluster_height=0;if(b.length&&(b=this.content_elem.children,b.length)){var d=b[Math.floor(b.length/2)];a.item_height=d.offsetHeight;"tr"==a.tag&&"collapse"!=m("borderCollapse",this.content_elem)&&(a.item_height+=parseInt(m("borderSpacing",this.content_elem),10)||0);"tr"!=a.tag&&(b=parseInt(m("marginTop",d),10)||0,d=parseInt(m("marginBottom",d),10)||0,a.item_height+=Math.max(b,d));a.block_height=a.item_height*a.rows_in_block; +a.rows_in_cluster=a.blocks_in_cluster*a.rows_in_block;a.cluster_height=a.blocks_in_cluster*a.block_height;return c!=a.item_height}},getClusterNum:function(){this.options.scroll_top=this.scroll_elem.scrollTop;return Math.floor(this.options.scroll_top/(this.options.cluster_height-this.options.block_height))||0},generateEmptyRow:function(){var b=this.options;if(!b.tag||!b.show_no_data_row)return[];var a=document.createElement(b.tag),c=document.createTextNode(b.no_data_text);a.className=b.no_data_class; +if("tr"==b.tag){var d=document.createElement("td");d.colSpan=100;d.appendChild(c)}a.appendChild(d||c);return[a.outerHTML]},generate:function(b,a){var c=this.options,d=b.length;if(de&&g++;f=l&&"tr"==this.options.tag){var c=document.createElement("div");for(c.innerHTML=""+b+"
    ";b=a.lastChild;)a.removeChild(b);for(c=this.getChildNodes(c.firstChild.firstChild);c.length;)a.appendChild(c.shift())}else a.innerHTML=b},getChildNodes:function(b){b=b.children;for(var a=[],c=0,d=b.length;ca?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; + +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
    a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:k.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("