Verison 1.0.3 Release

Based on Buildbotics 0.4.14
This commit is contained in:
OneFinityCNC
2020-08-27 23:20:27 -04:00
parent 6137475077
commit 24dfa6c64d
302 changed files with 58865 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@@ -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

394
CHANGELOG.md Normal file
View File

@@ -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.

22
CODE_TAG Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>

377
LICENSE Normal file
View File

@@ -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
<joseph@buildbotics.com>.
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
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.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

12
MANIFEST.in Normal file
View File

@@ -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

142
Makefile Normal file
View File

@@ -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

62
README_buildbotics.md Normal file
View File

@@ -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.

41
docs/bbdev-chroot.md Normal file
View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

View File

@@ -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 <linux/init.h>
#include <linux/module.h>
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

106
docs/development.md Normal file
View File

@@ -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=<pass>
Where ``<pass>`` 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.

45
docs/emu_chroot.md Normal file
View File

@@ -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

15
jshint.json Normal file
View File

@@ -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
}
}

14
package.json Normal file
View File

@@ -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": ""
}
}

11
pug-opts.js Normal file
View File

@@ -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()
}
}
}

View File

@@ -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"

206
scripts/avr109-flash.py Normal file
View File

@@ -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')

15
scripts/bbctrl.service Normal file
View File

@@ -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

15
scripts/browser Normal file
View File

@@ -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()

View File

@@ -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)

32
scripts/config-screen Normal file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
if [ $# != 3 ]; then
echo "Usage: $0 <width> <height> <rotation>"
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

259
scripts/config-wifi Normal file
View File

@@ -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 <SSID> Set SSID."
echo " -p <PASS> Set password."
echo " -c <CHANNEL> 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

17
scripts/demo-chroot Normal file
View File

@@ -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" "$@"

12
scripts/edit-boot-config Normal file
View File

@@ -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

83
scripts/edit-config Normal file
View File

@@ -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 = '<name>=<value>', 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)

6
scripts/gplan-build.sh Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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

121
scripts/install.sh Normal file
View File

@@ -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"

View File

@@ -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

19
scripts/next-rc Normal file
View File

@@ -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)

1
scripts/ratpoisonrc Normal file
View File

@@ -0,0 +1 @@
startup_message off

31
scripts/rc.local Normal file
View File

@@ -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

23
scripts/reset-video Normal file
View File

@@ -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)

63
scripts/rpi-chroot.sh Normal file
View File

@@ -0,0 +1,63 @@
#!/bin/bash -ex
ROOT="$PWD/rpi-root"
LOOP=9
if [ $# -lt 1 ]; then
echo "Usage: $0 <image> <exec>"
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

26
scripts/sethostname Normal file
View File

@@ -0,0 +1,26 @@
#!/bin/bash -e
HOSTNAME="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
if [ "$HOSTNAME" == "" ]; then
echo "Usage: $0 <hostname>"
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

110
scripts/setup_rpi.sh Normal file
View File

@@ -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

16
scripts/ssh-bbctrl Normal file
View File

@@ -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"

64
scripts/svg2abs.js Normal file
View File

@@ -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;
}
}

27
scripts/update-bbctrl Normal file
View File

@@ -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

25
scripts/upgrade-bbctrl Normal file
View File

@@ -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

22
scripts/xinitrc Normal file
View File

@@ -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

3
scripts/xorg.conf Normal file
View File

@@ -0,0 +1,3 @@
Section "ServerFlags"
Option "DontVTSwitch" "on"
EndSection

2
setup.cfg Normal file
View File

@@ -0,0 +1,2 @@
[sdist]
formats=bztar

40
setup.py Normal file
View File

@@ -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,
)

14
src/avr/.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
# Backup files
*~
\#*
build
.dep
/*.eep
/*.hex
/*.elf
/*.lss
/*.map
*.o

56
src/avr/BezierMath.md Normal file
View File

@@ -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

40
src/avr/Makefile Normal file
View File

@@ -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

115
src/avr/Makefile.common Normal file
View File

@@ -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/*)

18
src/avr/README.md Normal file
View File

@@ -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

63
src/avr/data_usage.py Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>. #
# #
# 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 #
# <http://www.gnu.org/licenses/>. #
# #
# For information regarding this software email: #
# "Joseph Coffland" <joseph@buildbotics.com> #
# #
################################################################################
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)

1
src/avr/emu/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
bbemu

38
src/avr/emu/Makefile Normal file
View File

@@ -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)

View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#define EEMEM
#define eeprom_update_word(PTR, VAL) *(PTR) = (VAL)
#define eeprom_read_word(PTR) *(PTR)
#define eeprom_is_ready() true

View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "io.h"
void cli();
void sei();
#define ISR(X) void __##X()

7718
src/avr/emu/src/avr/io.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#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)

33
src/avr/emu/src/avr/wdt.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#define WDTO_250MS 0
#define wdt_enable(...)
#define wdt_disable()
#define wdt_reset()

163
src/avr/emu/src/emu.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include <config.h>
#include <avr/io.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
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);
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#define ATOMIC_BLOCK(x)
#define ATOMIC_RESTORESTATE

View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#define _crc16_update(...) 0

View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include <unistd.h>
#define _delay_ms(x) usleep((x) * 1000)
#define _delay_us(x) usleep(x)

175
src/avr/src/SCurve.cpp Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "SCurve.h"
#include <math.h>
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;}

66
src/avr/src/SCurve.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include <math.h>
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);
};

90
src/avr/src/analog.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "analog.h"
#include "config.h"
#include <avr/interrupt.h>
#include <stdint.h>
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);}

33
src/avr/src/analog.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
void analog_init();
float analog_get(unsigned port);
void analog_rtc_callback();

124
src/avr/src/axis.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "axis.h"
#include "motor.h"
#include "switch.h"
#include "util.h"
#include <math.h>
#include <string.h>
#include <ctype.h>
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)

50
src/avr/src/axis.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "config.h"
#include <stdbool.h>
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);

151
src/avr/src/base64.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "base64.h"
#include "util.h"
#include <ctype.h>
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;
}

37
src/avr/src/base64.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include <stdint.h>
#include <stdbool.h>
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);

278
src/avr/src/command.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#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 <util/atomic.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#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;}

50
src/avr/src/command.def Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
//(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

58
src/avr/src/command.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "config.h"
#include "status.h"
#include <stdbool.h>
#include <stdint.h>
// 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();

View File

@@ -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
"_": {}
}

83
src/avr/src/commands.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#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 <stdio.h>
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;
}

222
src/avr/src/config.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "pins.h"
#include <avr/interrupt.h>
// 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

507
src/avr/src/cpp_magic.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
/* 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

546
src/avr/src/drv8711.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "drv8711.h"
#include "status.h"
#include "stepper.h"
#include "switch.h"
#include "estop.h"
#include "seek.h"
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#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);
}

188
src/avr/src/drv8711.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "config.h"
#include "status.h"
#include "motor.h"
#include <stdint.h>
#include <stdbool.h>
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);

36
src/avr/src/emu.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#ifdef __AVR__
#define emu_init()
#define emu_callback()
#else
void emu_init();
void emu_callback();
#endif

126
src/avr/src/estop.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#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;}

42
src/avr/src/estop.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "status.h"
#include <stdbool.h>
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)

306
src/avr/src/exec.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#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);
}

59
src/avr/src/exec.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "config.h"
#include "spindle.h"
#include "status.h"
#include <stdbool.h>
#include <stdint.h>
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();

158
src/avr/src/hardware.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "hardware.h"
#include "rtc.h"
#include "usart.h"
#include "config.h"
#include "pgmspace.h"
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <avr/wdt.h>
#include <stdbool.h>
#include <stddef.h>
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;}

40
src/avr/src/hardware.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "status.h"
#include <stdint.h>
void hw_init();
void hw_request_hard_reset();
void hw_reset_handler();
uint8_t hw_disable_watchdog();
void hw_restore_watchdog(uint8_t state);

301
src/avr/src/huanyang.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "huanyang.h"
#include "config.h"
#include "modbus.h"
#include "estop.h"
#include <util/atomic.h>
#include <string.h>
#include <math.h>
/*
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;}

226
src/avr/src/huanyang.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#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;

129
src/avr/src/i2c.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "i2c.h"
#include <avr/interrupt.h>
#include <stdbool.h>
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;}

41
src/avr/src/i2c.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "config.h"
#include <stdint.h>
#include <stdbool.h>
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);

118
src/avr/src/io.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "io.h"
#include "status.h"
#include "util.h"
#include "command.h"
#include "exec.h"
#include "rtc.h"
#include "analog.h"
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
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);
}

40
src/avr/src/io.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
typedef enum {
INPUT_IMMEDIATE,
INPUT_RISE,
INPUT_FALL,
INPUT_HIGH,
INPUT_LOW,
} input_mode_t;
void io_callback();

169
src/avr/src/jog.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#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 <stdbool.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
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;
}

34
src/avr/src/jog.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "status.h"
stat_t jog_exec();
void jog_stop();

143
src/avr/src/lcd.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "lcd.h"
#include "rtc.h"
#include "hardware.h"
#include "command.h"
#include <avr/io.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include <stdbool.h>
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();
}

103
src/avr/src/lcd.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#pragma once
#include "pgmspace.h"
#include <stdint.h>
// 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();

274
src/avr/src/line.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#include "config.h"
#include "exec.h"
#include "command.h"
#include "spindle.h"
#include "util.h"
#include "SCurve.h"
#include <math.h>
#include <float.h>
#include <string.h>
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);
}

101
src/avr/src/main.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
#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 <avr/wdt.h>
#include <stdio.h>
#include <stdbool.h>
// 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;
}

62
src/avr/src/messages.def Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
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")

Some files were not shown because too many files have changed in this diff Show More