Compare commits

...

120 Commits

Author SHA1 Message Date
Scott Allen bc460a2cff Fix typo in invert() function documentation 2023-08-14 14:59:19 -04:00
Scott Allen ca1c3ae024 Fix links to duration variable in BeepPin1 docs 2022-06-05 13:58:01 -04:00
Scott Allen 031e9164aa
Merge pull request #71 from Pharap/make-cabi-cpp-compatible
Make cabi C++ compatible
2022-05-01 10:38:18 -04:00
Pharap 7f456cff69
Make cabi C++ compatible
Casting the result of these calls to malloc should be all that is required to make cabi compilable as C++.
Without these casts cabi won't compile as C++ because in C++ `void *` is not implicitly convertible to other data types.
LodePNG already has C++ compatibility.
2022-05-01 08:41:41 +01:00
Scott Allen 2374247788
Merge pull request #63 from ace-dent/PR0626-Contributing
Clarify Contribution style requirements
2021-07-01 18:02:43 -04:00
Scott Allen 98afc7229f
Merge pull request #62 from ace-dent/PR0625-Typos
Fix typos in a few places
2021-07-01 17:57:11 -04:00
Scott Allen 4f0d802d50
Merge pull request #57 from ace-dent/PR0930-OptimizePNG
Optimize png images
2021-07-01 17:55:48 -04:00
Andrew Dent ccedf84434 Clarify Contribution style requirements
Add examples to give clear guidance (and a reminder),
of commit message style requirements.
2021-06-26 00:18:59 +01:00
Andrew Dent 279fd9d0f4 Fix typos in a few places
Pedantic tweaks:

- Fix spellings in a few places.

- Update License copyright year.
2021-06-26 00:13:05 +01:00
Scott Allen 36cbdc734c Fix typo in fillScreen() C++ equivalent code 2021-06-05 07:37:01 -04:00
Scott Allen ab4df7c77a Remove register keywords 2021-04-16 08:47:32 -04:00
Andrew Dent b4b4d45009 Optimize png images
The project’s images have been run through lossless optimization.
It will not affect the project but saves a few bytes for each
download of the repository.
See: https://github.com/ace-dent/pngslim
2020-09-30 11:41:46 +01:00
Scott Allen 7dc88bed30 Change version to 6.0.0
The major version number has been incremented only because there's a
chance that a sketch that sets text wrap on will behave differently,
due to the changes in the way wrapping is now handled.
2020-09-29 09:44:41 -04:00
Scott Allen 191e9a2e45 Document bitmap image height requirements
Added the requirement that bitmap heights must be a multiple of
8 pixels in the documentation for drawBitmap() and drawCompressed().
2020-09-29 09:44:41 -04:00
Scott Allen 014e6e0a8b Add FontDemo example sketch
Shows the entire font and demonstrates the wrap and raw mode handling.
2020-09-29 09:44:40 -04:00
Scott Allen 500d81abb7 Add image height check to cabi.c
An image converted using the cabi utility must have a height that is
a multiple of 8 pixels. An error is now reported if not.

Also, the README.md file for cabi was updated with this requirement.
2020-09-29 09:44:40 -04:00
Scott Allen 2baca75689 Fix colors in logo PNG files
Files arduboy_logo.png and arduboy_screen.png had some pixels that
were a dark gray color instead of fully black. The images now contain
only fully black and fully white pixels.
2020-09-29 09:44:40 -04:00
Scott Allen 2f1c515672 Add public variables to keywords.txt 2020-09-29 09:44:40 -04:00
Scott Allen 98f03773b2 Make state variables used by pollButtons() public
The variables currentButtonState and previousButtonState used by
pollButtons(), justPressed() and justReleased() have been made public.
This allows them to be manipulated if circumstances require it.

The documentation added for previousButtonState includes an example.
2020-09-29 09:44:40 -04:00
Scott Allen 4698dd276e Make all possible functions and variables static
This is mainly to reduce code size.

The write() function in class Arduboy2 has to remain virtual,
so functions that result in it eventually being called could
not be made static.

The members of the Point and Rect structures could not be made static
due to their intended use as having multiple instances.

Also to reduce code size, bootLogoExtra() was made non-virtual.
This meant that the begin() and bootLogo...() functions in the
Arduboy2Base class had to have duplicate or equivalent functions
added to the Arduboy2 class.

Related documentation was updated and some minor changes were made to
non-related documentation.
2020-09-29 09:44:40 -04:00
Scott Allen e78e7c9d55 Add more Doxygen cross-referencing
Also:
- Rearrange the order of some member function declarations.
- Minor documentation changes.
2020-09-29 09:44:40 -04:00
Scott Allen 1264edc748 Refactor function BitStreamReader()
Changed from struct to class.

Removed explicit use of the "this" pointer for consistency with
the rest of the library.
2020-09-29 09:44:40 -04:00
Scott Allen aaf4159274 Remove PIXEL_SAFE_MODE define
The ability to compile drawPixel() to not check for
drawing pixels off screen has been removed.
2020-09-29 09:44:20 -04:00
Scott Allen 86169a0027 Modify SetSystemEEPROM to use char size functions
The library functions to get the dimensions of text characters replace
values hard coded in the sketch.

Also changed some #define constants to constexpr variables.

The actual operation of the sketch is unchanged.
2020-09-28 21:12:31 -04:00
Scott Allen 58a35e2ad9 Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
  size and to make text wrapping operate more like what would normally
  be expected.

- A new flag variable, textRaw, has been added, along with functions
  setTextRawMode() and getTextRawMode() to set and read it.
  When set, the write() function will render the value \n (0x0a) as
  an "inverse white circle" instead of handling it as a newline and
  render value \r (0x0d) as a "musical eighth note" instead of
  ignoring it.

- Spacing added at the end of each character is no longer factored in
  when determining if a character will fit on screen at the end of
  a line.

- Checking if line wrap is neccessary is done before drawing a
  character, instead of doing it after drawing to prepare for the
  next character. This fixed a problem with requested line feeds
  interacting with wrapping, resulting in unexpected blank lines.

- Fixed a bug that caused the drawing of characters with a transparent
  background to not work properly. (This is selected by setting the
  background color to be the same as the text color.)

- Added new functions getCharacterWidth() getCharacterHeight()
  getCharacterSpacing() getLineSpacing() to programmatically get
  the dimensions of a character, given the desired text size.

- Functions write() and drawChar() are now able to render a font of
  any character size, and with any character and line spacing, by
  setting variables characterWidth characterHeight characterSpacing
  and lineSpacing, and providing a matching font array. This doesn't
  affect their use with this library but has been done for the sake
  of code portability.
2020-09-28 21:11:13 -04:00
Andrew Dent bbebec346d Add array lengths to comments for boot logos 2020-08-23 15:23:01 -04:00
Scott Allen 350a6ee629 Fix a cursor position bug in BeepDemo example 2020-08-23 15:23:00 -04:00
Scott Allen 7fcbd35266 Fix minor documentation issues 2020-08-23 15:21:28 -04:00
Scott Allen 5d154f817a Make SetSystemEEPROM more C++ correct
(Compiled output is identical to previous)

- Made enumerations scoped
- Changed all type byte to uint8_t
- Changed all type boolean to bool
- Changed some type unsigned int to uint16_t
- Changed C-style casts to static_cast
2020-07-27 09:37:55 -04:00
Scott Allen f1009abd7b Change version to 5.3.0 2020-07-25 18:38:05 -04:00
Scott Allen 8fc6eee194 Add file extras/docs/VERSIONING.txt
Lists the files containing the library version number that must be
updated for a new release.
2020-07-25 18:38:05 -04:00
Scott Allen f0be8ad3b4 Update Doxyfile for Doxygen version 1.8.18
Also made a few settings changes
2020-07-25 18:38:05 -04:00
Scott Allen ecb74632ba Remove emphasis in headers in README.md
Recent releases of Doxygen have problems with using markdown within
markdown headers. Headers in README.md containing emphasis were
changed to only plain text.
2020-07-25 18:38:05 -04:00
Scott Allen 13130f368a Move generateRandomSeed() to Arduboy2Core
Suggested by @yyyc514 (Josh Goebel)
The generateRandomSeed() function was moved from the Arduboy2Base class
to the Arduboy2Core class, since it deals directly with hardware.
It was also made static.

Some related documentation was changed.
2020-07-25 18:38:05 -04:00
Scott Allen 2491e639aa Fix a minor visual glitch in ArduBreakout
Submitted by @uXeBoy (Dan O'Shea).
The game screen was displayed very briefly after entering a high score,
before the title screen is displayed.
2020-07-25 18:38:05 -04:00
Scott Allen 32d23b7984 Refactor ArduBreakout sound and constants
- Use precomputed constant values directly usable by the Beep class
  for tone generation arguments.
  This reduces program size significantly.

- Use the constexpr specifier for all constants.
2020-07-25 18:38:05 -04:00
Scott Allen e0ade6ca99 Clean up ArduBreakout a bit
No actual code changes.

- Changed colors hardcoded as 0 and 1 to BLACK and WHITE.
- Added a comment describing EEPROM space used.
- Made placement of braces and else statements consistent.
- Added spaces between some function call arguments.
- Fixed some spelling mistakes in comments.
- Removed commented out diagnostic code.
- Updated "modifications" comment.
2020-07-25 18:38:05 -04:00
Scott Allen 4530325412 Fix portability issues in the Sprites classes
Issues discovered and fixes developed by @Pharap

The Sprites classes relied on the behavior of 16 bit integer arithmetic
for operations that cause an overflow; specifically (ofs + WIDTH)
calculations for an index into the screen buffer. This would cause
problems if the code was ported to an environment in which the basic
integer type (i.e. "int") is larger than 16 bits.

Also, there was code that assumed little endianness for a technique
used to retrieve the high byte a 16 bit value. This would cause
problems if the code was ported to a big endian architecture.

The changes made are strictly for the sake of improved portability and
better programming practices. They don’t affect code compiled for the
Arduboy in any way.
2020-07-25 18:38:05 -04:00
Scott Allen d7758249fa Add function SPItransferAndRead()
Suggested by @MrBlinky
Same as SPItransfer() but also returns the byte received.
Provided for use with homemade or expanded units that have had
additional peripherals added to the SPI bus that are capable of
sending data.
2020-07-25 18:38:05 -04:00
Pharap 5563599c6d Make writeUnitName() const-correct
Changed the writeUnitName() parameter from
"char* name" to "const char* name".
This will prevent warnings or errors when a string literal or
an array of type "const char" is passed to it.
2020-07-25 18:38:05 -04:00
Scott Allen 30fb448db5 Make lcdBootProgram[] a member of Arduboy2Core
The lcdBootProgram[] array, containing the commands to initialize
the OLED display, isn't considered to be part of the API.
To enforce this, and to avoid the possibility of conflicts from
other code using the same name, it has been changed from being a
global variable to a protected member of the Arduboy2Core class.
2020-07-25 18:38:04 -04:00
Scott Allen a04edf24c1 Move mainNoUSB() to its own class
Suggested and developed by @Pharap

The mainNoUSB() function was never intended to be used directly in
sketches. To help prevent this, it has been made a private member of
a new class, Arduboy2NoUSB, which declares main() as a friend.

The ARDUBOY_NO_USB macro has been modified appropriately.

The Arduboy2Core class now inherits Arduboy2NoUSB, for sketches that
improperly have their own main() which calls the old version of
mainNoUSB() instead of using the ARDUBOY_NO_USB macro.
2020-07-25 18:38:04 -04:00
Scott Allen b668114475 Add Cabi and LodePNG to LICENSE.txt
Also applied the CC0 public domain dedication to the
BeepDemo and RGBled example sketches
2020-07-25 18:38:04 -04:00
Scott Allen c74438e377 Rewrite Cabi README.md with more details
Also added file sample.png in the cabi directory, which is used
for the example sketch in the Cabi README.md file

Also added a note referring to Cabi in the documentation for
drawCompressed()
2020-07-25 18:37:44 -04:00
Scott Allen 566557eab3 Delete trailing whitespace in cabi.c 2020-07-24 17:15:37 -04:00
Scott Allen 3345198d3d Eliminate compile warnings and minor mods for cabi
Compiler warnings generated by cabi.c were mostly due to
signed/unsigned mismatches and unused variables. Unused variables
were removed and most type int variables were changed to unsigned,
since there is no signed math or negative numbers required.

More detailed error messages are reported by using the LodePNG
capability to provide text descriptions for its error codes.

The default output array name prefix was changed
from "out" to "compressed_image".

Changed the on/off opaque/transparent thresholds from 128 to 127,
the half way point, because it makes more sense.

Added a program description to the usage output given when no
arguments are provided.

Other unused code was removed and some code was refactored.
2020-07-24 17:15:37 -04:00
Scott Allen 1bcf707616 Add LodePNG files used by cabi to read PNG files
Added lodepng.c (renamed from lodepng.cpp), lodepng.h and LICENSE from
the LodePNG project by Lode Vandevenne.
https://github.com/lvandeve/lodepng
Since this code is licensed separately from the cabi code, it has been
placed in its own subdirectory named lodepng.

cabi.c and README.md have been modified for the LodePNG file location.
2020-07-24 17:15:37 -04:00
Scott Allen 568a15ff87 Elaborate on description and CC0 waiver in cabi.c
Also renamed file LICENSE to COPYING as recommended for CC0
2020-07-24 17:15:37 -04:00
Scott Allen 966c2125f1 Add the Team A.R.G. cabi program to extras
cabi is a command line program written in C. Its purpose is to read
a PNG file containing a bitmap image and convert it to C/C++ code
suitable for use with the drawCompressed() function.

cabi was published by the Team A.R.G. organization. This organization
has been dissolved and its presence on the internet has been removed,
thus public access to cabi from them is no longer available.

A copy of the relevant cabi files has been added under the extras
directory to maintain a method of creating drawCompressed() compatible
bitmaps.
2020-07-24 17:15:37 -04:00
Scott Allen 355e2d2081 Update or remove outdated URLs 2020-07-24 17:15:37 -04:00
Scott Allen 2d04cf60e1 Change non-API EEPROM defines to constexpr
Macro defines for system EEPROM access that were not intended to be
part of the API have been made into protected constexpr variables of
the Arduboy2Base class.
2020-07-24 17:15:37 -04:00
Scott Allen 6a8041f2fa Start boot logo with a row visible on screen
The first time the boot logo was displayed, for scrolling it down,
it was entirely off screen. It now starts with the lowest row of pixels
visible.
2020-07-24 17:15:37 -04:00
Scott Allen 205e84eae6 Fix off screen tests for bitmap functions
Issue reported by @MrBlinky
For drawBitmap(), drawSlowXYBitmap() and drawCompressed() the initial
check for the bitmap being entirely off screen would sometimes consider
a bitmap to be partially on screen when it is only one pixel above or
to the left of the screen. This would result in unnecessarily doing the
work to draw the bitmap when none of it would be visible.
2020-07-24 17:15:37 -04:00
Scott Allen 0adb83693a Add functions anyPressed() setCursorX() setCursorY()
Suggested by @Pharap
2020-07-24 17:15:37 -04:00
Scott Allen 53096d87bf Refactor API code. Clarify and enhance user docs.
Includes some suggestions and guidance by @Pharap

API:
- Rename non-API function swap() to swapInt16()
- Make swapInt16(), drawCircleHelper(), fillCircleHelper() and
  struct BitStreamReader protected members of Arduboy2Base
- Add #define ARDUBOY_UNIT_NAME_BUFFER_SIZE for the size of a buffer
  to hold a Unit Name
- Move the position of the PROGMEM keyword for lcdBootProgram[]
  for consistency

Documentation:
- Remove absolute values referring to a Unit Name
- More details relating to the use of the Unit Name
- More details relating to the font and text functions
- Other minor documentation changes and spelling corrections
2020-07-24 17:15:13 -04:00
Scott Allen b854e2295f Make "static" the first keyword, for consistency
Suggested by @Pharap
2020-07-20 19:19:50 -04:00
Scott Allen 4f884f4ed2 Make font and logo data arrays class members
To address a compiler warning for the font array and improve the API.
With help and guidance from @Pharap
2020-07-20 19:12:45 -04:00
Scott Allen 3f9e86ab99
Merge pull request #46 from Pharap/constexpr-point-and-rect
Make Rect and Point's constructors constexpr
2020-02-21 13:33:04 -05:00
Pharap 4e513a15d0
Make Rect and Point's constructors constexpr 2020-02-21 18:18:15 +00:00
Scott Allen bc4e0ed0b5
Merge pull request #45 from Pharap/add-drawexternalmask-comment
Add additional information to drawExternalMask
2019-07-14 11:46:00 -04:00
Pharap 891f68a0be
Add additional information to drawExternalMask
This information is already contained in the main details block,
but providing it here as well should make it more obvious.
2019-07-13 10:33:29 +01:00
Scott Allen 2fbde21e66
Merge pull request #44 from Pharap/make-width-and-height-constexpr
Make width and height constexpr
2019-05-31 12:22:56 -04:00
Pharap a31e5091d6
Make width and height constexpr
This allows these functions to be used as constant expressions.
2019-05-31 06:38:43 +01:00
Scott Allen 096aaac8dd Change version to 5.2.1 2019-02-27 17:33:10 -05:00
Scott Allen 903cfd9414 Add CONTRIBUTING.md file 2019-02-27 17:24:48 -05:00
Pharap b40e3b9c57 Fix missing Print::write overloads
Problem:
The overloading of `Print::write` was previously causing the other
overloads of `write` to be hidden.
For more information on this behaviour, see the following
StackOverflow question:
https://stackoverflow.com/questions/1628768

Solution:
The solution uses a `using`-declaration to bring the `Print::write`
overloads into `Arduboy2`'s scope.
For more information on this usage of a `using`-declaration, see the
following cppreference page:
https://en.cppreference.com/w/cpp/language/using_declaration#In_class_definition
2019-02-27 15:38:57 -05:00
Blake W. Ford 6ded14fb0e Use local label for loop in fillScreen() assembly
To fix error: symbol loopto is already defined with -O3 optimization.
2019-02-27 14:48:29 -05:00
Scott Allen 974f5298ee Change version to 5.2.0 2018-11-15 19:27:26 -05:00
Scott Allen c87f512b53 Document Rect and Point constructor parameters 2018-11-15 19:27:26 -05:00
Scott Allen 195d3e5cde Add C++ equivalent code for drawPixel() 2018-11-15 19:27:26 -05:00
Mr.Blinky 2941ed100f Fix a compiler issue and optimize drawPixel()
The new compiler in Arduino IDE 1.8.6/1.8.7 has an optimization glitch
where it fails to see the difference between a data structure in RAM
and in PROGMEM when the data is the same and optimizes one out.

drawPixel() is unable to fetch the correct pixel mask from the
bitshift_left array in PROGMEM causing junk to be drawn.

The bitshift_left[] array has now been optimised out and drawPixel()
will function properly. The optimization uses the same number of cycles
and saves 6 bytes.

drawPixel() has also been made static so the Arduboy object pointer is
no longer needlesly passed on to drawPixel() saving more bytes.
2018-11-15 19:26:58 -05:00
Pharap 1d49ce5df8 Move avr/wdt.h include to cpp file 2018-11-13 12:20:14 -05:00
Pharap d5b4a45706 Make both collide functions static 2018-11-13 12:20:14 -05:00
Pharap 1f86208163 Add constructors to Point 2018-11-13 12:19:35 -05:00
Pharap bc63e39931 Add constructors to Rect 2018-11-13 12:15:55 -05:00
Pharap 54d47ec04e
Remove unncessary limits.h includes 2018-09-03 07:37:16 +01:00
Scott Allen 0eae58c760 Fix initRandomSeed(). Minor code and doc changes
The call to generateRandomSeed() in initRandomSeed() was missing
parentheses.

Added keyword for generateRandomSeed.

Minor code changes, and documentation changes and additions.
2018-04-23 15:44:15 -04:00
Pharap 4c2e412985
Introduce generateRandomSeed function 2018-04-18 01:33:30 +01:00
Scott Allen 58ae256412 Change version to 5.1.0 2018-04-07 07:33:28 -04:00
Scott Allen 928c40813d Make sure TX and RX LEDs are off in mainNoUSB() 2018-04-07 07:28:15 -04:00
Scott Allen b8c1cb742a Add EEPROM flag to disable RGB LED with boot logo
Flag SYS_FLAG_SHOW_LOGO_LEDS added to EEPROM_SYS_FLAGS.
The boot logo functions check the flag and leave the RGB LED off if
the flag isn't set.

Added functions writeShowBootLogoLEDsFlag() and
readShowBootLogoLEDsFlag() for writing and reading the
SYS_FLAG_SHOW_LOGO_LEDS flag.

Example sketch SetSystemEEPROM modified to allow setting the
SYS_FLAG_SHOW_LOGO_LEDS flag.
2018-04-07 07:09:49 -04:00
Scott Allen 15db34cab2 Change version to 5.0.0 2018-03-22 08:11:24 -04:00
Scott Allen 674ac8a774 Don't disable timer 0 for Arduboy core 2018-03-22 08:11:24 -04:00
Scott Allen 9a13011474 Use display() in begin() instead of blank()
This saves some code since display() is used multiple times but
blank() normally isn't.
2018-03-22 08:11:24 -04:00
Scott Allen a43e745250 Add system EEPROM flag to disable the boot logo
Flag SYS_FLAG_SHOW_LOGO added to EEPROM_SYS_FLAGS. The boot logo
functions check the flag and bypass the logo sequence if the flag
isn't set.

Added functions writeShowBootLogoFlag() and readShowBootLogoFlag()
for writing and reading the SYS_FLAG_SHOW_LOGO flag.

Renamed example sketch SetNameAndID to SetSystemEEPROM and added
the ability to set the SYS_FLAG_SHOW_LOGO flag, plus menus to reset
the system and user EEPROM areas.
2018-03-22 08:11:24 -04:00
Delio Brignoli da2c7e3d35 Add SpritesB class as alternative to Sprites
The SpritesB class has functions identical to those in Sprites.
When used in place of Sprites it will usually reduce code size (at the
expense of slower execution speed). Code size reduction is accomplished
by factoring all draw modes into the same loop.

Modifications made to the Sprites class to create this class made by
@dxxb (Delio Brignoli).

Creation of the SpritesB class and documentation by
@MLXXXp (Scott Allen).

Also, bootLogoSpritesBSelfMasked() and bootLogoSpritesBOverwrite()
alternative boot logo functions added by @MLXXXp.
2018-03-22 08:11:24 -04:00
Scott Allen 53b50a9766 Add some previously missed keywords 2018-03-22 08:11:07 -04:00
Scott Allen d76eb15c51 Explicitly specify the type in doxygen code blocks
Just to be safe.
2018-03-11 05:40:44 -04:00
Scott Allen c00fee0a78 Add the ability to eliminate the USB stack code
Macro ARDUBOY_NO_USB will provide a substitute main() which will cause
the compiler to leave out the USB code. The macro also adds a check
for the DOWN button being pressed and, if so, will call the new
exitToBootloader() function.

New function exitToBootloader() will invoke the bootloader in command
mode, similar to pressing reset.
2018-03-11 05:40:44 -04:00
Scott Allen fb77929126 Refactor setRGBled() and add freeRGBled()
- setRGBled() has been rewritten to directly control the hardware
  instead of using the Arduino analogWrite() function.

- Added a two parameter version of setRGBled() that sets the brightness
  of one LED without affecting the others.

- Added function freeRGBled() for freeing the PWM control of the LEDs
  so they can be used digitally.

- Added example sketch RGBled.
2018-03-11 05:40:44 -04:00
Scott Allen eb041d24f8 Add function to wait for all buttons released
New function waitNoButtons() replaces the code at the end of begin()
that waits for all buttons to be released. This is to make it easier
to add back this functionality when using boot() in place of begin().
2018-03-11 05:40:44 -04:00
Scott Allen 53ea8188d5 Add Beep classes for playing simple tones
Also added a BeepDemo example sketch and modified the ArduBreakout
example sketch to use the BeepPin1 class instead of Arduino tone().
2018-03-11 05:40:44 -04:00
Scott Allen 460e768ea9 Refactor nextFrame() and add setFrameDuration()
nextFrame() changes were in colaboration with @MrBlinky

setFrameDuration() can be used as an alternative to setFrameRate()
2018-03-11 05:40:44 -04:00
Mr.Blinky 4788b1ab01 Refactor buttonsState()
By @MrBlinky
With some source code formatting and cleanup by @MLXXXp (Scott Allen).
2018-03-11 05:39:57 -04:00
Scott Allen d356be4e13 Refactor and speed up bootLogo functions 2018-03-04 13:51:37 -05:00
Scott Allen 6e1c47cbe0 Refactor idle() to reduce code size 2018-03-04 13:42:17 -05:00
Mr.Blinky bd726fc112 Refactor and optimise paintScreen() in assembler
By @MrBlinky with contributions from @veritazz (Michael Gollnick) and
@MLXXXp (Scott Allen).
2018-03-04 13:31:43 -05:00
Scott Allen d44b89e01f Minor source format changes for drawCompressed()
No code changes from previous commit.
2018-01-17 13:56:25 -05:00
Pharap 6f6849a5bb Reduce drawCompressed progmem usage (#19)
Reduce drawCompressed() progmem usage
2018-01-17 13:51:34 -05:00
Scott Allen f8f46de06d
Merge pull request #18 from eried/master
Fix typo in the code example for everyXFrames()
2017-12-19 09:43:08 -05:00
Erwin Ried d6336803a2 Small typo on the code example 2017-12-19 15:33:01 +01:00
Scott Allen 93c33f6ad6 Change characters used for Sprites examples 2017-12-10 09:13:11 -05:00
Scott Allen 5e58d9b6a8 Fix minor grammar error in Sprites documentation 2017-12-09 22:48:59 -05:00
Scott Allen b159b3751b Add info on sketch EEPROM use to README.md 2017-12-01 16:57:15 -05:00
Scott Allen 6360eb2b37 modify library.json to conform to documentation
Put the exclude field under the export object.
2017-10-02 15:46:46 -04:00
Scott Allen 383bdfc144 fix and clarify Sprite assembly comments 2017-08-28 21:31:10 -04:00
Scott Allen 420b958208 Merge branch 'master' of https://github.com/MLXXXp/Arduboy2 2017-08-28 21:13:10 -04:00
Scott Allen 5370409875 fix documentation comments for initRandomSeed() 2017-08-28 21:09:13 -04:00
Scott Allen 6416166ae7 Merge pull request #15 from yyyc514/sprite_register_fix
fix random display glitches with sprite rendering
2017-08-28 21:03:51 -04:00
Josh Goebel 4478b1fe0a fix random display glitches with sprite rendering
Sometimes the compiler would assign r28/29 to one of our input
variables despite our needing to use that register ourselves. This
fix attempts to prevent that from happening and also cleans up a
few other small details.

- remove unnecessary y_count (-1 register)
- set buffer_ofs_2 inside assembly (-1 register)
- require inputs/outputs to use lower or simple upper registers
2017-08-26 14:55:02 -04:00
Scott Allen 1bb4726ed0 Change version to 4.1.0 2017-07-31 17:10:21 -04:00
Scott Allen fe30863dbe Add complimentary "get" for text "set" functions
Added functions getTextColor(), getTextBackground(),
getTextSize() and getTextWrap() to the Arduboy2 class
2017-07-31 16:57:28 -04:00
Scott Allen 6ab6b83e2c Fix some small errors in documentation comments 2017-07-31 11:56:10 -04:00
Scott Allen fd614ab586 Change version to 4.0.2 2017-07-07 11:38:02 -04:00
Scott Allen e6e82bf336 Fix initialisation of Right button for DevKit 2017-07-07 11:30:48 -04:00
Scott Allen 46ecb2dd78 Change version to 4.0.1 2017-06-16 18:44:44 -04:00
Scott Allen b121d19b77 Refactor fillScreen() 2017-06-16 18:37:19 -04:00
Scott Allen bd5b470294 Change drawPixel() to avoid Z reg corruption
Also use _BV() to specify bit masks in the bitshift_left array,
and other minor formatting changes
2017-06-16 18:05:19 -04:00
Scott Allen 6074f9fc56 Merge pull request #14 from Pharap/master
Fix bug in drawPixel asm
2017-06-16 17:59:03 -04:00
Pharap 427c1d53f2 Fix bug in drawPixel asm 2017-06-14 19:31:54 +01:00
46 changed files with 15343 additions and 2227 deletions

30
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,30 @@
# Commit message style
When submitting changes via a pull request, or any other means, please format commit messages using ["50/72" guidelines, as suggested by Tim Pope](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
## Summary:
- Maximum 50 character first "title" line, written as a capitalized [imperative sentence](http://examples.yourdictionary.com/imperative-sentence-examples.html).
- If necessary, more detailed explanatory paragraphs following a blank line, with lines wrapped at maximum 72 characters.
- If used, bullet points have a "hanging indent".
## Example 1:
```
Make state variables used by pollButtons() public
The variables currentButtonState and previousButtonState used by
pollButtons(), justPressed() and justReleased() have been made public.
This allows them to be manipulated if circumstances require it.
The documentation added for previousButtonState includes an example.
```
## Example 2:
```
Refactor text code and add char size functions
- Functions write() and drawChar() were refactored for smaller code
size and to make text wrapping operate more like what would normally
be expected.
- A new flag variable, textRaw, has been added, along with functions
setTextRawMode() and getTextRawMode() to set and read it.
```

View File

@ -8,7 +8,7 @@ Software License Agreements
Licensed under the BSD 3-clause license: Licensed under the BSD 3-clause license:
Arduboy2 library: Arduboy2 library:
Copyright (c) 2016-2017, Scott Allen Copyright (c) 2016-2021, Scott Allen
All rights reserved. All rights reserved.
The Arduboy2 library was forked from the Arduboy library: The Arduboy2 library was forked from the Arduboy library:
@ -18,13 +18,14 @@ Copyright (c) 2016, Chris Martinez
Copyright (c) 2016, Josh Goebel Copyright (c) 2016, Josh Goebel
Copyright (c) 2016, Scott Allen Copyright (c) 2016, Scott Allen
All rights reserved. All rights reserved.
which is in turn partially based on the Adafruit_SSD1306 library
- which is in turn partially based on the Adafruit_SSD1306 library
https://github.com/adafruit/Adafruit_SSD1306 https://github.com/adafruit/Adafruit_SSD1306
Copyright (c) 2012, Adafruit Industries Copyright (c) 2012, Adafruit Industries
All rights reserved. All rights reserved.
SetNameAndID example sketch: SetSystemEEPROM example sketch:
Copyright (c) 2017, Scott Allen Copyright (c) 2018-2020, Scott Allen
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@ -87,7 +88,6 @@ https://github.com/yyyc514/ArduboyExtra
Copyright (c) 2015 Josh Goebel Copyright (c) 2015 Josh Goebel
Code for drawing compressed bitmaps: Code for drawing compressed bitmaps:
https://github.com/TEAMarg/drawCompressed
Copyright (c) 2016 TEAM a.r.g. Copyright (c) 2016 TEAM a.r.g.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
@ -134,6 +134,168 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details. Lesser General Public License for more details.
-------------------------------------------------------------------------------
Licensed under the zlib license:
LodePNG
https://github.com/lvandeve/lodepng
Copyright (c) 2005-2020 Lode Vandevenne
(Used by the Cabi program)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
-------------------------------------------------------------------------------
Placed in the public domain using Creative Commons CC0:
https://creativecommons.org/publicdomain/zero/1.0/
BeepDemo example sketch:
By Scott Allen
FontDemo example sketch:
By Scott Allen
RGBled example sketch:
By Scott Allen
Cabi PNG file converter program:
By zep
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
=============================================================================== ===============================================================================
\endverbatim \endverbatim
*/ */

194
README.md
View File

@ -4,7 +4,7 @@ The Arduboy2 library is maintained in a git repository hosted on [GitHub](https:
https://github.com/MLXXXp/Arduboy2 https://github.com/MLXXXp/Arduboy2
The **Arduboy2** library is a fork of the [Arduboy library](https://github.com/Arduboy/Arduboy), which provides a standard *application programming interface* (API) to the display, buttons and other hardware of the Arduino based [Arduboy miniature game system](https://www.arduboy.com/). The **Arduboy2** library is a fork of the [Arduboy library](https://github.com/Arduboy/Arduboy), which provides a standard *application programming interface* (API) to the display, buttons and other hardware of the Arduino based [Arduboy miniature game system](https://www.arduboy.com/). The original *Arduboy* library is no longer being maintained.
The name *Arduboy2* doesn't indicate that it's for a new "next generation" of the Arduboy hardware. The name was changed so it can coexist in the Arduino IDE with the current *Arduboy* library, without conflict. This way, existing sketches can continue to use the *Arduboy* library and class, without changes, while new sketches can be written (or old ones modified) to use and take advantage of the capabilities of the *Arduboy2* class and library. The name *Arduboy2* doesn't indicate that it's for a new "next generation" of the Arduboy hardware. The name was changed so it can coexist in the Arduino IDE with the current *Arduboy* library, without conflict. This way, existing sketches can continue to use the *Arduboy* library and class, without changes, while new sketches can be written (or old ones modified) to use and take advantage of the capabilities of the *Arduboy2* class and library.
@ -12,7 +12,7 @@ For notes on the differences between the *Arduboy2* library and the original *Ar
## Library documentation ## Library documentation
Comments in the library header files are formatted for the [Doxygen](http://www.doxygen.org) document generation system. The HTML files generated using the configuration file _extras/Doxyfile_ can be found at: Comments in the library header files are formatted for the [Doxygen](https://www.doxygen.nl/) document generation system. The HTML files generated using the configuration file _extras/Doxyfile_ can be found at:
https://MLXXXp.github.io/documents/Arduino/libraries/Arduboy2/Doxygen/html/index.html https://MLXXXp.github.io/documents/Arduino/libraries/Arduboy2/Doxygen/html/index.html
@ -41,13 +41,16 @@ The *begin()* function, used to initialize the library, includes features that a
At the start of the sketch, the **ARDUBOY** logo scrolls down from the top of the screen to the center. At the start of the sketch, the **ARDUBOY** logo scrolls down from the top of the screen to the center.
The RGB LED lights red then green then blue while the logo is scrolling. (If your Arduboy is one of those that has the RGB LED installed incorrectly, then it will light blue then off then red). The RGB LED lights red then green then blue while the logo is scrolling. (If your Arduboy is one of those that has the RGB LED installed incorrectly, then it will light blue then off then red). For users who do not wish to have the RGB LED flash during the boot logo sequence, a flag can be set in system EEPROM to have it remain off. The included *SetSystemEEPROM* example sketch can be used to set this flag.
A user settable *unit name* of up to 6 characters can be saved in system EEPROM memory. If set, this name will be briefly displayed at the bottom of the boot logo screen, after the logo stops scrolling down. This feature is only available if the *Arduboy2* class is used, not the *Arduboy2Base* class. This is because it requires the text display functions, which are only available in the *Arduboy2* class. A user settable *unit name* can be saved in system EEPROM memory. If set, this name will be briefly displayed at the bottom of the boot logo screen, after the logo stops scrolling down. This feature is only available if the *Arduboy2* class is used, not the *Arduboy2Base* class. This is because it requires the text display functions, which are only available in the *Arduboy2* class. A flag in system EEPROM controls whether or not the *unit name* is displayed on the boot logo screen, regardless of whether the *unit name* itself has been set. The included *SetSystemEEPROM* example sketch can be used to set both the *unit name* and this flag.
Once the logo display sequence completes, the sketch continues. Once the logo display sequence completes, the sketch continues.
For developers who wish to quickly begin testing, or impatient users who want to go strait to playing their game, the boot logo sequence can be bypassed by holding the *RIGHT* button while powering up, and then releasing it. Alternatively, the *RIGHT* button can be pressed while the logo is scrolling down. **Note:**
- For developers who wish to quickly begin testing, or impatient users who want to go strait to playing their game, the boot logo sequence can be bypassed by holding the *RIGHT* button while powering up, and then releasing it. Alternatively, the *RIGHT* button can be pressed while the logo is scrolling down.
- For users who wish to always disable the displaying of the boot logo sequence on boot up, a flag in system EEPROM is available for this. The included *SetSystemEEPROM* example sketch can be used to set this flag.
### "Flashlight" mode ### "Flashlight" mode
@ -105,7 +108,11 @@ Sample sketches have been included with the library as examples of how to use it
`File > Examples > Arduboy2` `File > Examples > Arduboy2`
More information on writing sketches for the Arduboy can be found in the [Arduboy Community Forum](http://community.arduboy.com/). More information on writing sketches for the Arduboy can be found in the [Arduboy Community Forum](https://community.arduboy.com/).
### Using EEPROM in a sketch <- THIS IS IMPORTANT!
The Arduboy2 library reserves an area at the start of EEPROM for storing system information, such as the current audio mute state and the Unit Name and Unit ID. A sketch **MUST NOT** use this reserved area for its own purposes. A sketch may use any EEPROM past this reserved area. The first EEPROM address available for sketch use is given as the defined value *EEPROM_STORAGE_SPACE_START*
### Audio control functions ### Audio control functions
@ -125,15 +132,23 @@ Arduboy2 arduboy;
arduboy.audio.off(); arduboy.audio.off();
``` ```
### Simple tone generation
The *BeepPin1* and *BeepPin2* classes are available to generate simple square wave tones using speaker pin 1 and speaker pin 2 respectively. These classes are documented in file *Arduboy2Beep.h*. Also, *BeepDemo* is included as one of the example sketches, which demonstrates basic use.
NOTE: These functions will not work with a DevKit Arduboy because the speaker pins used cannot be directly controlled by a timer/counter. "Dummy" functions are provided so a sketch will compile and work properly but no sound will be produced.
### Ways to make more code space available to sketches ### Ways to make more code space available to sketches
#### Sound effects and music #### Sound effects and music
If you want your sketch to have sound, then using the *ArduboyTones* library will be more code efficient than using *ArduboyPlaytune* or most other sound libraries compatible with the Arduboy. *ArduboyTones* even produces less code than the [Arduino built in *tone()* function](https://www.arduino.cc/en/Reference/Tone). You'll have to decide on the appropriate library or functions you use to generate sound, based on the features required and how much memory you want it to use. If all you want is to play single tones, using the built in *BeepPin1* or *BeepPin2* classes will be very efficient.
If you want to be able to play sequences of tones or background music, using the [*ArduboyTones*](https://github.com/MLXXXp/ArduboyTones) library will be more code efficient than using [*ArduboyPlaytune*](https://github.com/Arduboy/ArduboyPlayTune) or most other sound libraries compatible with the Arduboy. *ArduboyTones* even produces less code than the [Arduino built in *tone()* function](https://www.arduino.cc/en/Reference/Tone). You'll have to decide on the appropriate library or functions you use to generate sound, based on the features required and how much memory you want it to use.
#### Remove the text functions #### Remove the text functions
If your sketch doesn't use any of the functions for displaying text, such as *setCursor()* and *print()*, you can remove them. You could do this if your sketch generates whatever text it requires by some other means. Removing the text functions frees up code by not including the font table and some code that is always pulled in by inheriting the [Arduino *Print* class](http://playground.arduino.cc/Code/Printclass). If your sketch doesn't use any of the functions for displaying text, such as *setCursor()* and *print()*, you can remove them. You could do this if your sketch generates whatever text it requires by some other means. Removing the text functions frees up code by not including the font table and some code that is always pulled in by inheriting the [Arduino *Print* class](https://playground.arduino.cc/Code/Printclass/).
To eliminate text capability in your sketch, when creating the library object simply use the *Arduboy2Base* class instead of *Arduboy2*: To eliminate text capability in your sketch, when creating the library object simply use the *Arduboy2Base* class instead of *Arduboy2*:
@ -151,74 +166,125 @@ with
Arduboy2Base arduboy; Arduboy2Base arduboy;
``` ```
#### Remove boot up features #### Substitute or remove boot up features
As previously described, the *begin()* function includes features that are intended to be available to all sketches during boot up. However, if you're looking to gain some code space, you can call *boot()* instead of *begin()*. This will initialize the system but not include any of the extra boot up features. If desired, you can then add back in any of these features by calling the functions that perform them. You will have to trade off between the desirability of having a feature and how much memory you can recover by not including it. As previously described in the _Start up features_ section, the *begin()* function includes features that are intended to be available to all sketches during boot up. However, if you're looking to gain some code space, you can call *boot()* instead of *begin()*. This will initialize the system but not include any of the extra boot up features. You can then add back in any of these features by calling the functions that perform them. You will have to trade off between the desirability of having a feature and how much memory you can recover by not including it.
A good way to use *boot()* instead of *begin()* is to copy the code from the body of the *begin()* function, in file *Arduboy2.cpp*, into your sketch and then edit it to retain the *boot()* call and any feature calls desired. You should at least call either *flashlight()* or *safeMode()* as a safeguard to allow uploading a new sketch when the bootloader "magic key" problem is an issue.
As of this writing, the begin function is: Here is a template that provides the equivalent of *begin()*
```cpp ```cpp
void Arduboy2Base::begin() void setup()
{ {
boot(); // raw hardware // Required to initialize the hardware.
arduboy.boot();
blank(); // blank the display // This clears the display. (The screen buffer will be all zeros)
// It may not be needed if something clears the display later on but
// "garbage" will be displayed if systemButtons() is used without it.
arduboy.display();
flashlight(); // light the RGB LED and screen if UP button is being held. // flashlight() or safeMode() should always be included to provide
// a method of recovering from the bootloader "magic key" problem.
arduboy.flashlight();
// arduboy.safeMode();
// check for and handle buttons held during start up for system control // This allows sound to be turned on or muted. If the sketch provides
systemButtons(); // its own way of toggling sound, or doesn't produce any sound, this
// function may not be required.
arduboy.systemButtons();
audio.begin(); // This is required to initialize the speaker. It's not needed if
// the sketch doesn't produce any sounds.
arduboy.audio.begin();
bootLogo(); // This displays the boot logo sequence but note that the logo can
// be suppressed by the user, by pressing the RIGHT button or using
// a system EEPROM setting. If not removed entirely, an alternative
// bootLogo...() function may save some memory.
arduboy.bootLogo();
// arduboy.bootLogoCompressed();
// arduboy.bootLogoSpritesSelfMasked();
// arduboy.bootLogoSpritesOverwrite();
// arduboy.bootLogoSpritesBSelfMasked();
// arduboy.bootLogoSpritesBOverwrite();
// arduboy.bootLogoText();
// Wait for all buttons to be released, in case a pressed one might
// cause problems by being acted upon when the actual sketch code
// starts. If neither systemButtons() nor bootLogo() is kept, this
// function isn't required.
arduboy.waitNoButtons();
// Additional setup code...
// wait for all buttons to be released
do {
delay(50);
} while (buttonsState());
} }
``` ```
To incorporate it into your sketch just keep *boot()* and whatever feature calls are desired, if any. Comment out or delete the rest. Remember to add the class object name in front of each function call, since they're now being called from outside the class itself. If your sketch uses sound, it's a good idea to keep the call to *audio.begin()*.
For example: Let's say a sketch has its own code to enable, disable and save the *audio on/off* setting, and wants to keep the *flashlight* function. In *setup()* it could replace *begin()* with:
```cpp
arduboy.boot(); // raw hardware
// *** This particular sketch clears the display soon, so it doesn't need this:
// blank(); // blank the display
arduboy.flashlight(); // light the RGB LED and screen if UP button is being held.
// check for and handle buttons held during start up for system control
// systemButtons();
arduboy.audio.begin();
// bootLogo();
// wait for all buttons to be released
do {
delay(50);
} while (arduboy.buttonsState());
```
This saves whatever code *blank()*, *systemButtons()* and *bootLogo()* would use.
There are a few functions provided that are roughly equivalent to the standard functions used by *begin()* but which use less code space. There are a few functions provided that are roughly equivalent to the standard functions used by *begin()* but which use less code space.
- *bootLogoCompressed()*, *bootLogoSpritesSelfMasked()* and *bootLogoSpritesOverwrite()* will do the same as *bootLogo()* but will use *drawCompressed()*, or *Sprites* class *drawSelfMasked()* or *drawOverwrite()*, functions respectively, instead of *drawBitmask()*, to render the logo. If the sketch uses one of these functions, then using the boot logo function that also uses it may reduce code size. It's best to try each of them to see which one produces the smallest size. - *bootLogoCompressed()*, *bootLogoSpritesSelfMasked()*, *bootLogoSpritesOverwrite()*, *bootLogoSpritesBSelfMasked()* and *bootLogoSpritesBOverwrite()* will do the same as *bootLogo()* but will use *drawCompressed()*, or *Sprites* / *SpritesB* class *drawSelfMasked()* or *drawOverwrite()* functions respectively, instead of *drawBitmap()*, to render the logo. If the sketch uses one of these functions, then using the boot logo function that also uses it may reduce code size. It's best to try each of them to see which one produces the smallest size.
- *bootLogoText()* can be used in place *bootLogo()* in the case where the sketch uses text functions. It renders the logo as text instead of as a bitmap (so doesn't look as good). - *bootLogoText()* can be used in place *bootLogo()* in the case where the sketch uses text functions. It renders the logo as text instead of as a bitmap (so doesn't look as good).
- *safeMode()* can be used in place of *flashlight()* for cases where it's needed to allow uploading a new sketch when the bootloader "magic key" problem is an issue. It only lights the red RGB LED, so you don't get the bright light that is the primary purpose of *flashlight()*. - *safeMode()* can be used in place of *flashlight()* as a safeguard to allow uploading a new sketch when the bootloader "magic key" problem is an issue. It only lights the red RGB LED, so you don't get the bright light that is the primary purpose of *flashlight()*.
It is also possible to replace the boot logo drawing function with one that uses a different bitmap rendering function used elsewhere in your sketch. This may save memory by using this bitmap function for the logo, instead of the logo using a separate function that only ends up being used once. For example, if you use the *ArdBitmap* library's *drawCompressed()* function in your sketch, you could convert the **ARDUBOY** logo to *Ardbitmap* compressed format, and create *drawLogoArdCompressed()* and *bootLogoArdCompressed()* functions:
```cpp
void drawLogoArdCompressed(int16_t y)
{
ardbitmap.drawCompressed(20, y, arduboy_logo_ardbitmap,
WHITE, ALIGN_CENTER, MIRROR_NONE);
}
void bootLogoArdCompressed()
{
if (arduboy.bootLogoShell(drawLogoArdCompressed))
{
arduboy.bootLogoExtra();
}
}
void setup()
{
arduboy.beginDoFirst();
bootLogoArdCompressed();
arduboy.waitNoButtons();
// Additional setup code...
}
```
The **ARDUBOY** logo, in PNG format, is included in the library repository as file:
`extras/assets/arduboy_logo.png`
#### Use the SpritesB class instead of Sprites
The *SpritesB* class has functions identical to the *Sprites* class. The difference is that *SpritesB* is optimized for small code size rather than execution speed. If you want to use the sprites functions, and the slower speed of *SpritesB* doesn't affect your sketch, you may be able to use it to gain some code space.
Even if the speed is acceptable when using *SpritesB*, you should still try using *Sprites*. In some cases *Sprites* will produce less code than *SpritesB*, notably when only one of the functions is used.
You can easily switch between using *Sprites* or *SpritesB* by using one or the other to create an object instance:
```cpp
Sprites sprites; // Use this to optimize for execution speed
SpritesB sprites; // Use this to (likely) optimize for code size
```
#### Eliminate the USB stack code
**Warning:** Although this will free up a fair amount of code and some RAM space, without an active USB interface uploader programs will be unable to automatically force a reset to invoke the bootloader. This means the user will have to manually initiate a reset in order to upload a new sketch. This can be an inconvenience or even frustrating for a user, due to the fact that timing the sequence can sometimes be tricky. Therefore, using this technique should be considered as a last resort. If it is used, the sketch documentation should state clearly what will be involved to upload a new sketch.
The *ARDUBOY_NO_USB* macro is used to eliminate the USB code. The *exitToBootloader()* function is available to make it easier for a user to invoke the bootloader. For more details, see the documentation provided for these.
---------- ----------
## What's different from Arduboy library V1.1 ## What's different from Arduboy library V1.1
(These notes apply to when the *Arduboy2* library was first released. There will have been many additional changes, enhancements and features added to *Arduboy2* since then.)
A main goal of Arduboy2 is to provide ways in which more code space can be freed for use by large sketches. Another goal is to allow methods other than the *tunes* functions to be used to produce sounds. Arduboy2 remains substantially compatible with [Arduboy library V1.1](https://github.com/Arduboy/Arduboy/releases/tag/v1.1), which was the latest stable release at the time of the fork. Arduboy2 is based on the code targeted for Arduboy library V1.2, which was still in development and unreleased at the time it was forked. A main goal of Arduboy2 is to provide ways in which more code space can be freed for use by large sketches. Another goal is to allow methods other than the *tunes* functions to be used to produce sounds. Arduboy2 remains substantially compatible with [Arduboy library V1.1](https://github.com/Arduboy/Arduboy/releases/tag/v1.1), which was the latest stable release at the time of the fork. Arduboy2 is based on the code targeted for Arduboy library V1.2, which was still in development and unreleased at the time it was forked.
Main differences between Arduboy2 and Arduboy V1.1 are: Main differences between Arduboy2 and Arduboy V1.1 are:
@ -235,7 +301,7 @@ Main differences between Arduboy2 and Arduboy V1.1 are:
- The *beginNoLogo()* function is not included. This function could be used in Arduboy V1.1 in place of *begin()* to suppress the displaying of the ARDUBOY logo and thus free up the code that it required. Instead, Arduboy2 allows a sketch to call *boot()* and then add in any extra features that *begin()* provides by calling their functions directly after *boot()*, if desired. - The *beginNoLogo()* function is not included. This function could be used in Arduboy V1.1 in place of *begin()* to suppress the displaying of the ARDUBOY logo and thus free up the code that it required. Instead, Arduboy2 allows a sketch to call *boot()* and then add in any extra features that *begin()* provides by calling their functions directly after *boot()*, if desired.
- The *ArduboyCore* and *ArduboyAudio* base classes, previously only available to, and used to derive, the *Arduboy* class, have been made publicly available for the benefit of developers who may wish to use them as the base of an entirely new library. This change doesn't affect the existing API. - The *ArduboyCore* and *ArduboyAudio* base classes, previously only available to, and used to derive, the *Arduboy* class, have been made publicly available for the benefit of developers who may wish to use them as the base of an entirely new library. This change doesn't affect the existing API.
As of version 2.1.0 functionality from the [Team A.R.G.](http://www.team-arg.org/) *Arglib* library has been added: As of version 2.1.0 functionality from the Team A.R.G. *Arglib* library has been added:
- The sprite drawing functions, collision detection functions, and button handling functions that Team A.R.G. incorporated from the [ArduboyExtra](https://github.com/yyyc514/ArduboyExtra) project. The *poll()* function was renamed *pollButtons()* for clarity. The *Sprites* class doesn't require a parameter for the constructor, whereas in *Arglib* a pointer to an Arduboy class object is required. - The sprite drawing functions, collision detection functions, and button handling functions that Team A.R.G. incorporated from the [ArduboyExtra](https://github.com/yyyc514/ArduboyExtra) project. The *poll()* function was renamed *pollButtons()* for clarity. The *Sprites* class doesn't require a parameter for the constructor, whereas in *Arglib* a pointer to an Arduboy class object is required.
- The *drawCompressed()* function, which allows compressed bitmaps to be drawn. Saving bitmaps in compressed form may reduce overall sketch size. - The *drawCompressed()* function, which allows compressed bitmaps to be drawn. Saving bitmaps in compressed form may reduce overall sketch size.
@ -278,11 +344,11 @@ Arduboy2 arduboy;
If the sketch doesn't use any *tunes* functions, there's a good chance this is all that has to be done to make it compile. If the sketch doesn't use any *tunes* functions, there's a good chance this is all that has to be done to make it compile.
### Sketch uses only *tunes.tone()* for sound ### Sketch uses only tunes.tone() for sound
If the sketch has sound but only uses *tunes.tone()*, solutions are: If the sketch has sound but only uses *tunes.tone()*, solutions are:
#### Solution 1: Switch to using Arduino *tone()* #### Solution 1: Switch to using Arduino tone()
An easy change is to use the Arduino built in *tone()* function. You can add a function to the sketch that wraps *tone()* so that it works like *tunes.tone()*, like so: An easy change is to use the Arduino built in *tone()* function. You can add a function to the sketch that wraps *tone()* so that it works like *tunes.tone()*, like so:
@ -345,7 +411,7 @@ See the [ArduboyTones](https://github.com/MLXXXp/ArduboyTones) README file for m
See the following for how to do this: See the following for how to do this:
### Sketch uses *tunes.playScore()* ### Sketch uses tunes.playScore()
If the sketch uses *tunes.playScore()*, probably the easiest solution is to use the *ArduboyPlaytune* library. *ArduboyPlaytune* is essentially the code that was in the Arduboy V1.1 *tunes* subclass, which has been removed from Arduboy2. It's been cleaned up and a few enhancements have been added, but all the Arduboy V1.1 *tunes* functions are available. If the sketch uses *tunes.playScore()*, probably the easiest solution is to use the *ArduboyPlaytune* library. *ArduboyPlaytune* is essentially the code that was in the Arduboy V1.1 *tunes* subclass, which has been removed from Arduboy2. It's been cleaned up and a few enhancements have been added, but all the Arduboy V1.1 *tunes* functions are available.
@ -362,7 +428,7 @@ Arduboy2 arduboy;
ArduboyPlaytune tunes(arduboy.audio.enabled); ArduboyPlaytune tunes(arduboy.audio.enabled);
``` ```
The sound channels must then be initialzed and assigned to the speaker pins. This code would go in the *setup()* function: The sound channels must then be initialized and assigned to the speaker pins. This code would go in the *setup()* function:
```cpp ```cpp
// audio setup // audio setup
@ -395,15 +461,15 @@ If you don't need to play scores containing two parts, and don't require tones t
The benefit of using *ArduboyTones* would be reduced code size and possibly easier addition of new sequences without the need of a MIDI to Playtune format converter. The benefit of using *ArduboyTones* would be reduced code size and possibly easier addition of new sequences without the need of a MIDI to Playtune format converter.
### Sketch uses the *beginNoLogo()* function instead of *begin()* ### Sketch uses the beginNoLogo() function instead of begin()
The *beginNoLogo()* function has been removed. Instead, *boot()* can be used with additional functions following it to add back in desired boot functionality. See the information above, under the heading *Remove boot up features*, for more details. Assuming the object is named *arduboy*, a direct replacement for *beginNoLogo()* would be: The *beginNoLogo()* function has been removed. *beginNoLogo()* can be replaced with *begin()*, since users can choose to suppress the logo sequence using the *RIGHT* button or by setting a flag in system EEPROM.
If using *begin()* results in the sketch program memory size being too large, *beginDoFirst()* or *boot()* can be used with additional functions following it to add back in desired boot functionality. See the information above, under the heading *Substitute or remove boot up features*, for more details. Assuming the object is named *arduboy*, an equivalent replacement for *beginNoLogo()* would be:
```cpp ```cpp
arduboy.boot(); arduboy.beginDoFirst();
arduboy.blank(); arduboy.waitNoButtons();
arduboy.flashlight();
arduboy.audio.begin();
``` ```
---------- ----------

View File

@ -3,7 +3,8 @@
Copyright (C) 2011 Sebastian Goscik Copyright (C) 2011 Sebastian Goscik
All rights reserved. All rights reserved.
Modifications by Scott Allen 2016 (after previous changes by ???) Modifications by Scott Allen 2016, 2018, 2020
after previous changes by person(s) unknown.
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -16,18 +17,38 @@
// block in EEPROM to save high scores // block in EEPROM to save high scores
#define EE_FILE 2 #define EE_FILE 2
Arduboy2 arduboy; // EEPROM space used: 35 bytes (7*(3+2)) starting at
// EEPROM_STORAGE_SPACE_START + (EE_FILE * 35)
Arduboy2 arduboy;
BeepPin1 beep;
constexpr uint8_t frameRate = 40; // Frame rate in frames per second
// Tone frequencies. Converted to count values for the beep class
constexpr uint16_t tonePaddle = beep.freq(200); // Ball hits paddle
constexpr uint16_t toneBrick = beep.freq(261); // Ball hits a brick
constexpr uint16_t toneEdge = beep.freq(523); // Ball hits top or sides
constexpr uint16_t toneMiss = beep.freq(175); // Ball misses paddle, lose life
constexpr uint16_t toneInitialsChange = beep.freq(523); // Change initials
constexpr uint16_t toneInitialsMove = beep.freq(1046); // Select initials
// Tone durations
constexpr uint8_t toneTimeBeep = 250 / (1000 / frameRate); // Game (frames)
constexpr uint16_t toneTimeMiss = 500; // Miss paddle (milliseconds)
constexpr uint16_t toneTimeInitials = 80; // Initials entry (milliseconds)
constexpr unsigned int columns = 13; //Columns of bricks
constexpr unsigned int rows = 4; //Rows of bricks
const unsigned int COLUMNS = 13; //Columns of bricks
const unsigned int ROWS = 4; //Rows of bricks
int dx = -1; //Initial movement of ball int dx = -1; //Initial movement of ball
int dy = -1; //Initial movement of ball int dy = -1; //Initial movement of ball
int xb; //Balls starting possition int xb; //Ball's starting position
int yb; //Balls starting possition int yb; //Ball's starting position
boolean released; //If the ball has been released by the player boolean released; //If the ball has been released by the player
boolean paused = false; //If the game has been paused boolean paused = false; //If the game has been paused
byte xPaddle; //X position of paddle byte xPaddle; //X position of paddle
boolean isHit[ROWS][COLUMNS]; //Array of if bricks are hit or not boolean isHit[rows][columns]; //Array of if bricks are hit or not
boolean bounced=false; //Used to fix double bounce glitch boolean bounced=false; //Used to fix double bounce glitch
byte lives = 3; //Amount of lives byte lives = 3; //Amount of lives
byte level = 1; //Current level byte level = 1; //Current level
@ -57,7 +78,8 @@ byte tick;
void setup() void setup()
{ {
arduboy.begin(); arduboy.begin();
arduboy.setFrameRate(40); beep.begin();
arduboy.setFrameRate(frameRate);
arduboy.initRandomSeed(); arduboy.initRandomSeed();
} }
@ -67,6 +89,9 @@ void loop()
if (!(arduboy.nextFrame())) if (!(arduboy.nextFrame()))
return; return;
// Handle the timing and stopping of tones
beep.timer();
//Title screen loop switches from title screen //Title screen loop switches from title screen
//and high scores until FIRE is pressed //and high scores until FIRE is pressed
while (!start) while (!start)
@ -86,9 +111,9 @@ void loop()
//Selects Font //Selects Font
//Draws the new level //Draws the new level
level = 1; level = 1;
initialDraw=true;
newLevel(); newLevel();
score = 0; score = 0;
initialDraw=true;
} }
if (lives>0) if (lives>0)
@ -107,7 +132,7 @@ void loop()
oldpad = pad; oldpad = pad;
drawBall(); drawBall();
if(brickCount == ROWS * COLUMNS) if(brickCount == rows * columns)
{ {
level++; level++;
newLevel(); newLevel();
@ -158,12 +183,15 @@ void moveBall()
if(released) if(released)
{ {
//Move ball //Move ball
if (abs(dx)==2) { if (abs(dx)==2)
{
xb += dx/2; xb += dx/2;
// 2x speed is really 1.5 speed // 2x speed is really 1.5 speed
if (tick%2==0) if (tick%2==0)
xb += dx/2; xb += dx/2;
} else { }
else
{
xb += dx; xb += dx;
} }
yb=yb + dy; yb=yb + dy;
@ -179,18 +207,18 @@ void moveBall()
{ {
yb = 2; yb = 2;
dy = -dy; dy = -dy;
playTone(523, 250); playTone(toneEdge, toneTimeBeep);
} }
//Lose a life if bottom edge hit //Lose a life if bottom edge hit
if (yb >= 64) if (yb >= 64)
{ {
arduboy.drawRect(xPaddle, 63, 11, 1, 0); arduboy.drawRect(xPaddle, 63, 11, 1, BLACK);
xPaddle = 54; xPaddle = 54;
yb=60; yb=60;
released = false; released = false;
lives--; lives--;
playTone(175, 250); playToneTimed(toneMiss, toneTimeMiss);
if (random(0, 2) == 0) if (random(0, 2) == 0)
{ {
dx = 1; dx = 1;
@ -206,7 +234,7 @@ void moveBall()
{ {
xb = 2; xb = 2;
dx = -dx; dx = -dx;
playTone(523, 250); playTone(toneEdge, toneTimeBeep);
} }
//Bounce off right side //Bounce off right side
@ -214,7 +242,7 @@ void moveBall()
{ {
xb = WIDTH - 4; xb = WIDTH - 4;
dx = -dx; dx = -dx;
playTone(523, 250); playTone(toneEdge, toneTimeBeep);
} }
//Bounce off paddle //Bounce off paddle
@ -223,16 +251,17 @@ void moveBall()
dy = -dy; dy = -dy;
dx = ((xb-(xPaddle+6))/3); //Applies spin on the ball dx = ((xb-(xPaddle+6))/3); //Applies spin on the ball
// prevent straight bounce // prevent straight bounce
if (dx == 0) { if (dx == 0)
{
dx = (random(0, 2) == 1) ? 1 : -1; dx = (random(0, 2) == 1) ? 1 : -1;
} }
playTone(200, 250); playTone(tonePaddle, toneTimeBeep);
} }
//Bounce off Bricks //Bounce off Bricks
for (byte row = 0; row < ROWS; row++) for (byte row = 0; row < rows; row++)
{ {
for (byte column = 0; column < COLUMNS; column++) for (byte column = 0; column < columns; column++)
{ {
if (!isHit[row][column]) if (!isHit[row][column])
{ {
@ -242,14 +271,14 @@ void moveBall()
topBrick = 6 * row + 1; topBrick = 6 * row + 1;
bottomBrick = 6 * row + 7; bottomBrick = 6 * row + 7;
//If A collison has occured //If A collision has occurred
if (topBall <= bottomBrick && bottomBall >= topBrick && if (topBall <= bottomBrick && bottomBall >= topBrick &&
leftBall <= rightBrick && rightBall >= leftBrick) leftBall <= rightBrick && rightBall >= leftBrick)
{ {
Score(); Score();
brickCount++; brickCount++;
isHit[row][column] = true; isHit[row][column] = true;
arduboy.drawRect(10*column, 2+6*row, 8, 4, 0); arduboy.drawRect(10*column, 2+6*row, 8, 4, BLACK);
//Vertical collision //Vertical collision
if (bottomBall > bottomBrick || topBall < topBrick) if (bottomBall > bottomBrick || topBall < topBrick)
@ -260,11 +289,11 @@ void moveBall()
dy =- dy; dy =- dy;
yb += dy; yb += dy;
bounced = true; bounced = true;
playTone(261, 250); playTone(toneBrick, toneTimeBeep);
} }
} }
//Hoizontal collision //Horizontal collision
if (leftBall < leftBrick || rightBall > rightBrick) if (leftBall < leftBrick || rightBall > rightBrick)
{ {
//Only bounce once brick each ball move //Only bounce once brick each ball move
@ -273,7 +302,7 @@ void moveBall()
dx =- dx; dx =- dx;
xb += dx; xb += dx;
bounced = true; bounced = true;
playTone(261, 250); playTone(toneBrick, toneTimeBeep);
} }
} }
} }
@ -312,59 +341,58 @@ void moveBall()
void drawBall() void drawBall()
{ {
// arduboy.setCursor(0,0); arduboy.drawPixel(xb, yb, BLACK);
// arduboy.print(arduboy.cpuLoad()); arduboy.drawPixel(xb+1, yb, BLACK);
// arduboy.print(" "); arduboy.drawPixel(xb, yb+1, BLACK);
arduboy.drawPixel(xb, yb, 0); arduboy.drawPixel(xb+1, yb+1, BLACK);
arduboy.drawPixel(xb+1, yb, 0);
arduboy.drawPixel(xb, yb+1, 0);
arduboy.drawPixel(xb+1, yb+1, 0);
moveBall(); moveBall();
arduboy.drawPixel(xb, yb, 1); arduboy.drawPixel(xb, yb, WHITE);
arduboy.drawPixel(xb+1, yb, 1); arduboy.drawPixel(xb+1, yb, WHITE);
arduboy.drawPixel(xb, yb+1, 1); arduboy.drawPixel(xb, yb+1, WHITE);
arduboy.drawPixel(xb+1, yb+1, 1); arduboy.drawPixel(xb+1, yb+1, WHITE);
} }
void drawPaddle() void drawPaddle()
{ {
arduboy.drawRect(xPaddle, 63, 11, 1, 0); arduboy.drawRect(xPaddle, 63, 11, 1, BLACK);
movePaddle(); movePaddle();
arduboy.drawRect(xPaddle, 63, 11, 1, 1); arduboy.drawRect(xPaddle, 63, 11, 1, WHITE);
} }
void drawGameOver() void drawGameOver()
{ {
arduboy.drawPixel(xb, yb, 0); arduboy.drawPixel(xb, yb, BLACK);
arduboy.drawPixel(xb+1, yb, 0); arduboy.drawPixel(xb+1, yb, BLACK);
arduboy.drawPixel(xb, yb+1, 0); arduboy.drawPixel(xb, yb+1, BLACK);
arduboy.drawPixel(xb+1, yb+1, 0); arduboy.drawPixel(xb+1, yb+1, BLACK);
arduboy.setCursor(37, 42); arduboy.setCursor(37, 42);
arduboy.print("Game Over"); arduboy.print("Game Over");
arduboy.setCursor(31, 56); arduboy.setCursor(31, 56);
arduboy.print("Score: "); arduboy.print("Score: ");
arduboy.print(score); arduboy.print(score);
arduboy.display(); arduboy.display();
delay(4000); arduboy.delayShort(4000);
} }
void pause() void pause()
{ {
paused = true; paused = true;
//Stop tone if playing
beep.noTone();
//Draw pause to the screen //Draw pause to the screen
arduboy.setCursor(52, 45); arduboy.setCursor(52, 45);
arduboy.print("PAUSE"); arduboy.print("PAUSE");
arduboy.display(); arduboy.display();
while (paused) while (paused)
{ {
delay(150); arduboy.delayShort(150);
//Unpause if FIRE is pressed //Unpause if FIRE is pressed
pad2 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON); pad2 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
if (pad2 == true && oldpad2 == false && released) if (pad2 == true && oldpad2 == false && released)
{ {
arduboy.fillRect(52, 45, 30, 11, 0); arduboy.fillRect(52, 45, 30, 11, BLACK);
paused=false; paused=false;
} }
@ -377,15 +405,16 @@ void Score()
score += (level*10); score += (level*10);
} }
void newLevel(){ void newLevel()
{
//Undraw paddle //Undraw paddle
arduboy.drawRect(xPaddle, 63, 11, 1, 0); arduboy.drawRect(xPaddle, 63, 11, 1, BLACK);
//Undraw ball //Undraw ball
arduboy.drawPixel(xb, yb, 0); arduboy.drawPixel(xb, yb, BLACK);
arduboy.drawPixel(xb+1, yb, 0); arduboy.drawPixel(xb+1, yb, BLACK);
arduboy.drawPixel(xb, yb+1, 0); arduboy.drawPixel(xb, yb+1, BLACK);
arduboy.drawPixel(xb+1, yb+1, 0); arduboy.drawPixel(xb+1, yb+1, BLACK);
//Alter various variables to reset the game //Alter various variables to reset the game
xPaddle = 54; xPaddle = 54;
@ -394,23 +423,31 @@ void newLevel(){
released = false; released = false;
//Draws new bricks and resets their values //Draws new bricks and resets their values
for (byte row = 0; row < 4; row++) { for (byte row = 0; row < 4; row++)
{
for (byte column = 0; column < 13; column++) for (byte column = 0; column < 13; column++)
{ {
isHit[row][column] = false; isHit[row][column] = false;
arduboy.drawRect(10*column, 2+6*row, 8, 4, 1); arduboy.drawRect(10*column, 2+6*row, 8, 4, WHITE);
} }
} }
if (!initialDraw)
{
arduboy.clear();
}
else
{
arduboy.display(); arduboy.display();
} }
}
//Used to delay images while reading button input //Used to delay images while reading button input
boolean pollFireButton(int n) boolean pollFireButton(int n)
{ {
for(int i = 0; i < n; i++) for(int i = 0; i < n; i++)
{ {
delay(15); arduboy.delayShort(15);
pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON); pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
if(pad == true && oldpad == false) if(pad == true && oldpad == false)
{ {
@ -544,18 +581,18 @@ void enterInitials()
arduboy.print(initials[2]); arduboy.print(initials[2]);
for(byte i = 0; i < 3; i++) for(byte i = 0; i < 3; i++)
{ {
arduboy.drawLine(56 + (i*8), 27, 56 + (i*8) + 6, 27, 1); arduboy.drawLine(56 + (i*8), 27, 56 + (i*8) + 6, 27, WHITE);
} }
arduboy.drawLine(56, 28, 88, 28, 0); arduboy.drawLine(56, 28, 88, 28, BLACK);
arduboy.drawLine(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1); arduboy.drawLine(56 + (index*8), 28, 56 + (index*8) + 6, 28, WHITE);
delay(150); arduboy.delayShort(70);
if (arduboy.pressed(LEFT_BUTTON) || arduboy.pressed(B_BUTTON)) if (arduboy.pressed(LEFT_BUTTON) || arduboy.pressed(B_BUTTON))
{ {
if (index > 0) if (index > 0)
{ {
index--; index--;
playTone(1046, 250); playToneTimed(toneInitialsMove, toneTimeInitials);
} }
} }
@ -564,14 +601,14 @@ void enterInitials()
if (index < 2) if (index < 2)
{ {
index++; index++;
playTone(1046, 250); playToneTimed(toneInitialsMove, toneTimeInitials);
} }
} }
if (arduboy.pressed(DOWN_BUTTON)) if (arduboy.pressed(UP_BUTTON))
{ {
initials[index]++; initials[index]++;
playTone(523, 250); playToneTimed(toneInitialsChange, toneTimeInitials);
// A-Z 0-9 :-? !-/ ' ' // A-Z 0-9 :-? !-/ ' '
if (initials[index] == '0') if (initials[index] == '0')
{ {
@ -591,32 +628,37 @@ void enterInitials()
} }
} }
if (arduboy.pressed(UP_BUTTON)) if (arduboy.pressed(DOWN_BUTTON))
{ {
initials[index]--; initials[index]--;
playTone(523, 250); playToneTimed(toneInitialsChange, toneTimeInitials);
if (initials[index] == ' ') { if (initials[index] == ' ')
{
initials[index] = '?'; initials[index] = '?';
} }
if (initials[index] == '/') { if (initials[index] == '/')
{
initials[index] = 'Z'; initials[index] = 'Z';
} }
if (initials[index] == 31) { if (initials[index] == 31)
{
initials[index] = '/'; initials[index] = '/';
} }
if (initials[index] == '@') { if (initials[index] == '@')
{
initials[index] = ' '; initials[index] = ' ';
} }
} }
if (arduboy.pressed(A_BUTTON)) if (arduboy.pressed(A_BUTTON))
{ {
playToneTimed(toneInitialsMove, toneTimeInitials);
if (index < 2) if (index < 2)
{ {
index++; index++;
playTone(1046, 250); }
} else { else
playTone(1046, 250); {
return; return;
} }
} }
@ -643,7 +685,8 @@ void enterHighScore(byte file)
// The values are uninitialized, so treat this entry // The values are uninitialized, so treat this entry
// as a score of 0. // as a score of 0.
tmpScore = 0; tmpScore = 0;
} else }
else
{ {
tmpScore = (hi << 8) | lo; tmpScore = (hi << 8) | lo;
} }
@ -693,12 +736,20 @@ void enterHighScore(byte file)
} }
} }
// Wrap the Arduino tone() function so that the pin doesn't have to be // Play a tone at a frequency corresponding to the specified precomputed count,
// specified each time. Also, don't play if audio is set to off. // for the specified number of frames.
void playTone(unsigned int frequency, unsigned long duration) void playTone(uint16_t count, uint8_t frames)
{ {
if (arduboy.audio.enabled() == true) beep.tone(count, frames);
}
// Play a tone at a frequency corresponding to the specified precomputed count,
// for the specified duration in milliseconds, using a delay.
// Used when beep.timer() isn't being called.
void playToneTimed(uint16_t count, uint16_t duration)
{ {
tone(PIN_SPEAKER_1, frequency, duration); beep.tone(count);
} arduboy.delayShort(duration);
beep.noTone();
} }

View File

@ -0,0 +1,139 @@
/*
BeepDemo.ino
This sketch provides an example of using the Arduboy2 library's BeepPin1 class
to play simple tones.
*/
/*
Written in 2018 by Scott Allen saydisp-git@yahoo.ca
To the extent possible under law, the author(s) have dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
// Comments are only provided for code dealing with tone generation or control.
#include <Arduboy2.h>
// There's no need to #include <Arduboy2Beep.h>
// It will be included in Arduboy2.h
Arduboy2 arduboy;
BeepPin1 beep; // Create a class instance for speaker pin 1
//BeepPin2 beep; // For speaker pin 2, use this line instead of the line above
int objectX = 0;
char displayText[60];
void setup() {
arduboy.begin();
arduboy.setFrameRate(25);
beep.begin(); // Set up the hardware for playing tones
commandText("none - Press buttons");
}
void loop() {
if (!arduboy.nextFrame()) {
return;
}
// The timer() function is called once per frame, so duration values will be
// the number of frames that the tone plays for.
// At 25 frames per second each frame will be 40ms.
beep.timer(); // handle tone duration
arduboy.pollButtons();
arduboy.clear();
if (arduboy.justPressed(LEFT_BUTTON)) {
// Play a 523.251Hz tone (piano note C5) for 5 frames (200ms at 25 FPS)
// beep.freq(523.251) is used to convert 523.251Hz to the required count
beep.tone(beep.freq(523.251), 5);
commandText("beep.tone(\n beep.freq(523.251),\n 5)");
}
if (arduboy.justPressed(UP_BUTTON)) {
// Play a 587.330Hz tone (piano note D5) for 15 frames (600ms at 25 FPS)
beep.tone(beep.freq(587.330), 15);
commandText("beep.tone(\n beep.freq(587.330),\n 15)");
}
if (arduboy.justPressed(RIGHT_BUTTON)) {
// Play a 659.255Hz tone (piano note E5) for 50 frames (2s at 25 FPS)
beep.tone(beep.freq(659.255), 50);
commandText("beep.tone(\n beep.freq(659.255),\n 50)");
}
if (arduboy.justPressed(DOWN_BUTTON)) {
// Play a 698.456Hz tone (piano note F5) until stopped
// or replaced by another tone
beep.tone(beep.freq(698.456));
commandText("beep.tone(\n beep.freq(698.456))");
}
if (arduboy.justPressed(A_BUTTON)) {
// For short tones with a duration less than a frame time,
// or when timer() isn't being used, such as in a menu,
// a continuous tone can be played and then stopped after a delay
// but note that no other work will be done during the delay.
beep.tone(beep.freq(1000)); // Play a 1000Hz tone until stopped
arduboy.delayShort(30); // Delay for 30ms
beep.noTone(); // Stop the tone
commandText("beep.tone(\n beep.freq(1000))\n(delay 30ms)\nbeep.noTone()");
}
if (arduboy.justPressed(B_BUTTON)) {
beep.noTone(); // Stop the tone if one is playing
commandText("beep.noTone()");
}
arduboy.println(F("Last command:"));
arduboy.print(displayText);
// The Arduboy2 class's audio subclass controls sound muting.
// For this sketch, mute or unmute can be set using the "System Control"
// start up feature. (Hold B while powering up then, while still holding B,
// press UP to enable sound or DOWN for mute.)
if (!arduboy.audio.enabled()) {
arduboy.setCursor(22, 40);
arduboy.print(F("Sound is MUTED"));
}
arduboy.setCursor(0, 48);
// The "duration" variable can be tested for non-zero to determine if a
// timed tone is currently playing.
if (beep.duration != 0) {
arduboy.print(F("A tone is playing"));
}
else {
arduboy.print(F("Continuous tone or\nno tone playing"));
}
arduboy.drawRect(objectX, 40, 6, 6);
if (++objectX == WIDTH - 6) {
objectX = 0;
}
arduboy.display();
}
void commandText(const char* text) {
strncpy(displayText, text, sizeof displayText);
displayText[sizeof displayText - 1] = '\0';
}

122
examples/BeepDemo/COPYING Normal file
View File

@ -0,0 +1,122 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@ -22,10 +22,10 @@ char title[] = "Press Buttons!";
byte x; byte x;
byte y; byte y;
// Width of each charcter including inter-character space // Width of each character including inter-character space
#define CHAR_WIDTH 6 #define CHAR_WIDTH 6
// Height of each charater // Height of each character
#define CHAR_HEIGHT 8 #define CHAR_HEIGHT 8
// To get the number of characters, we subtract 1 from the length of // To get the number of characters, we subtract 1 from the length of
@ -101,6 +101,6 @@ void loop() {
// then we print to screen what is stored in our title variable we declared earlier // then we print to screen what is stored in our title variable we declared earlier
arduboy.print(title); arduboy.print(title);
// then we finaly we tell the arduboy to display what we just wrote to the display. // then we finally we tell the arduboy to display what we just wrote to the display.
arduboy.display(); arduboy.display();
} }

View File

@ -1,4 +1,4 @@
Buttons Buttons
======= =======
A an example that demonstrates how to capture input from the buttons. An example that demonstrates how to capture input from the buttons.

122
examples/FontDemo/COPYING Normal file
View File

@ -0,0 +1,122 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@ -0,0 +1,127 @@
/*
FontDemo.ino
This sketch draws all the characters of the library's font as a 16 x 16 block.
The bottom half of the block will be initially off screen. An information
overlay will be drawn on top of the block showing the X and Y cursor position
of the start of the block, the current text wrap state and the current text
raw mode state.
The block can be moved around and off the screen using the direction buttons,
to view all the characters and show how the wrap and raw modes behave.
Controls:
Direction Buttons: Move the block 1 pixel.
Hold A with Direction Buttons: Continuously move the block.
B Button: Toggle text raw mode on and off.
Hold A, press B: Toggle text wrap mode on and off.
*/
/*
Written in September 2020 by Scott Allen saydisp-git@yahoo.ca
To the extent possible under law, the author(s) have dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include <Arduboy2.h>
Arduboy2 arduboy;
constexpr uint8_t charWidth = arduboy.getCharacterWidth() + arduboy.getCharacterSpacing();
constexpr uint8_t charHeight = arduboy.getCharacterHeight() + arduboy.getLineSpacing();
constexpr uint16_t infoX = charWidth * 8;
constexpr uint16_t infoY = charHeight * 2;
constexpr uint8_t infoWidth = charWidth * 5;
constexpr uint8_t infoBorder = 1;
int16_t xPos = 0;
int16_t yPos = 0;
void setup() {
arduboy.begin();
arduboy.setFrameRate(20);
arduboy.clear();
arduboy.print(F("DISPLAY AND MOVE FONT\n\n"
" Dpad: move 1\n"
"A + Dpad: repeat move\n"
" B: toggle raw\n"
" A + B: toggle wrap\n\n"
" Press A to start"));
arduboy.display();
while (!arduboy.pressed(A_BUTTON)) {
arduboy.idle();
}
}
void loop() {
if (!arduboy.nextFrame()) {
return;
}
arduboy.pollButtons();
if (arduboy.justPressed(B_BUTTON)) {
if (arduboy.pressed(A_BUTTON)) {
arduboy.setTextWrap(!arduboy.getTextWrap());
}
else {
arduboy.setTextRawMode(!arduboy.getTextRawMode());
}
}
if (arduboy.justPressed(UP_BUTTON) ||
arduboy.pressed(UP_BUTTON + A_BUTTON)) {
--yPos;
}
if (arduboy.justPressed(DOWN_BUTTON) ||
arduboy.pressed(DOWN_BUTTON + A_BUTTON)) {
++yPos;
}
if (arduboy.justPressed(LEFT_BUTTON) ||
arduboy.pressed(LEFT_BUTTON | A_BUTTON)) {
--xPos;
}
if (arduboy.justPressed(RIGHT_BUTTON) ||
arduboy.pressed(RIGHT_BUTTON | A_BUTTON)) {
++xPos;
}
arduboy.clear();
char code = 0;
for (uint8_t i = 0; i < 16; ++i) {
arduboy.setCursor(xPos, yPos + (charHeight * i));
for (uint8_t j = 0; j < 16; ++j) {
arduboy.print(code);
++code;
}
}
arduboy.fillRect(infoX - infoBorder, infoY - infoBorder,
infoWidth + infoBorder, charHeight * 4 + infoBorder);
arduboy.setTextColor(BLACK);
arduboy.setTextBackground(WHITE);
arduboy.setCursor(infoX, infoY);
arduboy.print(F("X:"));
arduboy.print(xPos);
arduboy.setCursor(infoX, infoY + charHeight);
arduboy.print(F("Y:"));
arduboy.print(yPos);
arduboy.setCursor(infoX, infoY + (charHeight * 2));
arduboy.print(F("W:"));
arduboy.print(arduboy.getTextWrap() ? F("Yes") : F("No"));
arduboy.setCursor(infoX, infoY + (charHeight * 3));
arduboy.print(F("R:"));
arduboy.print(arduboy.getTextRawMode() ? F("Yes") : F("No"));
arduboy.setTextColor(WHITE);
arduboy.setTextBackground(BLACK);
arduboy.display();
}

View File

@ -46,6 +46,6 @@ void loop() {
// then we print to screen what is in the Quotation marks "" // then we print to screen what is in the Quotation marks ""
arduboy.print(F("Hello, world!")); arduboy.print(F("Hello, world!"));
// then we finaly we tell the arduboy to display what we just wrote to the display // then we finally we tell the arduboy to display what we just wrote to the display
arduboy.display(); arduboy.display();
} }

122
examples/RGBled/COPYING Normal file
View File

@ -0,0 +1,122 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

362
examples/RGBled/RGBled.ino Normal file
View File

@ -0,0 +1,362 @@
/*
RGBled
This sketch demonstrates controlling the Arduboy's RGB LED,
in both analog and digital modes.
*/
/*
Written in 2018 by Scott Allen saydisp-git@yahoo.ca
To the extent possible under law, the author(s) have dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include <Arduboy2.h>
// The frame rate determines the button auto-repeat rate
#define FRAME_RATE 25
// The increment/decrement amount when auto-repeating
#define REPEAT_AMOUNT 3
// Delay time before button auto-repeat starts, in milliseconds
#define REPEAT_DELAY 700
// Calculation of the number of frames to wait before button auto-repeat starts
#define DELAY_FRAMES (REPEAT_DELAY / (1000 / FRAME_RATE))
#define ANALOG false
#define DIGITAL true
#define ANALOG_MAX 255
// Color array index
enum class Color {
RED,
GREEN,
BLUE,
COUNT
};
// Map LED color index to LED name
const byte LEDpin[(byte)(Color::COUNT)] = {
RED_LED,
GREEN_LED,
BLUE_LED
};
Arduboy2 arduboy;
// Analog LED values
byte analogValue[3] = { 0, 0, 0};
// Digital LED states
byte digitalState[3] = { RGB_OFF, RGB_OFF, RGB_OFF };
byte analogSelected = (byte)(Color::RED);
byte digitalSelected = (byte)(Color::RED);
boolean controlMode = ANALOG;
// Button repeat handling
unsigned int delayCount = 0;
boolean repeating = false;
// ============================= SETUP ===================================
void setup() {
arduboy.begin();
arduboy.setFrameRate(FRAME_RATE);
analogSet();
}
// =======================================================================
// =========================== MAIN LOOP =================================
void loop() {
if (!arduboy.nextFrame()) {
return;
}
arduboy.pollButtons();
// Toggle analog/digital control mode
if (arduboy.justPressed(A_BUTTON)) {
if ((controlMode = !controlMode) == DIGITAL) {
arduboy.freeRGBled();
digitalSet();
}
else {
analogSet();
}
}
// Reset to Analog mode and all LEDs off
if (arduboy.justPressed(B_BUTTON)) {
reset();
}
// Handle D-pad buttons for current mode
if (controlMode == ANALOG) {
modeAnalog();
}
else {
modeDigital();
}
// Handle delay before button auto-repeat starts
if ((delayCount != 0) && (--delayCount == 0)) {
repeating = true;
}
renderScreen(); // Render and display the entire screen
}
// =======================================================================
// Analog control
void modeAnalog() {
if (arduboy.justPressed(RIGHT_BUTTON)) {
valueInc(1);
startButtonDelay();
}
else if (arduboy.justPressed(LEFT_BUTTON)) {
valueDec(1);
startButtonDelay();
}
else if (repeating && arduboy.pressed(RIGHT_BUTTON)) {
valueInc(REPEAT_AMOUNT);
}
else if (repeating && arduboy.pressed(LEFT_BUTTON)) {
valueDec(REPEAT_AMOUNT);
}
else if (arduboy.justPressed(DOWN_BUTTON)) {
analogSelectInc();
}
else if (arduboy.justPressed(UP_BUTTON)) {
analogSelectDec();
}
else if (repeating) {
stopButtonRepeat();
}
}
// Digital control
void modeDigital() {
if (arduboy.justPressed(RIGHT_BUTTON) || arduboy.justPressed(LEFT_BUTTON)) {
digitalState[digitalSelected] = (digitalState[digitalSelected] == RGB_ON) ?
RGB_OFF : RGB_ON;
arduboy.digitalWriteRGB(LEDpin[digitalSelected],
digitalState[digitalSelected]);
}
else if (arduboy.justPressed(DOWN_BUTTON)) {
digitalSelectInc();
}
else if (arduboy.justPressed(UP_BUTTON)) {
digitalSelectDec();
}
}
// Reset to analog mode and turn all LEDs off
void reset() {
digitalState[(byte)(Color::RED)] = RGB_OFF;
digitalState[(byte)(Color::GREEN)] = RGB_OFF;
digitalState[(byte)(Color::BLUE)] = RGB_OFF;
digitalSet();
analogValue[(byte)(Color::RED)] = 0;
analogValue[(byte)(Color::GREEN)] = 0;
analogValue[(byte)(Color::BLUE)] = 0;
analogSet();
digitalSelected = (byte)(Color::RED);
analogSelected = (byte)(Color::RED);
controlMode = ANALOG;
}
// Increment the selected analog LED value by the specified amount
// and update the LED
void valueInc(byte amount) {
if ((ANALOG_MAX - analogValue[analogSelected]) <= amount) {
analogValue[analogSelected] = ANALOG_MAX;
}
else {
analogValue[analogSelected] += amount;
}
arduboy.setRGBled(LEDpin[analogSelected], analogValue[analogSelected]);
}
// Decrement the selected analog LED value by the specified amount
// and update the LED
void valueDec(byte amount) {
if (analogValue[analogSelected] <= amount) {
analogValue[analogSelected] = 0;
}
else {
analogValue[analogSelected] -= amount;
}
arduboy.setRGBled(LEDpin[analogSelected], analogValue[analogSelected]);
}
// Select the next analog color index with wrap
void analogSelectInc() {
selectInc(analogSelected);
}
// Select the previous analog color index with wrap
void analogSelectDec() {
selectDec(analogSelected);
}
// Select the next digital color index with wrap
void digitalSelectInc() {
selectInc(digitalSelected);
}
// Select the previous digital color index with wrap
void digitalSelectDec() {
selectDec(digitalSelected);
}
// Select the next color index with wrap
void selectInc(byte &index) {
if (++index == (byte)(Color::COUNT)) {
index = 0;
}
}
// Select the previous color index with wrap
void selectDec(byte &index) {
if (index == 0) {
index = ((byte)(Color::COUNT) - 1);
}
else {
index--;
}
}
// Update all LEDs in analog mode
void analogSet() {
arduboy.setRGBled(analogValue[(byte)(Color::RED)],
analogValue[(byte)(Color::GREEN)],
analogValue[(byte)(Color::BLUE)]);
}
// Update all LEDs in digital mode
void digitalSet() {
arduboy.digitalWriteRGB(digitalState[(byte)(Color::RED)],
digitalState[(byte)(Color::GREEN)],
digitalState[(byte)(Color::BLUE)]);
}
// Start the button auto-repeat delay
void startButtonDelay() {
delayCount = DELAY_FRAMES;
repeating = false;
}
// Stop the button auto-repeat or delay
void stopButtonRepeat() {
delayCount = 0;
repeating = false;
}
// Render and display the screen
void renderScreen() {
arduboy.setCursor(12, 0);
arduboy.print(F("RGB LED"));
arduboy.setCursor(15, 56);
arduboy.print(F("A:Mode B:Reset"));
arduboy.setCursor(74, 0);
if (controlMode == ANALOG) {
arduboy.print(F(" Analog"));
drawAnalog(9, Color::RED, "Red:");
drawAnalog(25, Color::GREEN, "Green:");
drawAnalog(41, Color::BLUE, "Blue:");
}
else { // Digital
arduboy.print(F("Digital"));
drawDigital(9, Color::RED, "Red:");
drawDigital(25, Color::GREEN, "Green:");
drawDigital(41, Color::BLUE, "Blue:");
}
arduboy.display(CLEAR_BUFFER);
}
// Draw the information for one analog color
void drawAnalog(int y, Color color, const char* name) {
byte value = analogValue[(byte)color];
arduboy.setCursor(0, y);
arduboy.print(name);
arduboy.setCursor(42, y);
printValue(value);
if (analogSelected == (byte)color) {
arduboy.print(F(" <--"));
}
drawBar(y + 8, color, value);
}
// Draw the value bar for an analog color
void drawBar(int y, Color color, byte value) {
byte barLength = value / 2;
if (barLength == 0) {
return;
}
if (analogSelected == (byte)color) {
arduboy.fillRect(0, y, barLength, 5);
}
else {
arduboy.drawRect(0, y, barLength, 5);
}
}
// Draw the information for one digital color
void drawDigital(int y, Color color, const char* name) {
byte state = digitalState[(byte)color];
arduboy.setCursor(34, y + 3);
arduboy.print(name);
arduboy.setCursor(76, y + 3);
if (state == RGB_ON) {
arduboy.print(F("ON "));
arduboy.fillCircle(22, y + 6, 4);
}
else {
arduboy.print(F("OFF"));
arduboy.drawCircle(22, y + 6, 4);
}
if (digitalSelected == (byte)color) {
arduboy.print(F(" <--"));
arduboy.drawRect(16, y, 13, 13);
}
}
// Print a byte in decimal and hex
void printValue(byte val) {
if (val < 100) {
arduboy.print(' ');
}
if (val < 10) {
arduboy.print(' ');
}
arduboy.print(val);
arduboy.print(F(" 0x"));
if (val < 0x10) {
arduboy.print('0');
}
arduboy.print(val, HEX);
}

View File

@ -1,835 +0,0 @@
// =======================================================================
// Manage an Arduboy's unit name and ID stored in the system EEPROM area
// =======================================================================
/*
----------------------------------------------------------------------------
The area in EEPROM reserved by the library for global system use includes
space to store a Unit Name and Unit ID. These can be read by sketches,
using the readUnitName() and readUnitID() functions. Also, the Arduboy2
class will display the Unit Name at the end of the boot logo sequence,
if possible, during start up.
This sketch allows the Unit Name and Unit ID to be set or changed and saved.
----------------------------------------------------------------------------
*/
// Version 2.0
/*
------------------------------------------------------------------------------
Copyright (c) 2017, Scott Allen
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
*/
#include <Arduboy2.h>
// The frame rate determines the button auto-repeat rate for unit name entry
#define FRAME_RATE 10
// The unit ID auto-repeat rate is slowed, compared to the unit name rate, by
// repeating only once per the defined number of frames
#define ID_REPEAT_FRAMES 3
// Delay time before button auto-repeat starts, in milliseconds
#define REPEAT_DELAY 700
// All the constant stings
const char StrName[] PROGMEM = "NAME";
const char StrID[] PROGMEM = "ID";
const char StrYes[] PROGMEM = "YES";
const char StrNo[] PROGMEM = "NO";
const char StrSaveQ[] PROGMEM = "SAVE?";
const char StrSaved[] PROGMEM = "SAVED";
const char StrShowNameQ1[] PROGMEM = "Show Unit Name";
const char StrShowNameQ2[] PROGMEM = "on logo screen?";
const char StrBtnChangeName[] PROGMEM = "UP:change Unit Name";
const char StrBtnChangeID[] PROGMEM = "DOWN:change Unit ID";
const char StrBtnShowName[] PROGMEM = "LEFT:set \"show name\"";
const char StrBtnMenu[] PROGMEM = "A:menu";
const char StrBtnSave[] PROGMEM = "B:save";
const char StrBtnYes[] PROGMEM = "A:yes";
const char StrBtnNo[] PROGMEM = "B:no";
const char StrBtnTestLogo[] PROGMEM = "DOWN:test boot logo";
const char StrHex[] PROGMEM = "hex";
const char StrDecimal[] PROGMEM = "decimal";
#define CHAR_WIDTH 6
#define CHAR_HEIGHT 8
#define SMALL_SPACE 4 // The number of pixels for a small space between groups
// Defines for text and field locations
#define MENU_BTN_CHANGE_NAME_X 0
#define MENU_BTN_CHANGE_NAME_Y 0
#define MENU_NAME_X centerStrLen(ARDUBOY_UNIT_NAME_LEN)
#define MENU_NAME_Y (MENU_BTN_CHANGE_NAME_Y + CHAR_HEIGHT + 3)
#define MENU_BTN_CHANGE_ID_X 0
#define MENU_BTN_CHANGE_ID_Y 26
#define MENU_HEADING_HEX_X (centerStr_P(StrHex) - (WIDTH / 4))
#define MENU_HEADING_DECIMAL_X (centerStr_P(StrDecimal) + (WIDTH / 4))
#define MENU_HEADINGS_Y (MENU_BTN_CHANGE_ID_Y + CHAR_HEIGHT + 1)
#define MENU_ID_HEX_X (centerStrLen(5) - (WIDTH / 4))
#define MENU_ID_DECIMAL_X (centerStrLen(5) + (WIDTH / 4))
#define MENU_ID_Y (MENU_HEADINGS_Y + CHAR_HEIGHT + 1)
#define MENU_BTN_SHOW_NAME_X 0
#define MENU_BTN_SHOW_NAME_Y 56
#define NAME_TITLE_X centerStr_P(StrName)
#define NAME_TITLE_Y 0
#define NAME_BTN_MENU_X 0
#define NAME_BTN_MENU_Y 0
#define NAME_BTN_SAVE_X rightStr_P(StrBtnSave)
#define NAME_BTN_SAVE_Y NAME_BTN_MENU_Y
#define NAME_LARGE_X centerStrLen(ARDUBOY_UNIT_NAME_LEN * 2)
#define NAME_LARGE_Y 11
#define NAME_HEX_X 0
#define NAME_HEX_Y 40
#define NAME_DECIMAL_X 0
#define NAME_DECIMAL_Y 54
#define NAME_BTN_YES_X 0
#define NAME_BTN_YES_Y 0
#define NAME_BTN_NO_X rightStr_P(StrBtnNo)
#define NAME_BTN_NO_Y NAME_BTN_YES_Y
#define NAME_SAVE_Q_X (centerStr_P(StrSaveQ) - ((6 * CHAR_WIDTH) + (CHAR_WIDTH / 2)))
#define NAME_SAVE_Q_Y (NAME_LARGE_Y + (CHAR_HEIGHT / 2) + 3)
#define NAME_SAVE_X (NAME_SAVE_Q_X + ((strlen_P(StrSaveQ) * CHAR_WIDTH) + CHAR_WIDTH))
#define NAME_SAVE_Y (NAME_LARGE_Y + 3)
#define ID_TITLE_X centerStr_P(StrID)
#define ID_TITLE_Y 0
#define ID_BTN_MENU_X 0
#define ID_BTN_MENU_Y 0
#define ID_BTN_SAVE_X rightStr_P(StrBtnSave)
#define ID_BTN_SAVE_Y ID_BTN_MENU_Y
#define ID_LARGE_X centerStrLen(5 * 2)
#define ID_LARGE_Y 13
#define ID_2_DECIMAL_X 12
#define ID_2_DECIMAL_Y 38
#define ID_DECIMAL_X (WIDTH - (CHAR_WIDTH * 5 + 12))
#define ID_DECIMAL_Y ID_2_DECIMAL_Y
#define ID_BINARY_X 0
#define ID_BINARY_Y 54
#define ID_BTN_YES_X 0
#define ID_BTN_YES_Y 0
#define ID_BTN_NO_X rightStr_P(StrBtnNo)
#define ID_BTN_NO_Y ID_BTN_YES_Y
#define ID_SAVE_Q_X (centerStr_P(StrSaveQ) - ((5 * CHAR_WIDTH) + (CHAR_WIDTH / 2)))
#define ID_SAVE_Q_Y (ID_LARGE_Y + (CHAR_HEIGHT / 2) + 1)
#define ID_SAVE_X (ID_SAVE_Q_X + ((strlen_P(StrSaveQ) * CHAR_WIDTH) + CHAR_WIDTH))
#define ID_SAVE_Y (ID_LARGE_Y + 1)
#define SHOW_NAME_BTN_MENU_X 0
#define SHOW_NAME_BTN_MENU_Y 0
#define SHOW_NAME_BTN_SAVE_X rightStr_P(StrBtnSave)
#define SHOW_NAME_BTN_SAVE_Y SHOW_NAME_BTN_MENU_Y
#define SHOW_NAME_Q_1_X centerStr_P(StrShowNameQ1)
#define SHOW_NAME_Q_1_Y 12
#define SHOW_NAME_Q_2_X centerStr_P(StrShowNameQ2)
#define SHOW_NAME_Q_2_Y (SHOW_NAME_Q_1_Y + 8)
#define SHOW_NAME_YES_X ((WIDTH / 2) - ((strlen_P(StrYes) + 1) * CHAR_WIDTH * 2))
#define SHOW_NAME_YES_Y 34
#define SHOW_NAME_NO_X ((WIDTH / 2) + (CHAR_WIDTH * 2))
#define SHOW_NAME_NO_Y SHOW_NAME_YES_Y
#define SHOW_NAME_TEST_X 0
#define SHOW_NAME_TEST_Y 56
#define SHOW_NAME_SAVED_X centerStr2_P(StrSaved)
#define SHOW_NAME_SAVED_Y ((HEIGHT / 2) - CHAR_HEIGHT)
// Calculation of the number of frames to wait before button auto-repeat starts
#define DELAY_FRAMES (REPEAT_DELAY / (1000 / FRAME_RATE))
// The Arduino "magic" has trouble creating prototypes for functions called
// by pointers, so they're declared here manually
void stateMain(), stateName(), stateID(), stateShowName();
void stateSaveName(), stateSaveID();
void screenMain(), screenName(), screenID(), screenShowName();
void screenSaveName(), screenSaveID();
Arduboy2 arduboy;
char unitName[ARDUBOY_UNIT_NAME_LEN + 1];
byte nameIndex;
uint16_t unitID;
byte idIndex;
boolean showNameFlag;
// Assign numbers for each state/screen
enum State : byte {
sMain,
sName,
sID,
sShowName,
sSaveName,
sSaveID,
sMAX = sSaveID
};
byte currentState;
// Function pointer array for button handling
void (*stateFunc[sMAX + 1])() = {
stateMain,
stateName,
stateID,
stateShowName,
stateSaveName,
stateSaveID
};
// Function pointer array for screen drawing
void (*screenFunc[sMAX + 1])() = {
screenMain,
screenName,
screenID,
screenShowName,
screenSaveName,
screenSaveID
};
unsigned int delayCount = 0;
boolean repeating = false;
// ============================= SETUP ===================================
void setup() {
arduboy.begin();
arduboy.setFrameRate(FRAME_RATE);
setState(sMain);
}
// =======================================================================
// =========================== MAIN LOOP =================================
void loop() {
if (!arduboy.nextFrame()) {
return;
}
arduboy.pollButtons();
(*stateFunc[currentState])();
if ((delayCount != 0) && (--delayCount == 0)) {
repeating = true;
}
}
// =======================================================================
// ------------------------- Program States ------------------------------
// Set to the given state and display the screen for that state
// Can be called with the current state to update the current screen
void setState(byte newState) {
currentState = newState;
stopButtonRepeat();
drawScreen();
}
// STATE: Main selection screen
void stateMain() {
if (arduboy.justPressed(UP_BUTTON)) {
setState(sName);
}
else if (arduboy.justPressed(DOWN_BUTTON)) {
setState(sID);
}
else if (arduboy.justPressed(LEFT_BUTTON)) {
setState(sShowName);
}
}
// STATE: Change unit name
void stateName() {
if (arduboy.justPressed(UP_BUTTON)) {
nameCharInc();
startButtonDelay();
}
else if (arduboy.justPressed(DOWN_BUTTON)) {
nameCharDec();
startButtonDelay();
}
else if (repeating && arduboy.pressed(UP_BUTTON)) {
nameCharInc();
}
else if (repeating && arduboy.pressed(DOWN_BUTTON)) {
nameCharDec();
}
else if (arduboy.justPressed(RIGHT_BUTTON)) {
nameCursorRight();
}
else if (arduboy.justPressed(LEFT_BUTTON)) {
nameCursorLeft();
}
else if (arduboy.justPressed(A_BUTTON)) {
setState(sMain);
}
else if (arduboy.justPressed(B_BUTTON)) {
setState(sSaveName);
}
else if (repeating) {
stopButtonRepeat();
}
}
// STATE: Change unit ID
void stateID() {
if (arduboy.justPressed(UP_BUTTON)) {
idDigitInc();
startButtonDelay();
}
else if (arduboy.justPressed(DOWN_BUTTON)) {
idDigitDec();
startButtonDelay();
}
else if (repeating && arduboy.pressed(UP_BUTTON)) {
if (arduboy.everyXFrames(ID_REPEAT_FRAMES)) {
idDigitInc();
}
}
else if (repeating && arduboy.pressed(DOWN_BUTTON)) {
if (arduboy.everyXFrames(ID_REPEAT_FRAMES)) {
idDigitDec();
}
}
else if (arduboy.justPressed(RIGHT_BUTTON)) {
idCursorRight();
}
else if (arduboy.justPressed(LEFT_BUTTON)) {
idCursorLeft();
}
else if (arduboy.justPressed(A_BUTTON)) {
setState(sMain);
}
else if (arduboy.justPressed(B_BUTTON)) {
setState(sSaveID);
}
else if (repeating) {
stopButtonRepeat();
}
}
// STATE: Set "Show Unit Name" flag
void stateShowName() {
if (arduboy.justPressed(RIGHT_BUTTON)) {
showNameToggle();
}
else if (arduboy.justPressed(LEFT_BUTTON)) {
showNameToggle();
}
else if (arduboy.justPressed(A_BUTTON)) {
setState(sMain);
}
else if (arduboy.justPressed(B_BUTTON)) {
saveShowName();
setState(sShowName);
}
else if (arduboy.justPressed(DOWN_BUTTON)) {
showNameFlag = arduboy.readShowUnitNameFlag();
arduboy.bootLogo();
delay(1000);
setState(sShowName);
}
}
// STATE: Prompt to save the unit name
void stateSaveName() {
if (arduboy.justPressed(A_BUTTON)) {
arduboy.writeUnitName(unitName);
setState(sMain);
}
else if (arduboy.justPressed(B_BUTTON)) {
setState(sName);
}
}
// STATE: Prompt to save the unit ID
void stateSaveID() {
if (arduboy.justPressed(A_BUTTON)) {
arduboy.writeUnitID(unitID);
setState(sMain);
}
else if (arduboy.justPressed(B_BUTTON)) {
setState(sID);
}
}
// ------------------------- Screen Display ------------------------------
// Display the screen for the current state
void drawScreen() {
arduboy.clear();
(*screenFunc[currentState])();
arduboy.display();
}
// DISPLAY: Main screen
void screenMain() {
readEEPROM();
nameIndex = idIndex = 0;
printStr_P(MENU_BTN_CHANGE_NAME_X, MENU_BTN_CHANGE_NAME_Y, StrBtnChangeName);
printName(MENU_NAME_X, MENU_NAME_Y);
printStr_P(MENU_BTN_CHANGE_ID_X, MENU_BTN_CHANGE_ID_Y, StrBtnChangeID);
printStr_P(MENU_HEADING_HEX_X, MENU_HEADINGS_Y, StrHex);
printStr_P(MENU_HEADING_DECIMAL_X, MENU_HEADINGS_Y, StrDecimal);
printIDHex(MENU_ID_HEX_X, MENU_ID_Y);
printIDDecimal(MENU_ID_DECIMAL_X, MENU_ID_Y);
printStr_P(MENU_BTN_SHOW_NAME_X, MENU_BTN_SHOW_NAME_Y, StrBtnShowName);
}
// DISPLAY: Change unit name
void screenName() {
printNameScreenCommon();
printStr_P(NAME_BTN_MENU_X, NAME_BTN_MENU_Y, StrBtnMenu);
printStr_P(NAME_BTN_SAVE_X, NAME_BTN_SAVE_Y, StrBtnSave);
printNameLarge(NAME_LARGE_X, NAME_LARGE_Y);
printNameUnderline(NAME_LARGE_X, NAME_LARGE_Y);
printNameCursors();
}
// DISPLAY: Change unit ID
void screenID() {
printIDScreenCommon();
printStr_P(ID_BTN_MENU_X, ID_BTN_MENU_Y, StrBtnMenu);
printStr_P(ID_BTN_SAVE_X, ID_BTN_SAVE_Y, StrBtnSave);
printIDLarge(ID_LARGE_X, ID_LARGE_Y);
printIDCursors();
}
// DISPLAY: Set "Show Unit Name" flag
void screenShowName() {
printStr_P(SHOW_NAME_BTN_MENU_X, SHOW_NAME_BTN_MENU_Y, StrBtnMenu);
printStr_P(SHOW_NAME_BTN_SAVE_X, SHOW_NAME_BTN_SAVE_Y, StrBtnSave);
printStr_P(SHOW_NAME_Q_1_X, SHOW_NAME_Q_1_Y, StrShowNameQ1);
printStr_P(SHOW_NAME_Q_2_X, SHOW_NAME_Q_2_Y, StrShowNameQ2);
arduboy.setTextSize(2);
printStr_P(SHOW_NAME_YES_X, SHOW_NAME_YES_Y, StrYes);
printStr_P(SHOW_NAME_NO_X, SHOW_NAME_NO_Y, StrNo);
arduboy.setTextSize(1);
printShowNameCursor();
printStr_P(SHOW_NAME_TEST_X, SHOW_NAME_TEST_Y, StrBtnTestLogo);
}
// DISPLAY: Prompt to save the unit name
void screenSaveName() {
printNameScreenCommon();
printStr_P(NAME_BTN_YES_X, NAME_BTN_YES_Y, StrBtnYes);
printStr_P(NAME_BTN_NO_X, NAME_BTN_NO_Y, StrBtnNo);
printSavePrompt(NAME_SAVE_Q_X, NAME_SAVE_Q_Y);
printNameLarge(NAME_SAVE_X, NAME_SAVE_Y);
}
// DISPLAY: Prompt to save the unit ID
void screenSaveID() {
printIDScreenCommon();
printStr_P(ID_BTN_YES_X, ID_BTN_YES_Y, StrBtnYes);
printStr_P(ID_BTN_NO_X, ID_BTN_NO_Y, StrBtnNo);
printSavePrompt(ID_SAVE_Q_X, ID_SAVE_Q_Y);
printIDLarge(ID_SAVE_X, ID_SAVE_Y);
}
// Save the "Show Unit Name" flag and overlay the "SAVED" message on the screen
void saveShowName() {
arduboy.writeShowUnitNameFlag(showNameFlag);
arduboy.fillRect(SHOW_NAME_SAVED_X - 4, SHOW_NAME_SAVED_Y - 4,
strlen_P(StrSaved) * CHAR_WIDTH * 2 + 6, CHAR_HEIGHT * 2 + 6);
arduboy.setTextColor(BLACK);
arduboy.setTextBackground(WHITE);
arduboy.setTextSize(2);
printStr_P(SHOW_NAME_SAVED_X, SHOW_NAME_SAVED_Y, StrSaved);
arduboy.setTextSize(1);
arduboy.setTextColor(WHITE);
arduboy.setTextBackground(BLACK);
arduboy.display();
delay(1000);
}
// --------------------- Printing Functions ------------------------------
// Print the name entry screen common information
void printNameScreenCommon() {
printStr_P(NAME_TITLE_X, NAME_TITLE_Y, StrName);
printNameHex(NAME_HEX_X, NAME_HEX_Y);
printNameDecimal(NAME_DECIMAL_X, NAME_DECIMAL_Y);
}
// Print the name entry screen common information
void printIDScreenCommon() {
printStr_P(ID_TITLE_X, ID_TITLE_Y, StrID);
printIDDecimalBytes(ID_2_DECIMAL_X, ID_2_DECIMAL_Y);
printIDDecimal(ID_DECIMAL_X, ID_DECIMAL_Y);
printIDBinary(ID_BINARY_X, ID_BINARY_Y);
}
// Print the name screen cursors
void printNameCursors() {
arduboy.fillRect(NAME_LARGE_X + (nameIndex * CHAR_WIDTH * 2),
NAME_LARGE_Y + (CHAR_HEIGHT * 2) + 2,
(CHAR_WIDTH * 2) - 2, 2);
arduboy.drawFastHLine(NAME_HEX_X +
(nameIndex * (CHAR_WIDTH * 3 + SMALL_SPACE)),
NAME_HEX_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 3 - 1);
arduboy.drawFastHLine(NAME_DECIMAL_X +
(nameIndex * (CHAR_WIDTH * 3 + SMALL_SPACE)),
NAME_DECIMAL_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 3 - 1);
}
// Print the ID screen cursors
void printIDCursors() {
arduboy.fillRect(ID_LARGE_X + ((idIndex + 1) * (CHAR_WIDTH * 2)),
ID_LARGE_Y + (CHAR_HEIGHT * 2),
(CHAR_WIDTH * 2) - 2, 2);
arduboy.drawFastHLine(ID_2_DECIMAL_X +
((idIndex / 2) * (CHAR_WIDTH * 3 + SMALL_SPACE)),
ID_2_DECIMAL_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 3 - 1);
arduboy.drawFastHLine(ID_DECIMAL_X, ID_DECIMAL_Y + CHAR_HEIGHT + 1,
CHAR_WIDTH * 5 - 1);
arduboy.drawFastHLine((ID_BINARY_X + CHAR_WIDTH + SMALL_SPACE) +
(idIndex * (CHAR_WIDTH * 4 + SMALL_SPACE)),
ID_BINARY_Y + CHAR_HEIGHT + 1, CHAR_WIDTH * 4 - 1);
}
// Print the current "Show Unit Name" cursor
void printShowNameCursor() {
if (showNameFlag) {
arduboy.fillRect(SHOW_NAME_YES_X, SHOW_NAME_YES_Y + (CHAR_HEIGHT * 2),
(strlen_P(StrYes) * CHAR_WIDTH - 1) * 2, 2);
}
else {
arduboy.fillRect(SHOW_NAME_NO_X, SHOW_NAME_NO_Y + (CHAR_HEIGHT * 2),
(strlen_P(StrNo) * CHAR_WIDTH - 1) * 2, 2);
}
}
// Print the unit name in normal size including an extent underline
// at the given location
void printName(int x, int y) {
printStr(x, y, unitName);
y += (CHAR_HEIGHT + 1);
for (byte i = 0; i < ARDUBOY_UNIT_NAME_LEN; i++, x += CHAR_WIDTH) {
arduboy.drawFastHLine(x, y, CHAR_WIDTH - 1);
}
}
// Print the unit name in large size at the given location
void printNameLarge(int x, int y) {
arduboy.setTextSize(2);
printStr(x, y, unitName);
arduboy.setTextSize(1);
}
// Add a line below the large name showing the current length
// Coordinates are for the name itself
void printNameUnderline(int x, int y) {
int lWidth;
if (unitName[0] != 0) {
x -= 1;
y += ((CHAR_HEIGHT * 2) + 6);
lWidth = (strlen(unitName) * (CHAR_WIDTH * 2));
arduboy.drawPixel(x, y);
arduboy.drawPixel(x + lWidth - 1, y);
arduboy.drawFastHLine(x, y + 1, lWidth);
}
}
// Print the unit name in hex at the given location
void printNameHex(int x, int y) {
for (byte i = 0; i < ARDUBOY_UNIT_NAME_LEN; i++) {
printHex8(x, y, unitName[i]);
x += CHAR_WIDTH * 3 + SMALL_SPACE;
}
}
// Print the unit name in decimal at the given location
void printNameDecimal(int x, int y) {
for (byte i = 0; i < ARDUBOY_UNIT_NAME_LEN; i++) {
printDecimal8(x, y, unitName[i]);
x += CHAR_WIDTH * 3 + SMALL_SPACE;
}
}
// Print the unit ID in large size at the given location
void printIDLarge(int x, int y) {
arduboy.setTextSize(2);
printIDHex(x, y);
arduboy.setTextSize(1);
}
// Print the unit ID in normal size at the given location
void printIDHex(int x, int y) {
printHex16(x, y, unitID);
}
// Print the unit ID as 2 decimal bytes at the given location
void printIDDecimalBytes(int x, int y) {
printDecimal8(x, y, unitID >> 8);
printDecimal8(x + CHAR_WIDTH * 3 + SMALL_SPACE, y, unitID & 0x00FF);
}
// Print the unit ID in decimal at the given location
void printIDDecimal(int x, int y) {
printDecimal16(x, y, unitID);
}
// print the unit ID in binary at the given location as 4 nybbles
// with a leading 'b'
void printIDBinary(int x, int y) {
arduboy.setCursor(x, y);
arduboy.print('b');
x += CHAR_WIDTH + SMALL_SPACE;
for (char i = 3 * 4; i >= 0; i -= 4) {
printBinaryNybble(x, y, (byte)(unitID >> i));
x += CHAR_WIDTH * 4 + SMALL_SPACE;
}
}
// Print the save prompt in reverse at the given location
void printSavePrompt(int x, int y) {
arduboy.fillRect(x - 2, y - 2,
strlen_P(StrSaveQ) * CHAR_WIDTH + 3, CHAR_HEIGHT + 3);
arduboy.setTextColor(BLACK);
arduboy.setTextBackground(WHITE);
printStr_P(x, y, StrSaveQ);
arduboy.setTextColor(WHITE);
arduboy.setTextBackground(BLACK);
}
// Print a string at the given location
void printStr(int x, int y, char* str) {
arduboy.setCursor(x, y);
arduboy.print(str);
}
// Print a string in program memory at the given location
void printStr_P(int x, int y, const char* str) {
arduboy.setCursor(x, y);
arduboy.print((__FlashStringHelper*)(str));
}
// Print an 8 bit number in decimal, right justified with leading spaces
void printDecimal8(int x, int y, byte val) {
printDecimalHelper(x, y, 2, 100, val);
}
// Print a 16 bit number in decimal, right justified with leading spaces
void printDecimal16(int x, int y, unsigned int val) {
printDecimalHelper(x, y, 4, 10000, val);
}
// Print a right justified decimal number, given width-1 and (width-1)^10
void printDecimalHelper(int x, int y, byte width, unsigned int pwr10,
unsigned int val) {
arduboy.setCursor(x, y);
while (width > 0) {
if (val >= pwr10) {
break;
}
arduboy.print(' ');
pwr10 /= 10;
width--;
}
arduboy.print(val);
}
// Print an 8 bit hex number with leading x and zeros
void printHex8(int x, int y, byte val) {
arduboy.setCursor(x, y);
arduboy.print('x');
if (val < 16) {
arduboy.print('0');
}
arduboy.print(val, HEX);
}
// Print a 16 bit hex number with leading x and zeros
void printHex16(int x, int y, unsigned int val) {
arduboy.setCursor(x, y);
arduboy.print('x');
for (char i = 3 * 4; i >= 0; i -= 4) {
arduboy.print((val >> i) & 0x000F, HEX);
}
}
// Print a nybble in binary from the lowest 4 bits of the provided byte
void printBinaryNybble(int x, int y, byte val) {
arduboy.setCursor(x, y);
for (char i = 3; i >= 0; i--) {
arduboy.print((val >> i) & 0x01);
}
}
// ---------------- Control and Utility Functions ------------------------
// Get the current unit name and ID, and the "Show Unit Name" flag, from EEPROM
void readEEPROM() {
memset(unitName, 0, sizeof(unitName));
arduboy.readUnitName(unitName);
unitID = arduboy.readUnitID();
showNameFlag = arduboy.readShowUnitNameFlag();
}
// Increment the name character at the cursor position
void nameCharInc() {
while (invalidChar(++unitName[nameIndex])) { }
drawScreen();
}
// Decrement the name character at the cursor position
void nameCharDec() {
while (invalidChar(--unitName[nameIndex])) { }
drawScreen();
}
// Return true if the given character is not allowed
boolean invalidChar(char c) {
return (c == '\n') || (c == '\r') || ((byte)c == 0xFF);
}
// Move the name cursor right
void nameCursorRight() {
if (++nameIndex == ARDUBOY_UNIT_NAME_LEN) {
nameIndex = 0;
}
drawScreen();
}
// Move the name cursor left
void nameCursorLeft() {
if (nameIndex == 0) {
nameIndex = ARDUBOY_UNIT_NAME_LEN - 1;
}
else {
nameIndex--;
}
drawScreen();
}
// Increment the ID digit at the cursor position
void idDigitInc() {
uint16_t mask = 0xF000 >> (idIndex * 4);
uint16_t val = 0x1000 >> (idIndex * 4);
unitID = (unitID & ~mask) | ((unitID + val) & mask);
drawScreen();
}
// Decrement the ID digit at the cursor position
void idDigitDec() {
uint16_t mask = 0xF000 >> (idIndex * 4);
uint16_t val = 0x1000 >> (idIndex * 4);
unitID = (unitID & ~mask) | ((unitID - val) & mask);
drawScreen();
}
// Move the ID cursor right
void idCursorRight() {
if (++idIndex == sizeof(unitID) * 2) {
idIndex = 0;
}
drawScreen();
}
// Move the ID cursor left
void idCursorLeft() {
if (idIndex == 0) {
idIndex = sizeof(unitID) * 2 - 1;
}
else {
idIndex--;
}
drawScreen();
}
// Toggle the "Show Unit Name" selection
void showNameToggle() {
showNameFlag = !showNameFlag;
drawScreen();
}
// Start the button auto-repeat delay
void startButtonDelay() {
delayCount = DELAY_FRAMES;
repeating = false;
}
// Stop the button auto-repeat or delay
void stopButtonRepeat() {
delayCount = 0;
repeating = false;
}
// Calculate the X coordinate to center a string of the given length
int centerStrLen(unsigned int len) {
return (WIDTH / 2) - (len * CHAR_WIDTH / 2);
}
// Calculate the X coordinate to center a string located in program memory
int centerStr_P(const char* str) {
return (WIDTH / 2) - (strlen_P(str) * CHAR_WIDTH / 2);
}
// Calculate the X coordinate to center a size 2 string located in
// program memory
int centerStr2_P(const char* str) {
return (WIDTH / 2) - (strlen_P(str) * CHAR_WIDTH);
}
// Calculate the X coordinate to right justify a string in program memory
int rightStr_P(const char* str) {
return WIDTH - (strlen_P(str) * CHAR_WIDTH) + 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,59 +1,50 @@
# Doxyfile 1.8.11 # Doxyfile 1.8.18
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
# All text after a double hash (##) is considered a comment and is placed in
# front of the TAG it is preceding.
#
# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
# TAG = value [value, ...]
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Project related configuration options # Project related configuration options
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8 DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "Arduboy2 Library" PROJECT_NAME = "Arduboy2 Library"
PROJECT_NUMBER = PROJECT_NUMBER = 6.0.0
PROJECT_BRIEF = PROJECT_BRIEF =
PROJECT_LOGO = PROJECT_LOGO =
OUTPUT_DIRECTORY = ./doxygen OUTPUT_DIRECTORY = doxygen
CREATE_SUBDIRS = NO CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English OUTPUT_LANGUAGE = English
OUTPUT_TEXT_DIRECTION = None
BRIEF_MEMBER_DESC = YES BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = NO ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = YES INLINE_INHERITED_MEMB = YES
FULL_PATH_NAMES = YES FULL_PATH_NAMES = YES
STRIP_FROM_PATH = STRIP_FROM_PATH = src
STRIP_FROM_INC_PATH = STRIP_FROM_INC_PATH =
SHORT_NAMES = NO SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO JAVADOC_AUTOBRIEF = NO
JAVADOC_BANNER = NO
QT_AUTOBRIEF = NO QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 2 TAB_SIZE = 2
ALIASES = ALIASES =
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING = ino=C++ OPTIMIZE_OUTPUT_SLICE = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5
AUTOLINK_SUPPORT = YES AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO INLINE_SIMPLE_STRUCTS = NO
@ -64,14 +55,15 @@ LOOKUP_CACHE_SIZE = 0
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
EXTRACT_ALL = NO EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO EXTRACT_PRIVATE = NO
EXTRACT_PRIV_VIRTUAL = NO
EXTRACT_PACKAGE = NO EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = NO EXTRACT_LOCAL_CLASSES = NO
EXTRACT_LOCAL_METHODS = NO EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_MEMBERS = YES
HIDE_UNDOC_CLASSES = YES HIDE_UNDOC_CLASSES = YES
HIDE_FRIEND_COMPOUNDS = NO HIDE_FRIEND_COMPOUNDS = YES
HIDE_IN_BODY_DOCS = NO HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES CASE_SENSE_NAMES = YES
@ -107,14 +99,18 @@ WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = YES WARN_NO_PARAMDOC = YES
WARN_AS_ERROR = YES
WARN_FORMAT = "$file:$line: $text" WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE = WARN_LOGFILE =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the input files # Configuration options related to the input files
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
INPUT = ./src ./README.md ./LICENSE.txt INPUT = src \
README.md \
LICENSE.txt
INPUT_ENCODING = UTF-8 INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c *.cc *.cxx *.cpp *.c++ *.h *.hh *.hxx *.hpp *.h++ FILE_PATTERNS = *.cpp \
*.h
RECURSIVE = NO RECURSIVE = NO
EXCLUDE = EXCLUDE =
EXCLUDE_SYMLINKS = NO EXCLUDE_SYMLINKS = NO
@ -128,7 +124,7 @@ INPUT_FILTER =
FILTER_PATTERNS = FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS = FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = ./README.md USE_MDFILE_AS_MAINPAGE = README.md
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to source browsing # Configuration options related to source browsing
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -143,10 +139,11 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES VERBATIM_HEADERS = YES
CLANG_ASSISTED_PARSING = NO CLANG_ASSISTED_PARSING = NO
CLANG_OPTIONS = CLANG_OPTIONS =
CLANG_DATABASE_PATH =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index # Configuration options related to the alphabetical class index
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5 COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX = IGNORE_PREFIX =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -164,6 +161,7 @@ HTML_COLORSTYLE_HUE = 245
HTML_COLORSTYLE_SAT = 90 HTML_COLORSTYLE_SAT = 90
HTML_COLORSTYLE_GAMMA = 95 HTML_COLORSTYLE_GAMMA = 95
HTML_TIMESTAMP = YES HTML_TIMESTAMP = YES
HTML_DYNAMIC_MENUS = NO
HTML_DYNAMIC_SECTIONS = YES HTML_DYNAMIC_SECTIONS = YES
HTML_INDEX_NUM_ENTRIES = 100 HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO GENERATE_DOCSET = NO
@ -193,11 +191,13 @@ GENERATE_TREEVIEW = YES
ENUM_VALUES_PER_LINE = 4 ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250 TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO EXT_LINKS_IN_WINDOW = NO
HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10 FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES FORMULA_TRANSPARENT = YES
FORMULA_MACROFILE =
USE_MATHJAX = NO USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
MATHJAX_EXTENSIONS = MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE = MATHJAX_CODEFILE =
SEARCHENGINE = YES SEARCHENGINE = YES
@ -212,8 +212,9 @@ EXTRA_SEARCH_MAPPINGS =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
GENERATE_LATEX = YES GENERATE_LATEX = YES
LATEX_OUTPUT = latex LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex LATEX_CMD_NAME =
MAKEINDEX_CMD_NAME = makeindex MAKEINDEX_CMD_NAME = makeindex
LATEX_MAKEINDEX_CMD = makeindex
COMPACT_LATEX = NO COMPACT_LATEX = NO
PAPER_TYPE = a4 PAPER_TYPE = a4
EXTRA_PACKAGES = EXTRA_PACKAGES =
@ -227,6 +228,8 @@ LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = YES
LATEX_EMOJI_DIRECTORY =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the RTF output # Configuration options related to the RTF output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -251,6 +254,7 @@ MAN_LINKS = NO
GENERATE_XML = NO GENERATE_XML = NO
XML_OUTPUT = xml XML_OUTPUT = xml
XML_PROGRAMLISTING = YES XML_PROGRAMLISTING = YES
XML_NS_MEMB_FILE_SCOPE = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output # Configuration options related to the DOCBOOK output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -288,12 +292,10 @@ GENERATE_TAGFILE =
ALLEXTERNALS = NO ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES EXTERNAL_PAGES = YES
PERL_PATH = /usr/bin/perl
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the dot tool # Configuration options related to the dot tool
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES CLASS_DIAGRAMS = YES
MSCGEN_PATH =
DIA_PATH = DIA_PATH =
HIDE_UNDOC_RELATIONS = YES HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES HAVE_DOT = YES
@ -304,7 +306,7 @@ DOT_FONTPATH =
CLASS_GRAPH = YES CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES GROUP_GRAPHS = YES
UML_LOOK = YES UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10 UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES INCLUDE_GRAPH = YES
@ -320,6 +322,7 @@ DOTFILE_DIRS =
MSCFILE_DIRS = MSCFILE_DIRS =
DIAFILE_DIRS = DIAFILE_DIRS =
PLANTUML_JAR_PATH = PLANTUML_JAR_PATH =
PLANTUML_CFG_FILE =
PLANTUML_INCLUDE_PATH = PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50 DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0 MAX_DOT_GRAPH_DEPTH = 0

BIN
extras/assets/arduboy_logo.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 238 B

BIN
extras/assets/arduboy_screen.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 343 B

122
extras/cabi/COPYING Normal file
View File

@ -0,0 +1,122 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

206
extras/cabi/README.md Normal file
View File

@ -0,0 +1,206 @@
# Cabi - Compress Arduboy Image
A command line program to read a PNG (Portable Network Graphics) file
containing a bitmap image, compress it using RLE encoding and convert it to
C/C++ code suitable for use with the Team A.R.G. *drawCompressed()* function.
This function is included in the Arduboy2 library.
Written by zep
https://www.lexaloffle.com/bbs/?uid=1
https://twitter.com/lexaloffle
Contributed to Team A.R.G.
This program uses code from the LodePNG project by Lode Vandevenne to read
and decode PNG files.
https://github.com/lvandeve/lodepng
This version of Cabi is maintained as part of the Arduboy2 library so that it
remains available since the demise of Team A.R.G.
## Building the program
Pre-built executable code is not provided due to the difficulty of maintaining
versions for all the many operating systems that it could be run on.
The code is written in C and should compile properly using any ANSI C99
compatible compiler, such as (but not limited to) gcc or clang.
### Build examples
To build from a copy of the cabi directory tree provided, while in the base
directory containing cabi.c use:
`gcc cabi.c lodepng/lodepng.c -o cabi`
or
`clang cabi.c lodepng/lodepng.c -o cabi`
For Windows, it may be more desirable to name the program `CABI.EXE` by using:
`-o CABI.EXE`
Compiler options for optimization, etc. (such as -O2 or -Os) can be added if
desired but likely won't make much difference for most uses.
## Usage
The binary executable file (cabi or CABI.EXE) should be placed somewhere in the
path for executables on the operating system used, or else include the path as
part of the command given.
Running Cabi without any parameters will just output a brief program
description and the usage syntax:
```text
cabi - Compress Arduboy Image
Convert a PNG file into RLE encoded C/C++ source
for use with Arduboy2 drawCompressed()
usage: cabi in.png [array_name_prefix]
```
For `in.png` substitute the name of the PNG file to be converted. If the file
isn't in the current directory, the full path and name can be specified.
For `[array_name_prefix]` an optional prefix for the names of the arrays created
can be given. If this parameter isn't provided, `compressed_image` will be used
for the prefix.
If the program is unable to produce proper output, an error message will be
given and a non-zero exit code will be returned.
## Input file decoding
The input file should be a PNG file containing the image to be converted.
The height of the image must be a multiple of 8 pixels (8, 16, 24, 32, ...).
The width can be any size.
The image will be translated to a raw array of 32 bit RGBA (Red, Green, Blue,
Alpha) pixels internally before being processed to output. Ideally, pixels that
are to be drawn (represented as a 1 in the image output) should be fully white.
Non-drawn (0) pixels should be fully black. Pixels intended to be masked out of
the image (represented as a 0 in both the image and mask output), should be
fully transparent and their color doesn't matter.
However, after translation to RGBA, any pixel with an alpha (opaqueness) value
of 127 or less will be set as non-drawn (0) for both the image and the mask.
For the image, after the alpha value is first taken into account, pixels with a
red color value greater than 127 will be set as drawn (1) and others will be
set as non-drawn (0). For the mask, only the alpha value is used and red is
ignored. Green and blue color values are ignored for both image and mask.
### To summarize:
For the image:
Green and blue are ignored.
| Alpha | Red | Output |
|:------:|:-------:|:------:|
| <= 127 | <= 127 | 0 |
| <= 127 | > 127 | 0 |
| > 127 | <= 127 | 0 |
| > 127 | > 127 | 1 |
For the mask:
Red, green and blue are ignored.
| Alpha | Output |
|:------:|:------:|
| <= 127 | 0 |
| > 127 | 1 |
## Output
Cabi will send all output to `stdout`, which is usually the console unless
redirected. To save the output, you may be able to copy and paste it into your
editor, or you can redirect `stdout` to a file for importing. For example:
`cabi PlayerSprite.png PlayerSprite > PlayerSprite.out`
If conversion is successful, the output will be text representing C/C++ code
for two arrays, an image and a mask, that can be included in a sketch for use
by the *drawCompressed()* function. The image array will be named the same as
the prefix. The mask name will be the prefix with `_mask` appended to it.
Along with the actual array text, a comment will be included before each array
giving the input file name used and the dimensions of the image. A comment
included after each array will give the size of the array and the compression
ratio compared to the non-compressed equivalent (although the ratio is based
on the compressed array including two bytes for the bitmap dimensions compared
to a non-compressed array without bitmap dimensions).
Note that it's possible that the "compressed" array will actually end up
larger than the equivalent non-compressed one would. This is indicated by
a compression ratio greater than 1. The ratio should be noted and taken into
account when determining whether using Cabi compressed bitmaps is suitable for
the intended purpose.
If masking isn't required, the mask array can be ignored or deleted.
Note that the usage message or any error message will also be sent to `stdout`,
rather than `stderr`. Therefore, if you redirect the output to a file, in this
case the file will contain only that text.
## Using the output with *drawCompressed()*
The Arduboy2 *drawCompressed()* function doesn't natively handle a mask for
"transparent" pixels in an image. However, masking can be accomplished by
calling *drawCompressed()* twice with the same coordinates. The first call
specifies the mask array and the color BLACK. The second call specifies the
image array and the color WHITE.
An example PNG bitmap named `sample.png` is included with the program. Here is
an example Arduboy sketch that draws this bitmap with masking, using the Cabi
output imported into the sketch.
```cpp
#include <Arduboy2.h>
Arduboy2 arduboy;
// ===== Cabi output =====
// sample.png width: 32 height: 32
const PROGMEM uint8_t sample[] = {
0x1f,0x1f,0x68,0x93,0xca,0x39,0xe5,0x9c,0x72,0xca,0xe9,0x74,0x4b,0x25,0x95,0xdc,
0x6e,0xb7,0xdb,0xed,0x56,0x49,0x65,0xb7,0x4a,0x3a,0xa9,0xac,0x92,0x4e,0x3a,0xa9,
0x74,0x94,0x8c,0x6a,0xbb,0xdd,0x6e,0xb7,0x8c,0x76,0xbb,0xdd,0x6e,0xb7,0xdb,0xed,
0x76,0xbb,0xdd,0xf2,0xf1,0xa6,0xb7,0x52,0x79,0xc5,0xa4,0xbc,0x92,0x76,0x1d,0x2f,
0x9f,0xdd,0x6e,0xb7,0xdb,0xed,0x76,0xbb,0xdd,0x6e,0xb7,0x8c,0xf4,0xd9,0x15,0x23,
0x65,0x5a,0x49,0x27,0x9d,0x54,0x56,0x49,0x27,0x95,0xdd,0x2a,0xa9,0xec,0x76,0xbb,
0xdd,0x6e,0x97,0x4a,0x2a,0xb9,0x54,0xce,0x39,0xe5,0x94,0x73,0xca,0x39,0x25,0xa3,
0x05
};
// bytes:113 ratio: 0.883
const PROGMEM uint8_t sample_mask[] = {
0x1f,0x1f,0x68,0x93,0xca,0x39,0x25,0x95,0xdc,0xa6,0xd3,0xa1,0x35,0x9d,0x4e,0x6f,
0x95,0x54,0xd2,0x39,0xa9,0x74,0x94,0xe8,0xb4,0xdb,0xed,0x76,0xbb,0xdd,0x6e,0xb7,
0xdb,0xed,0x16,0x8f,0x8a,0x49,0xe1,0xd1,0x6e,0xb7,0xdb,0xed,0x76,0xbb,0xdd,0x6e,
0xb7,0x5b,0x74,0x52,0xa6,0x95,0x74,0x4e,0x2a,0xa9,0xec,0x3a,0x9d,0x0e,0xad,0xe9,
0x74,0x76,0xa9,0xa4,0x72,0x4e,0xc9,0x68,0x01
};
// bytes:73 ratio: 0.570
// =======================
void setup() {
arduboy.begin();
}
void loop() {
arduboy.clear();
arduboy.drawCompressed(20, 10, sample_mask, BLACK);
arduboy.drawCompressed(20, 10, sample, WHITE);
arduboy.display();
}
```

385
extras/cabi/cabi.c Normal file
View File

@ -0,0 +1,385 @@
/*
cabi - Compress Arduboy Image
A command line program to read a PNG file containing a bitmap image, compress
it using RLE encoding and convert it to C/C++ code suitable for use with the
Team A.R.G. drawCompressed() function. This function is included in the
Arduboy2 library.
Written by zep
https://www.lexaloffle.com/bbs/?uid=1
https://twitter.com/lexaloffle
Contributed to Team A.R.G.
Modifications by Scott Allen - July 2020, September 2020
To the extent possible under law, the author(s) have dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
Usage:
cabi in.png [array_name_prefix]
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <memory.h>
#include "lodepng/lodepng.h"
// alternative pixel order mapping
//#define READING_ORDER 1
unsigned reading_order = 0;
// one byte encodes a 1x8 stick; low byte at top
// for testing
void draw_sprite_ascii(const uint8_t *dat, unsigned w, unsigned h)
{
unsigned x, y;
unsigned row, bit;
for (y = 0; y < h; y ++)
{
row = y/8;
bit = y&7;
for (x = 0; x < w; x++)
{
if (dat[x + (row*w)] & (1 << bit))
printf("#");
else
printf(".");
}
printf("\n");
}
}
// ----------------------------------------------------------------------------
// :: Decompress
// ----------------------------------------------------------------------------
// compression / decompression session state
typedef struct CSESSION{
unsigned byte;
unsigned bit;
const uint8_t *src;
uint8_t *dest;
unsigned src_pos;
unsigned out_pos;
unsigned w, h;
}CSESSION;
static CSESSION cs;
// get an n-bit number from the compressed data stream
static unsigned getval(unsigned bits)
{
unsigned val = 0;
unsigned i;
for (i = 0; i < bits; i++)
{
if (cs.bit == 0x100)
{
cs.bit = 0x1;
cs.byte = cs.src[cs.src_pos];
cs.src_pos ++;
}
if (cs.byte & cs.bit)
val += (1 << i);
cs.bit <<= 1;
}
return val;
}
// decompress_rle
// if not NULL, w and h give back the size of the sprite.
void draw_compressed_sprite_ascii(const uint8_t *src)
{
unsigned col;
unsigned bl, len;
unsigned i;
unsigned w, h;
unsigned x, y;
unsigned total = 0;
memset(&cs, 0, sizeof(cs));
cs.src = src;
cs.bit = 0x100;
cs.src_pos = 0;
// header
w = getval(8) + 1;
h = getval(8) + 1;
col = getval(1); // starting colour
x = y = 0;
while (y < h)
{
bl = 1;
while (!getval(1))
bl += 2;
len = getval(bl)+1; // span length
for (i = 0; i < len; i++)
{
//if ((x%8) == 0) // every 8th bit (format test)
printf("%s", col ? "#":".");
if (col) total++;
x++;
if (x >= w)
{
printf("\n");
y ++;
x = 0;
}
//if ((x+y*w)%(w*8) == 0) printf("\n"); // print every 8th line (format test)
}
col = 1-col; // toggle
}
printf("\ntotal: %u\n", total);
}
// ----------------------------------------------------------------------------
// :: Compress
// ----------------------------------------------------------------------------
/*
getcol
pos is the index of the pixel: 0 .. w*h-1
*/
static unsigned getcol(unsigned pos)
{
unsigned x, y;
// display order
if (reading_order == 0)
{
if (cs.src[pos/8] & (1 << (pos&7))) return 1;
return 0;
}
// reading order (compresses slightly better but harder to optimize sprite blit)
// or use this after loading png into display order (no need for extra conversion)
x = (pos % cs.w);
y = (pos / cs.w);
if (cs.src[x + ((y/8)*cs.w)] & (1 << (y&7))) return 1;
return 0;
}
static unsigned find_rlen(unsigned pos, unsigned plen)
{
unsigned col;
unsigned pos0;
col = getcol(pos);
pos0 = pos;
while(getcol(pos) == col && pos < plen)
pos ++;
return pos-pos0;
}
// write a bit to the stream. non-zero val means 1, otherwise 0.
static void putbit(unsigned val)
{
if (val) cs.byte |= cs.bit;
cs.bit <<= 1;
if (cs.bit == 0x100)
{
//output byte
if (cs.out_pos != 0) printf(",");
if (cs.out_pos % 16 == 0) printf("\n");
printf("0x%02x", cs.byte);
cs.out_pos ++;
cs.bit = 0x1;
cs.byte = 0;
}
}
// write an n-bit (bits) number (val) to the output steam
static void putval(unsigned val, unsigned bits)
{
unsigned i;
if (bits <= 0) return;
for (i = 0; i < bits; i++)
putbit(val & (1 << i));
}
// write a span length
// a string of bits encoding the number of bits needed to encode the length,
// and then the length.
static void putsplen(unsigned len)
{
unsigned blen = 1; // how bits needed to encode length
while ((unsigned)(1 << blen) <= len) {
blen += 2;
}
// write number of bits (1-terminated string of zeroes)
putval(0,(blen-1)/2);
putval(1,1); // terminator
// write length
putval(len, blen);
}
/*
comp
compress plen 1-bit pixels from src to dest
*/
unsigned compress_rle(const uint8_t *src, unsigned w, unsigned h, char *prefix, char *suffix)
{
unsigned pos;
unsigned rlen;
printf("const PROGMEM uint8_t %s%s[] = {", prefix, suffix);
fflush(stdout);
memset(&cs, 0, sizeof(cs));
cs.src = src;
cs.bit = 1;
cs.w = w;
cs.h = h;
// header
putval(w-1, 8);
putval(h-1, 8);
putval(getcol(0), 1); // first colour
pos = 0;
// span data
while (pos < w*h)
{
rlen = find_rlen(pos, w*h);
pos += rlen;
putsplen(rlen-1);
}
// pad with zeros and flush
while (cs.bit != 0x1)
putbit(0);
printf("\n};\n");
return cs.out_pos; // bytes
}
int main(int argc, char **argv)
{
unsigned compressed_len;
unsigned w, h;
unsigned char *bmp = NULL;
unsigned char *bmp0 = NULL;
unsigned char *bmp1 = NULL;
unsigned result;
unsigned rawlen;
unsigned x, y;
unsigned row, bit;
char default_prefix[] = "compressed_image";
char *prefix = default_prefix;
if (argc < 2)
{
printf("cabi - Compress Arduboy Image\n");
printf("Convert a PNG file into RLE encoded C/C++ source\n");
printf("for use with Arduboy2 drawCompressed()\n\n");
printf("usage: cabi in.png [array_name_prefix]\n");
exit(1);
}
if (argc >= 3) {
prefix = argv[2];
}
result = lodepng_decode32_file(&bmp, &w, &h, argv[1]);
if (result != 0) {
printf("error %u: file %s: %s\n", result, argv[1], lodepng_error_text(result));
free(bmp);
exit(result);
}
if (h % 8 != 0) {
printf("error 120: file %s: image height must be a multiple of 8 but is %u\n", argv[1], h);
free(bmp);
exit(120);
}
// generate sprite and mask
rawlen = w * (h+7) / 8;
bmp0 = (unsigned char *)malloc(rawlen); memset(bmp0, 0, rawlen);
bmp1 = (unsigned char *)malloc(rawlen); memset(bmp1, 0, rawlen);
printf("// %s width: %u height: %u\n", argv[1], w, h);
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
row = y/8;
bit = y&7;
if (bmp[(x+y*w)*4 + 3] > 127) // need to be opaque to count
if (bmp[(x+y*w)*4 + 0] > 127)
{
// set sprite
bmp0[x + (row*w)] |= (1 << bit);
}
if (bmp[(x+y*w)*4 + 3] > 127)
{
// set mask
bmp1[x + (row*w)] |= (1 << bit);
}
}
}
compressed_len = compress_rle(bmp0, w, h, prefix, "");
printf("// bytes:%u ratio: %3.3f\n\n", compressed_len, (float)(compressed_len * 8)/ (float)(w*h));
compressed_len = compress_rle(bmp1, w, h, prefix, "_mask");
printf("// bytes:%u ratio: %3.3f\n\n", compressed_len, (float)(compressed_len * 8)/ (float)(w*h));
free(bmp);
free(bmp0);
free(bmp1);
return 0;
}

View File

@ -0,0 +1,21 @@
Copyright (c) 2005-2018 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
extras/cabi/sample.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

View File

@ -1,22 +1,22 @@
# File Descriptions # File Descriptions
Documentation for files contained in this repository which aren't self explanatory. Documentation for files contained in this repository that aren't self explanatory.
### /library.properties ### /library.properties
Provides information so that this library can be installed and updated in the Arduino IDE using the [Library Manager](https://www.arduino.cc/en/Guide/Libraries#toc3). Provides information so that this library can be installed and updated in the Arduino IDE using the [Library Manager](https://www.arduino.cc/en/Guide/Libraries#toc3).
The value of *version* must be set to the latest stable tagged release. This should be changed and commited just before tagging the new release. The value of *version* must be set to the latest stable tagged release. This should be changed and committed just before tagging the new release.
See the [Arduino IDE 1.5: Library specification](https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5:-Library-specification) for details. See the [Arduino IDE 1.5: Library specification](https://arduino.github.io/arduino-cli/library-specification/) for details.
### /library.json ### /library.json
This JSON file is a manifest used by the [PlatformIO IDE](http://platformio.org/) to make this library available in its [Library Manager](http://docs.platformio.org/en/latest/librarymanager/index.html). This JSON file is a manifest used by the [PlatformIO IDE](https://platformio.org/) to make this library available in its [Library Manager](https://docs.platformio.org/en/latest/librarymanager/index.html).
The value of *version* must be set to the latest stable tagged release. This should be changed and commited just before tagging the new release. The value of *version* must be set to the latest stable tagged release. This should be changed and committed just before tagging the new release.
See the [PlatformIO library.json](http://docs.platformio.org/en/latest/librarymanager/config.html) documentation for details. See the [PlatformIO library.json](https://docs.platformio.org/en/latest/librarymanager/config.html) documentation for details.
### /extras/assets/arduboy_logo.png<br>/extras/assets/arduboy_screen.png ### /extras/assets/arduboy_logo.png<br>/extras/assets/arduboy_screen.png

View File

@ -0,0 +1,15 @@
The following files contain the Arduboy2 library version number.
They must be updated to match the release tag before a new version is released.
library.properties
version=
library.json
"version":
src/Arduboy2.h
#define ARDUBOY_LIB_VER
extras/Doxyfile
PROJECT_NUMBER =

View File

@ -1,5 +1,5 @@
####################################### #######################################
# Syntax Coloring Map For Arduboy # Syntax Coloring Map For Arduboy2
####################################### #######################################
####################################### #######################################
@ -8,19 +8,28 @@
Arduboy2 KEYWORD1 Arduboy2 KEYWORD1
Arduboy2Base KEYWORD1 Arduboy2Base KEYWORD1
BeepPin1 KEYWORD1
BeepPin2 KEYWORD1
Point KEYWORD1
Rect KEYWORD1
Sprites KEYWORD1 Sprites KEYWORD1
SpritesB KEYWORD1
####################################### #######################################
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)
####################################### #######################################
allPixelsOn KEYWORD2 allPixelsOn KEYWORD2
anyPressed KEYWORD2
begin KEYWORD2 begin KEYWORD2
blank KEYWORD2 blank KEYWORD2
boot KEYWORD2 boot KEYWORD2
bootLogo KEYWORD2 bootLogo KEYWORD2
bootLogoCompressed KEYWORD2 bootLogoCompressed KEYWORD2
bootLogoExtra KEYWORD2
bootLogoShell KEYWORD2 bootLogoShell KEYWORD2
bootLogoSpritesBOverwrite KEYWORD2
bootLogoSpritesBSelfMasked KEYWORD2
bootLogoSpritesOverwrite KEYWORD2 bootLogoSpritesOverwrite KEYWORD2
bootLogoSpritesSelfMasked KEYWORD2 bootLogoSpritesSelfMasked KEYWORD2
bootLogoText KEYWORD2 bootLogoText KEYWORD2
@ -47,6 +56,7 @@ drawSlowXYBitmap KEYWORD2
drawTriangle KEYWORD2 drawTriangle KEYWORD2
enabled KEYWORD2 enabled KEYWORD2
everyXFrames KEYWORD2 everyXFrames KEYWORD2
exitToBootloader KEYWORD2
fillCircle KEYWORD2 fillCircle KEYWORD2
fillRect KEYWORD2 fillRect KEYWORD2
fillRoundRect KEYWORD2 fillRoundRect KEYWORD2
@ -55,9 +65,21 @@ fillTriangle KEYWORD2
flashlight KEYWORD2 flashlight KEYWORD2
flipVertical KEYWORD2 flipVertical KEYWORD2
flipHorizontal KEYWORD2 flipHorizontal KEYWORD2
freeRGBled KEYWORD2
generateRandomSeed KEYWORD2
getBuffer KEYWORD2 getBuffer KEYWORD2
getCharacterHeight KEYWORD2
getCharacterSpacing KEYWORD2
getCharacterWidth KEYWORD2
getCursorX KEYWORD2 getCursorX KEYWORD2
getCursorY KEYWORD2 getCursorY KEYWORD2
getLineSpacing KEYWORD2
getPixel KEYWORD2
getTextBackground KEYWORD2
getTextColor KEYWORD2
getTextRawMode KEYWORD2
getTextSize KEYWORD2
getTextWrap KEYWORD2
height KEYWORD2 height KEYWORD2
idle KEYWORD2 idle KEYWORD2
initRandomSeed KEYWORD2 initRandomSeed KEYWORD2
@ -73,26 +95,42 @@ paint8Pixels KEYWORD2
paintScreen KEYWORD2 paintScreen KEYWORD2
pollButtons KEYWORD2 pollButtons KEYWORD2
pressed KEYWORD2 pressed KEYWORD2
readShowBootLogoFlag KEYWORD2
readShowBootLogoLEDsFlag KEYWORD2
readShowUnitNameFlag KEYWORD2 readShowUnitNameFlag KEYWORD2
readUnitID KEYWORD2 readUnitID KEYWORD2
readUnitName KEYWORD2 readUnitName KEYWORD2
safeMode KEYWORD2 safeMode KEYWORD2
saveOnOff KEYWORD2 saveOnOff KEYWORD2
setCursor KEYWORD2 setCursor KEYWORD2
setCursorX KEYWORD2
setCursorY KEYWORD2
setFrameDuration KEYWORD2
setFrameRate KEYWORD2 setFrameRate KEYWORD2
setRGBled KEYWORD2 setRGBled KEYWORD2
setTextColor KEYWORD2
setTextBackground KEYWORD2 setTextBackground KEYWORD2
setTextColor KEYWORD2
setTextRawMode KEYWORD2
setTextSize KEYWORD2 setTextSize KEYWORD2
setTextWrap KEYWORD2 setTextWrap KEYWORD2
SPItransfer KEYWORD2 SPItransfer KEYWORD2
SPItransferAndRead KEYWORD2
systemButtons KEYWORD2 systemButtons KEYWORD2
toggle KEYWORD2 toggle KEYWORD2
waitNoButtons KEYWORD2
width KEYWORD2 width KEYWORD2
writeShowBootLogoFlag KEYWORD2
writeShowBootLogoLEDsFlag KEYWORD2
writeShowUnitNameFlag KEYWORD2 writeShowUnitNameFlag KEYWORD2
writeUnitID KEYWORD2 writeUnitID KEYWORD2
writeUnitName KEYWORD2 writeUnitName KEYWORD2
# Arduboy2Beep classes
freq KEYWORD2
noTone KEYWORD2
timer KEYWORD2
tone KEYWORD2
# Sprites class # Sprites class
drawErase KEYWORD2 drawErase KEYWORD2
drawExternalMask KEYWORD2 drawExternalMask KEYWORD2
@ -100,12 +138,30 @@ drawOverwrite KEYWORD2
drawPlusMask KEYWORD2 drawPlusMask KEYWORD2
drawSelfMasked KEYWORD2 drawSelfMasked KEYWORD2
##### Public variables #####
audio KEYWORD2
arduboy_logo KEYWORD2
arduboy_logo_compressed KEYWORD2
arduboy_logo_sprite KEYWORD2
currentButtonState KEYWORD2
font5x7 KEYWORD2
frameCount KEYWORD2
previousButtonState KEYWORD2
sBuffer KEYWORD2
# Arduboy2Beep classes
duration KEYWORD2
####################################### #######################################
# Constants (LITERAL1) # Constants (LITERAL1)
####################################### #######################################
ARDUBOY_LIB_VER LITERAL1 ARDUBOY_LIB_VER LITERAL1
ARDUBOY_UNIT_NAME_LEN LITERAL1
ARDUBOY_UNIT_NAME_BUFFER_SIZE LITERAL1
EEPROM_STORAGE_SPACE_START LITERAL1 EEPROM_STORAGE_SPACE_START LITERAL1
HEIGHT LITERAL1 HEIGHT LITERAL1
@ -134,3 +190,5 @@ RED_LED LITERAL1
RGB_OFF LITERAL1 RGB_OFF LITERAL1
RGB_ON LITERAL1 RGB_ON LITERAL1
ARDUBOY_NO_USB LITERAL1

View File

@ -7,8 +7,11 @@
"type": "git", "type": "git",
"url": "https://github.com/MLXXXp/Arduboy2.git" "url": "https://github.com/MLXXXp/Arduboy2.git"
}, },
"version": "4.0.0", "version": "6.0.0",
"exclude": "extras", "export":
{
"exclude": "extras"
},
"frameworks": "arduino", "frameworks": "arduino",
"platforms": "atmelavr" "platforms": "atmelavr"
} }

View File

@ -1,5 +1,5 @@
name=Arduboy2 name=Arduboy2
version=4.0.0 version=6.0.0
author=Chris J. Martinez, Kevin Bates, Josh Goebel, Scott Allen, Ross O. Shoger author=Chris J. Martinez, Kevin Bates, Josh Goebel, Scott Allen, Ross O. Shoger
maintainer=Scott Allen <saydisp-git@yahoo.ca> maintainer=Scott Allen <saydisp-git@yahoo.ca>
sentence=An alternative library for use with the Arduboy game system. sentence=An alternative library for use with the Arduboy game system.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,6 @@
*/ */
#include "Arduboy2.h" #include "Arduboy2.h"
#include "Arduboy2Audio.h"
bool Arduboy2Audio::audio_enabled = false; bool Arduboy2Audio::audio_enabled = false;
@ -43,12 +42,12 @@ void Arduboy2Audio::toggle()
void Arduboy2Audio::saveOnOff() void Arduboy2Audio::saveOnOff()
{ {
EEPROM.update(EEPROM_AUDIO_ON_OFF, audio_enabled); EEPROM.update(Arduboy2Base::eepromAudioOnOff, audio_enabled);
} }
void Arduboy2Audio::begin() void Arduboy2Audio::begin()
{ {
if (EEPROM.read(EEPROM_AUDIO_ON_OFF)) if (EEPROM.read(Arduboy2Base::eepromAudioOnOff))
on(); on();
else else
off(); off();

View File

@ -28,7 +28,7 @@
* *
* Example: * Example:
* *
* \code * \code{.cpp}
* #include <Arduboy2.h> * #include <Arduboy2.h>
* *
* Arduboy2 arduboy; * Arduboy2 arduboy;
@ -52,7 +52,7 @@
* `enabled()` type function to be passed as a parameter in the constructor, * `enabled()` type function to be passed as a parameter in the constructor,
* like so: * like so:
* *
* \code * \code{.cpp}
* #include <Arduboy2.h> * #include <Arduboy2.h>
* #include <ArduboyTones.h> * #include <ArduboyTones.h>
* *
@ -80,12 +80,9 @@ class Arduboy2Audio
* *
* \details * \details
* The speaker is initialized based on the current mute setting saved in * The speaker is initialized based on the current mute setting saved in
* system EEPROM. This function is called by `Arduboy2Base::begin()` so it * system EEPROM.
* isn't normally required to call it within a sketch. However, if
* `Arduboy2Core::boot()` is used instead of `Arduboy2Base::begin()` and the
* sketch includes sound, then this function should be called after `boot()`.
*/ */
void static begin(); static void begin();
/** \brief /** \brief
* Turn sound on. * Turn sound on.
@ -97,7 +94,7 @@ class Arduboy2Audio
* *
* \see off() toggle() saveOnOff() * \see off() toggle() saveOnOff()
*/ */
void static on(); static void on();
/** \brief /** \brief
* Turn sound off (mute). * Turn sound off (mute).
@ -109,7 +106,7 @@ class Arduboy2Audio
* *
* \see on() toggle() saveOnOff() * \see on() toggle() saveOnOff()
*/ */
void static off(); static void off();
/** \brief /** \brief
* Toggle the sound on/off state. * Toggle the sound on/off state.
@ -122,7 +119,7 @@ class Arduboy2Audio
* *
* \see on() off() saveOnOff() * \see on() off() saveOnOff()
*/ */
void static toggle(); static void toggle();
/** \brief /** \brief
* Save the current sound state in EEPROM. * Save the current sound state in EEPROM.
@ -138,7 +135,7 @@ class Arduboy2Audio
* *
* \see on() off() toggle() * \see on() off() toggle()
*/ */
void static saveOnOff(); static void saveOnOff();
/** \brief /** \brief
* Get the current sound state. * Get the current sound state.
@ -152,10 +149,10 @@ class Arduboy2Audio
* *
* \see on() off() toggle() * \see on() off() toggle()
*/ */
bool static enabled(); static bool enabled();
protected: protected:
bool static audio_enabled; static bool audio_enabled;
}; };
#endif #endif

155
src/Arduboy2Beep.cpp Normal file
View File

@ -0,0 +1,155 @@
/**
* @file Arduboy2Beep.cpp
* \brief
* Classes to generate simple square wave tones on the Arduboy speaker pins.
*/
#include <Arduino.h>
#include "Arduboy2Beep.h"
#ifndef AB_DEVKIT
// Speaker pin 1, Timer 3A, Port C bit 6, Arduino pin 5
uint8_t BeepPin1::duration = 0;
void BeepPin1::begin()
{
TCCR3A = 0;
TCCR3B = (bit(WGM32) | bit(CS31)); // CTC mode. Divide by 8 clock prescale
}
void BeepPin1::tone(uint16_t count)
{
tone(count, 0);
}
void BeepPin1::tone(uint16_t count, uint8_t dur)
{
duration = dur;
TCCR3A = bit(COM3A0); // set toggle on compare mode (which connects the pin)
OCR3A = count; // load the count (16 bits), which determines the frequency
}
void BeepPin1::timer()
{
if (duration && (--duration == 0)) {
TCCR3A = 0; // set normal mode (which disconnects the pin)
}
}
void BeepPin1::noTone()
{
duration = 0;
TCCR3A = 0; // set normal mode (which disconnects the pin)
}
// Speaker pin 2, Timer 4A, Port C bit 7, Arduino pin 13
uint8_t BeepPin2::duration = 0;
void BeepPin2::begin()
{
TCCR4A = 0; // normal mode. Disable PWM
TCCR4B = bit(CS43); // divide by 128 clock prescale
TCCR4D = 0; // normal mode
TC4H = 0; // toggle pin at count = 0
OCR4A = 0; // "
}
void BeepPin2::tone(uint16_t count)
{
tone(count, 0);
}
void BeepPin2::tone(uint16_t count, uint8_t dur)
{
duration = dur;
TCCR4A = bit(COM4A0); // set toggle on compare mode (which connects the pin)
TC4H = highByte(count); // load the count (10 bits),
OCR4C = lowByte(count); // which determines the frequency
}
void BeepPin2::timer()
{
if (duration && (--duration == 0)) {
TCCR4A = 0; // set normal mode (which disconnects the pin)
}
}
void BeepPin2::noTone()
{
duration = 0;
TCCR4A = 0; // set normal mode (which disconnects the pin)
}
#else /* AB_DEVKIT */
// *** The pins used for the speaker on the DevKit cannot be directly
// controlled by a timer/counter. The following "dummy" functions will
// compile and operate properly but no sound will be produced
uint8_t BeepPin1::duration = 0;
void BeepPin1::begin()
{
}
void BeepPin1::tone(uint16_t count)
{
tone(count, 0);
}
void BeepPin1::tone(uint16_t count, uint8_t dur)
{
(void) count; // parameter not used
duration = dur;
}
void BeepPin1::timer()
{
if (duration) {
--duration;
}
}
void BeepPin1::noTone()
{
duration = 0;
}
uint8_t BeepPin2::duration = 0;
void BeepPin2::begin()
{
}
void BeepPin2::tone(uint16_t count)
{
tone(count, 0);
}
void BeepPin2::tone(uint16_t count, uint8_t dur)
{
(void) count; // parameter not used
duration = dur;
}
void BeepPin2::timer()
{
if (duration) {
--duration;
}
}
void BeepPin2::noTone()
{
duration = 0;
}
#endif

362
src/Arduboy2Beep.h Normal file
View File

@ -0,0 +1,362 @@
/**
* @file Arduboy2Beep.h
* \brief
* Classes to generate simple square wave tones on the Arduboy speaker pins.
*/
#ifndef ARDUBOY2_BEEP_H
#define ARDUBOY2_BEEP_H
/** \brief
* Play simple square wave tones using speaker pin 1.
*
* \note
* Class `BeepPin2` provides identical functions for playing tones on speaker
* pin 2. Both classes can be used in the same sketch to allow playing
* two tones at the same time. To do this, the `begin()` and `timer()`
* functions of both classes must be used.
*
* \details
* This class can be used to play square wave tones on speaker pin 1.
* The functions are designed to produce very small and efficient code.
*
* A tone can be set to play for a given duration, or continuously until
* stopped or replaced by a new tone. No interrupts are used. A tone is
* generated by a hardware timer/counter directly toggling the pin,
* so once started, no CPU cycles are used to actually play the tone.
* The program continues to run while a tone is playing. However, a small
* amount of code is required to time and stop a tone after a given duration.
*
* Tone frequencies can range from 15.26Hz to 1000000Hz.
*
* Although there's no specific code to handle mute control, the
* `Arduboy2Audio` class will work since it has code to mute sound by setting
* the speaker pins to input mode and unmute by setting the pins as outputs.
* The `BeepPin1` class doesn't interfere with this operation.
*
* In order to avoid needing to use interrupts, the duration of tones is timed
* by calling the `timer()` function continuously at a fixed interval.
* The duration of a tone is given by specifying the number of times `timer()`
* will be called before stopping the tone.
*
* For sketches that use `Arduboy2::nextFrame()`, or some other method to
* generate frames at a fixed rate, `timer()` can be called once per frame.
* Tone durations will then be given as the number of frames to play the tone
* for. For example, with a rate of 60 frames per second a duration of 30
* would be used to play a tone for half a second.
*
* The variable named #duration is the counter that times the duration of a
* tone. A sketch can determine if a tone is currently playing by testing if
* the #duration variable is non-zero (assuming it's a timed tone, not a
* continuous tone).
*
* To keep the code small and efficient, the frequency of a tone is specified
* by the actual count value to be loaded into to timer/counter peripheral.
* The frequency will be determined by the count provided and the clock rate
* of the timer/counter. In order to allow a tone's frequency to be specified
* in hertz (cycles per second) the `freq()` helper function is provided,
* which converts a given frequency to the required count value.
*
* NOTE that it is intended that `freq()` only be called with constant values.
* If `freq()` is called with a variable, code to perform floating point math
* will be included in the sketch, which will likely greatly increase the
* sketch's code size unless the sketch also uses floating point math for
* other purposes.
*
* The formulas for frequency/count conversion are:
*
* count=(1000000/frequency)-1
* frequency=1000000/(count+1)
*
* Counts must be between 0 and 65535.
*
* All members of the class are static, so it's not necessary to create an
* instance of the class in order to use it. However, creating an instance
* doesn't produce any more code and it may make the source code smaller and
* make it easier to switch to the `BeepPin2` class if it becomes necessary.
*
* The following is a basic example sketch, which will generate a tone when
* a button is pressed.
*
* \code{.cpp}
* #include <Arduboy2.h>
* // There's no need to #include <Arduboy2Beep.h>
* // It will be included in Arduboy2.h
*
* Arduboy2 arduboy;
* BeepPin1 beep; // class instance for speaker pin 1
*
* void setup() {
* arduboy.begin();
* arduboy.setFrameRate(50);
* beep.begin(); // set up the hardware for playing tones
* }
*
* void loop() {
* if (!arduboy.nextFrame()) {
* return;
* }
*
* beep.timer(); // handle tone duration
*
* arduboy.pollButtons();
*
* if (arduboy.justPressed(A_BUTTON)) {
* // play a 1000Hz tone for 100 frames (2 seconds at 50 FPS)
* // beep.freq(1000) is used to convert 1000Hz to the required count
* beep.tone(beep.freq(1000), 100);
* }
* }
* \endcode
*
* \note
* These functions, and the equivalents in class `BeepPin2`, will not work with
* a DevKit Arduboy because the speaker pins used cannot be directly controlled
* by a timer/counter. "Dummy" functions are provided so a sketch will compile
* and work properly but no sound will be produced.
*
* \see BeepPin2
*/
class BeepPin1
{
public:
/** \brief
* The counter used by the `timer()` function to time the duration of a tone.
*
* \details
* This variable is set by the `dur` parameter of the `tone()` function.
* It is then decremented each time the `timer()` function is called, if its
* value isn't 0. When `timer()` decrements it to 0, a tone that is playing
* will be stopped.
*
* A sketch can determine if a tone is currently playing by testing if
* this variable is non-zero (assuming it's a timed tone, not a continuous
* tone).
*
* Example:
* \code{.cpp}
* beep.tone(beep.freq(1000), 15);
* while (beep.duration != 0) { } // wait for the tone to stop playing
* \endcode
*
* It can also be manipulated directly by the sketch, although this should
* seldom be necessary.
*/
static uint8_t duration;
/** \brief
* Set up the hardware.
*
* \details
* Prepare the hardware for playing tones.
* This function must be called (usually in `setup()`) before using any of
* the other functions in this class.
*/
static void begin();
/** \brief
* Play a tone continually, until replaced by a new tone or stopped.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
*
* \details
* A tone is played indefinitely, until replaced by another tone or stopped
* using `noTone()`.
*
* The tone's frequency is determined by the specified count, which is loaded
* into the timer/counter that generates the tone. A desired frequency can be
* converted into the required count value using the `freq()` function.
*
* \see freq() timer() noTone()
*/
static void tone(uint16_t count);
/** \brief
* Play a tone for a given duration.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
* \param dur The duration of the tone, used by `timer()`.
*
* \details
* A tone is played for the specified duration, or until replaced by another
* tone or stopped using `noTone()`.
*
* The tone's frequency is determined by the specified count, which is loaded
* into the timer/counter that generates the tone. A desired frequency can be
* converted into the required count value using the `freq()` function.
*
* The duration value is the number of times the `timer()` function must be
* called before the tone is stopped.
*
* \see freq() timer() noTone()
*/
static void tone(uint16_t count, uint8_t dur);
/** \brief
* Handle the duration that a tone plays for.
*
* \details
* This function must be called at a constant interval, which would normally
* be once per frame, in order to stop a tone after the desired tone duration
* has elapsed.
*
* If the value of the `duration` variable is not 0, it will be decremented.
* When the `duration` variable is decremented to 0, a playing tone will be
* stopped.
*/
static void timer();
/** \brief
* Stop a tone that is playing.
*
* \details
* If a tone is playing it will be stopped. It's safe to call this function
* even if a tone isn't currently playing.
*
* \see tone()
*/
static void noTone();
/** \brief
* Convert a frequency to the required count.
*
* \param hz The frequency, in hertz (cycles per second), to be converted
* to a count.
*
* \return The required count to be loaded into the timer/counter for the
* given frequency.
*
* \details
* This helper function will convert a desired tone frequency to the closest
* value required by the `tone()` function's `count` parameter.
* The calculated count is rounded up or down to the nearest integer,
* if necessary.
*
* Example:
* \code{.cpp}
* beep.tone(beep.freq(440)); // play a 440Hz tone until stopped or replaced
* \endcode
*
* \note
* It is intended that `freq()` only be called with constant values.
* If `freq()` is called with a variable, code to perform floating point math
* will be included in the sketch, which will likely greatly increase the
* sketch's code size unless the sketch also uses floating point math for
* other purposes.
*/
static constexpr uint16_t freq(const float hz)
{
return (uint16_t) (((F_CPU / 8 / 2) + (hz / 2)) / hz) - 1;
}
};
/** \brief
* Play simple square wave tones using speaker pin 2.
*
* \details
* This class contains the same functions as class `BeepPin1` except they use
* speaker pin 2 instead of speaker pin 1.
*
* Using `BeepPin1` is more desirable, as it uses a 16 bit Timer, which can
* produce a greater frequency range and resolution than the 10 bit Timer
* used by `BeepPin2`. However, if the sketch also includes other sound
* generating code that uses speaker pin 1, `BeepPin2` can be used to avoid
* conflict.
*
* Tone frequencies on speaker pin 2 can range from 61.04Hz to 15625Hz using
* allowed counts from 3 to 1023.
*
* The formulas for frequency/count conversion are:
*
* count=(62500/frequency)-1
* frequency=62500/(count+1)
*
* See the documentation for `BeepPin1` for more details.
*
* \see BeepPin1
*/
class BeepPin2
{
public:
/** \brief
* The counter used by the `timer()` function to time the duration of a tone
* played on speaker pin 2.
*
* \details
* For details see `BeepPin1::duration`.
*/
static uint8_t duration;
/** \brief
* Set up the hardware for playing tones using speaker pin 2.
*
* \details
* For details see `BeepPin1::begin()`.
*/
static void begin();
/** \brief
* Play a tone on speaker pin 2 continually, until replaced by a new tone
* or stopped.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
*
* \details
* For details see `BeepPin1::tone(uint16_t)`.
*/
static void tone(uint16_t count);
/** \brief
* Play a tone on speaker pin 2 for a given duration.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
* \param dur The duration of the tone, used by `timer()`.
*
* \details
* For details see `BeepPin1::tone(uint16_t, uint8_t)`.
*/
static void tone(uint16_t count, uint8_t dur);
/** \brief
* Handle the duration that a tone on speaker pin 2 plays for.
*
* \details
* For details see `BeepPin1::timer()`.
*/
static void timer();
/** \brief
* Stop a tone that is playing on speaker pin 2.
*
* \details
* For details see `BeepPin1::noTone()`.
*/
static void noTone();
/** \brief
* Convert a frequency to the required count for speaker pin 2.
*
* \param hz The frequency, in hertz (cycles per second), to be converted
* to a count.
*
* \return The required count to be loaded into the timer/counter for the
* given frequency.
*
* \details
* For details see `BeepPin1::freq()`.
*/
static constexpr uint16_t freq(const float hz)
{
return (uint16_t) (((F_CPU / 128 / 2) + (hz / 2)) / hz) - 1;
}
};
#endif

View File

@ -6,7 +6,15 @@
#include "Arduboy2Core.h" #include "Arduboy2Core.h"
const uint8_t PROGMEM lcdBootProgram[] = { #include <avr/wdt.h>
//========================================
//========== class Arduboy2Core ==========
//========================================
// Commands sent to the OLED display to initialize it
const PROGMEM uint8_t Arduboy2Core::lcdBootProgram[] = {
// boot defaults are commented out but left here in case they // boot defaults are commented out but left here in case they
// might prove useful for reference // might prove useful for reference
// //
@ -70,9 +78,6 @@ const uint8_t PROGMEM lcdBootProgram[] = {
// 0x22, 0x00, PAGE_ADDRESS_END // 0x22, 0x00, PAGE_ADDRESS_END
}; };
Arduboy2Core::Arduboy2Core() { }
void Arduboy2Core::boot() void Arduboy2Core::boot()
{ {
#ifdef ARDUBOY_SET_CPU_8MHZ #ifdef ARDUBOY_SET_CPU_8MHZ
@ -80,7 +85,7 @@ void Arduboy2Core::boot()
setCPUSpeed8MHz(); setCPUSpeed8MHz();
#endif #endif
// Select the ADC input here so a delay isn't required in initRandomSeed() // Select the ADC input here so a delay isn't required in generateRandomSeed()
ADMUX = RAND_SEED_IN_ADMUX; ADMUX = RAND_SEED_IN_ADMUX;
bootPins(); bootPins();
@ -117,10 +122,10 @@ void Arduboy2Core::bootPins()
_BV(B_BUTTON_BIT); _BV(B_BUTTON_BIT);
// Port B INPUT or LOW (none) // Port B INPUT or LOW (none)
// Port B inputs // Port B inputs
DDRB &= ~(_BV(B_BUTTON_BIT)); DDRB &= ~(_BV(B_BUTTON_BIT) | _BV(SPI_MISO_BIT));
// Port B outputs // Port B outputs
DDRB |= _BV(RED_LED_BIT) | _BV(GREEN_LED_BIT) | _BV(BLUE_LED_BIT) | DDRB |= _BV(RED_LED_BIT) | _BV(GREEN_LED_BIT) | _BV(BLUE_LED_BIT) |
_BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT); _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT) | _BV(SPI_SS_BIT);
// Port C // Port C
// Speaker: Not set here. Controlled by audio class // Speaker: Not set here. Controlled by audio class
@ -158,15 +163,17 @@ void Arduboy2Core::bootPins()
_BV(BLUE_LED_BIT); _BV(BLUE_LED_BIT);
// Port B INPUT or LOW (none) // Port B INPUT or LOW (none)
// Port B inputs // Port B inputs
DDRB &= ~(_BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT)); DDRB &= ~(_BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT) |
_BV(SPI_MISO_BIT));
// Port B outputs // Port B outputs
DDRB |= _BV(BLUE_LED_BIT) | _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT); DDRB |= _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT) | _BV(SPI_SS_BIT) |
_BV(BLUE_LED_BIT);
// Port C INPUT_PULLUP or HIGH // Port C INPUT_PULLUP or HIGH
PORTE |= _BV(RIGHT_BUTTON_BIT); PORTC |= _BV(RIGHT_BUTTON_BIT);
// Port C INPUT or LOW (none) // Port C INPUT or LOW (none)
// Port C inputs // Port C inputs
DDRE &= ~(_BV(RIGHT_BUTTON_BIT)); DDRC &= ~(_BV(RIGHT_BUTTON_BIT));
// Port C outputs (none) // Port C outputs (none)
// Port D INPUT_PULLUP or HIGH // Port D INPUT_PULLUP or HIGH
@ -234,7 +241,7 @@ void Arduboy2Core::SPItransfer(uint8_t data)
SPDR = data; SPDR = data;
/* /*
* The following NOP introduces a small delay that can prevent the wait * The following NOP introduces a small delay that can prevent the wait
* loop form iterating when running at the maximum speed. This gives * loop from iterating when running at the maximum speed. This gives
* about 10% more speed, even if it seems counter-intuitive. At lower * about 10% more speed, even if it seems counter-intuitive. At lower
* speeds it is unnoticed. * speeds it is unnoticed.
*/ */
@ -242,15 +249,24 @@ void Arduboy2Core::SPItransfer(uint8_t data)
while (!(SPSR & _BV(SPIF))) { } // wait while (!(SPSR & _BV(SPIF))) { } // wait
} }
// Write to and read from the SPI bus (out to MOSI pin, in from MISO pin)
uint8_t Arduboy2Core::SPItransferAndRead(uint8_t data)
{
SPItransfer(data);
return SPDR;
}
void Arduboy2Core::safeMode() void Arduboy2Core::safeMode()
{ {
if (buttonsState() == UP_BUTTON) if (buttonsState() == UP_BUTTON)
{ {
digitalWriteRGB(RED_LED, RGB_ON); digitalWriteRGB(RED_LED, RGB_ON);
#ifndef ARDUBOY_CORE // for Arduboy core timer 0 should remain enabled
// prevent the bootloader magic number from being overwritten by timer 0 // prevent the bootloader magic number from being overwritten by timer 0
// when a timer variable overlaps the magic number location // when a timer variable overlaps the magic number location
power_timer0_disable(); power_timer0_disable();
#endif
while (true) { } while (true) { }
} }
@ -261,17 +277,18 @@ void Arduboy2Core::safeMode()
void Arduboy2Core::idle() void Arduboy2Core::idle()
{ {
set_sleep_mode(SLEEP_MODE_IDLE); SMCR = _BV(SE); // select idle mode and enable sleeping
sleep_mode(); sleep_cpu();
SMCR = 0; // disable sleeping
} }
void Arduboy2Core::bootPowerSaving() void Arduboy2Core::bootPowerSaving()
{ {
// disable Two Wire Interface (I2C) and the ADC // disable Two Wire Interface (I2C) and the ADC
// All other bits will be written with 0 so will be enabled
PRR0 = _BV(PRTWI) | _BV(PRADC); PRR0 = _BV(PRTWI) | _BV(PRADC);
// disable USART1 // disable USART1
PRR1 = _BV(PRUSART1); PRR1 |= _BV(PRUSART1);
// All other bits will be written with 0 so will be enabled
} }
// Shut down the display // Shut down the display
@ -291,10 +308,6 @@ void Arduboy2Core::displayOn()
bootOLED(); bootOLED();
} }
uint8_t Arduboy2Core::width() { return WIDTH; }
uint8_t Arduboy2Core::height() { return HEIGHT; }
/* Drawing */ /* Drawing */
@ -313,6 +326,39 @@ void Arduboy2Core::paintScreen(const uint8_t *image)
// paint from a memory buffer, this should be FAST as it's likely what // paint from a memory buffer, this should be FAST as it's likely what
// will be used by any buffer based subclass // will be used by any buffer based subclass
//
// The following assembly code runs "open loop". It relies on instruction
// execution times to allow time for each byte of data to be clocked out.
// It is specifically tuned for a 16MHz CPU clock and SPI clocking at 8MHz.
void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
{
uint16_t count;
asm volatile (
" ldi %A[count], %[len_lsb] \n\t" //for (len = WIDTH * HEIGHT / 8)
" ldi %B[count], %[len_msb] \n\t"
"1: ld __tmp_reg__, %a[ptr] ;2 \n\t" //tmp = *(image)
" out %[spdr], __tmp_reg__ ;1 \n\t" //SPDR = tmp
" cpse %[clear], __zero_reg__ ;1/2 \n\t" //if (clear) tmp = 0;
" mov __tmp_reg__, __zero_reg__ ;1 \n\t"
"2: sbiw %A[count], 1 ;2 \n\t" //len --
" sbrc %A[count], 0 ;1/2 \n\t" //loop twice for cheap delay
" rjmp 2b ;2 \n\t"
" st %a[ptr]+, __tmp_reg__ ;2 \n\t" //*(image++) = tmp
" brne 1b ;1/2 :18 \n\t" //len > 0
" in __tmp_reg__, %[spsr] \n\t" //read SPSR to clear SPIF
: [ptr] "+&e" (image),
[count] "=&w" (count)
: [spdr] "I" (_SFR_IO_ADDR(SPDR)),
[spsr] "I" (_SFR_IO_ADDR(SPSR)),
[len_msb] "M" (WIDTH * (HEIGHT / 8 * 2) >> 8), // 8: pixels per byte
[len_lsb] "M" (WIDTH * (HEIGHT / 8 * 2) & 0xFF), // 2: for delay loop multiplier
[clear] "r" (clear)
);
}
#if 0
// For reference, this is the "closed loop" C++ version of paintScreen()
// used prior to the above version.
void Arduboy2Core::paintScreen(uint8_t image[], bool clear) void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
{ {
uint8_t c; uint8_t c;
@ -349,6 +395,7 @@ void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
} }
while (!(SPSR & _BV(SPIF))) { } // wait for the last byte to be sent while (!(SPSR & _BV(SPIF))) { } // wait for the last byte to be sent
} }
#endif
void Arduboy2Core::blank() void Arduboy2Core::blank()
{ {
@ -394,10 +441,17 @@ void Arduboy2Core::flipHorizontal(bool flipped)
void Arduboy2Core::setRGBled(uint8_t red, uint8_t green, uint8_t blue) void Arduboy2Core::setRGBled(uint8_t red, uint8_t green, uint8_t blue)
{ {
#ifdef ARDUBOY_10 // RGB, all the pretty colors #ifdef ARDUBOY_10 // RGB, all the pretty colors
// inversion is necessary because these are common annode LEDs // timer 0: Fast PWM, OC0A clear on compare / set at top
analogWrite(RED_LED, 255 - red); // We must stay in Fast PWM mode because timer 0 is used for system timing.
analogWrite(GREEN_LED, 255 - green); // We can't use "inverted" mode because it won't allow full shut off.
analogWrite(BLUE_LED, 255 - blue); TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00);
OCR0A = 255 - green;
// timer 1: Phase correct PWM 8 bit
// OC1A and OC1B set on up-counting / clear on down-counting (inverted). This
// allows the value to be directly loaded into the OCR with common anode LED.
TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0) | _BV(WGM10);
OCR1AL = blue;
OCR1BL = red;
#elif defined(AB_DEVKIT) #elif defined(AB_DEVKIT)
// only blue on DevKit, which is not PWM capable // only blue on DevKit, which is not PWM capable
(void)red; // parameter unused (void)red; // parameter unused
@ -406,6 +460,39 @@ void Arduboy2Core::setRGBled(uint8_t red, uint8_t green, uint8_t blue)
#endif #endif
} }
void Arduboy2Core::setRGBled(uint8_t color, uint8_t val)
{
#ifdef ARDUBOY_10
if (color == RED_LED)
{
OCR1BL = val;
}
else if (color == GREEN_LED)
{
OCR0A = 255 - val;
}
else if (color == BLUE_LED)
{
OCR1AL = val;
}
#elif defined(AB_DEVKIT)
// only blue on DevKit, which is not PWM capable
if (color == BLUE_LED)
{
bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, val ? RGB_ON : RGB_OFF);
}
#endif
}
void Arduboy2Core::freeRGBled()
{
#ifdef ARDUBOY_10
// clear the COM bits to return the pins to normal I/O mode
TCCR0A = _BV(WGM01) | _BV(WGM00);
TCCR1A = _BV(WGM10);
#endif
}
void Arduboy2Core::digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue) void Arduboy2Core::digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue)
{ {
#ifdef ARDUBOY_10 #ifdef ARDUBOY_10
@ -450,29 +537,124 @@ uint8_t Arduboy2Core::buttonsState()
{ {
uint8_t buttons; uint8_t buttons;
// using ports here is ~100 bytes smaller than digitalRead() #ifdef ARDUBOY_10
#ifdef AB_DEVKIT // up, right, left, down
buttons = ((~PINF) &
(_BV(UP_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) |
_BV(LEFT_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT)));
// A
if (bitRead(A_BUTTON_PORTIN, A_BUTTON_BIT) == 0) { buttons |= A_BUTTON; }
// B
if (bitRead(B_BUTTON_PORTIN, B_BUTTON_BIT) == 0) { buttons |= B_BUTTON; }
#elif defined(AB_DEVKIT)
// down, left, up // down, left, up
buttons = ((~PINB) & B01110000); buttons = ((~PINB) &
// right button (_BV(DOWN_BUTTON_BIT) | _BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT)));
buttons = buttons | (((~PINC) & B01000000) >> 4); // right
// A and B if (bitRead(RIGHT_BUTTON_PORTIN, RIGHT_BUTTON_BIT) == 0) { buttons |= RIGHT_BUTTON; }
buttons = buttons | (((~PINF) & B11000000) >> 6); // A
#elif defined(ARDUBOY_10) if (bitRead(A_BUTTON_PORTIN, A_BUTTON_BIT) == 0) { buttons |= A_BUTTON; }
// down, up, left right // B
buttons = ((~PINF) & B11110000); if (bitRead(B_BUTTON_PORTIN, B_BUTTON_BIT) == 0) { buttons |= B_BUTTON; }
// A (left)
buttons = buttons | (((~PINE) & B01000000) >> 3);
// B (right)
buttons = buttons | (((~PINB) & B00010000) >> 2);
#endif #endif
return buttons; return buttons;
} }
unsigned long Arduboy2Core::generateRandomSeed()
{
unsigned long seed;
power_adc_enable(); // ADC on
// do an ADC read from an unconnected input pin
ADCSRA |= _BV(ADSC); // start conversion (ADMUX has been pre-set in boot())
while (bit_is_set(ADCSRA, ADSC)) { } // wait for conversion complete
seed = ((unsigned long)ADC << 16) + micros();
power_adc_disable(); // ADC off
return seed;
}
// delay in ms with 16 bit duration // delay in ms with 16 bit duration
void Arduboy2Core::delayShort(uint16_t ms) void Arduboy2Core::delayShort(uint16_t ms)
{ {
delay((unsigned long) ms); delay((unsigned long) ms);
} }
void Arduboy2Core::exitToBootloader()
{
cli();
// set bootloader magic key
// storing two uint8_t instead of one uint16_t saves an instruction
// when high and low bytes of the magic key are the same
*(uint8_t *)MAGIC_KEY_POS = lowByte(MAGIC_KEY);
*(uint8_t *)(MAGIC_KEY_POS + 1) = highByte(MAGIC_KEY);
// enable watchdog timer reset, with 16ms timeout
wdt_reset();
WDTCSR = (_BV(WDCE) | _BV(WDE));
WDTCSR = _BV(WDE);
while (true) { }
}
//=========================================
//========== class Arduboy2NoUSB ==========
//=========================================
void Arduboy2NoUSB::mainNoUSB()
{
// disable USB
UDCON = _BV(DETACH);
UDIEN = 0;
UDINT = 0;
USBCON = _BV(FRZCLK);
UHWCON = 0;
power_usb_disable();
init();
// This would normally be done in the USB code that uses the TX and RX LEDs
TX_RX_LED_INIT;
TXLED0;
RXLED0;
// Set the DOWN button pin for INPUT_PULLUP
bitSet(DOWN_BUTTON_PORT, DOWN_BUTTON_BIT);
bitClear(DOWN_BUTTON_DDR, DOWN_BUTTON_BIT);
// Delay to give time for the pin to be pulled high if it was floating
Arduboy2Core::delayShort(10);
// if the DOWN button is pressed
if (bitRead(DOWN_BUTTON_PORTIN, DOWN_BUTTON_BIT) == 0) {
Arduboy2Core::exitToBootloader();
}
// The remainder is a copy of the Arduino main() function with the
// USB code and other unneeded code commented out.
// init() was called above.
// The call to function initVariant() is commented out to fix compiler
// error: "multiple definition of 'main'".
// The return statement is removed since this function is type void.
// init();
// initVariant();
//#if defined(USBCON)
// USBDevice.attach();
//#endif
setup();
for (;;) {
loop();
// if (serialEventRun) serialEventRun();
}
// return 0;
}

View File

@ -10,7 +10,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <avr/power.h> #include <avr/power.h>
#include <avr/sleep.h> #include <avr/sleep.h>
#include <limits.h>
// main hardware compile flags // main hardware compile flags
@ -50,12 +49,6 @@
#define RST_PORT PORTD // Display reset port #define RST_PORT PORTD // Display reset port
#define RST_BIT PORTD7 // Display reset physical bit number #define RST_BIT PORTD7 // Display reset physical bit number
#define SPI_MOSI_PORT PORTB
#define SPI_MOSI_BIT PORTB2
#define SPI_SCK_PORT PORTB
#define SPI_SCK_BIT PORTB1
#define RED_LED 10 /**< The pin number for the red color in the RGB LED. */ #define RED_LED 10 /**< The pin number for the red color in the RGB LED. */
#define GREEN_LED 11 /**< The pin number for the greem color in the RGB LED. */ #define GREEN_LED 11 /**< The pin number for the greem color in the RGB LED. */
#define BLUE_LED 9 /**< The pin number for the blue color in the RGB LED. */ #define BLUE_LED 9 /**< The pin number for the blue color in the RGB LED. */
@ -80,26 +73,38 @@
#define PIN_LEFT_BUTTON A2 #define PIN_LEFT_BUTTON A2
#define LEFT_BUTTON_PORT PORTF #define LEFT_BUTTON_PORT PORTF
#define LEFT_BUTTON_PORTIN PINF
#define LEFT_BUTTON_DDR DDRF
#define LEFT_BUTTON_BIT PORTF5 #define LEFT_BUTTON_BIT PORTF5
#define PIN_RIGHT_BUTTON A1 #define PIN_RIGHT_BUTTON A1
#define RIGHT_BUTTON_PORT PORTF #define RIGHT_BUTTON_PORT PORTF
#define RIGHT_BUTTON_PORTIN PINF
#define RIGHT_BUTTON_DDR DDRF
#define RIGHT_BUTTON_BIT PORTF6 #define RIGHT_BUTTON_BIT PORTF6
#define PIN_UP_BUTTON A0 #define PIN_UP_BUTTON A0
#define UP_BUTTON_PORT PORTF #define UP_BUTTON_PORT PORTF
#define UP_BUTTON_PORTIN PINF
#define UP_BUTTON_DDR DDRF
#define UP_BUTTON_BIT PORTF7 #define UP_BUTTON_BIT PORTF7
#define PIN_DOWN_BUTTON A3 #define PIN_DOWN_BUTTON A3
#define DOWN_BUTTON_PORT PORTF #define DOWN_BUTTON_PORT PORTF
#define DOWN_BUTTON_PORTIN PINF
#define DOWN_BUTTON_DDR DDRF
#define DOWN_BUTTON_BIT PORTF4 #define DOWN_BUTTON_BIT PORTF4
#define PIN_A_BUTTON 7 #define PIN_A_BUTTON 7
#define A_BUTTON_PORT PORTE #define A_BUTTON_PORT PORTE
#define A_BUTTON_PORTIN PINE
#define A_BUTTON_DDR DDRE
#define A_BUTTON_BIT PORTE6 #define A_BUTTON_BIT PORTE6
#define PIN_B_BUTTON 8 #define PIN_B_BUTTON 8
#define B_BUTTON_PORT PORTB #define B_BUTTON_PORT PORTB
#define B_BUTTON_PORTIN PINB
#define B_BUTTON_DDR DDRB
#define B_BUTTON_BIT PORTB4 #define B_BUTTON_BIT PORTB4
#define PIN_SPEAKER_1 5 /**< The pin number of the first lead of the speaker */ #define PIN_SPEAKER_1 5 /**< The pin number of the first lead of the speaker */
@ -112,12 +117,6 @@
#define SPEAKER_2_PORT PORTC #define SPEAKER_2_PORT PORTC
#define SPEAKER_2_DDR DDRC #define SPEAKER_2_DDR DDRC
#define SPEAKER_2_BIT PORTC7 #define SPEAKER_2_BIT PORTC7
#define RAND_SEED_IN A4 // Open analog input used for noise by initRandomSeed()
#define RAND_SEED_IN_PORTF
#define RAND_SEED_IN_BIT PORTF1
// Value for ADMUX to read the random seed pin: 2.56V reference, ADC1
#define RAND_SEED_IN_ADMUX (_BV(REFS0) | _BV(REFS1) | _BV(MUX0))
// ----------------------- // -----------------------
// ----- DevKit pins ----- // ----- DevKit pins -----
@ -135,12 +134,6 @@
#define RST_PORT PORTD // Display reset port #define RST_PORT PORTD // Display reset port
#define RST_BIT PORTD6 // Display reset physical bit number #define RST_BIT PORTD6 // Display reset physical bit number
#define SPI_MOSI_PORT PORTB
#define SPI_MOSI_BIT PORTB2
#define SPI_SCK_PORT PORTB
#define SPI_SCK_BIT PORTB1
// map all LEDs to the single TX LED on DEVKIT // map all LEDs to the single TX LED on DEVKIT
#define RED_LED 17 #define RED_LED 17
#define GREEN_LED 17 #define GREEN_LED 17
@ -161,26 +154,38 @@
// pin values for buttons, probably shouldn't use these // pin values for buttons, probably shouldn't use these
#define PIN_LEFT_BUTTON 9 #define PIN_LEFT_BUTTON 9
#define LEFT_BUTTON_PORT PORTB #define LEFT_BUTTON_PORT PORTB
#define LEFT_BUTTON_PORTIN PINB
#define LEFT_BUTTON_DDR DDRB
#define LEFT_BUTTON_BIT PORTB5 #define LEFT_BUTTON_BIT PORTB5
#define PIN_RIGHT_BUTTON 5 #define PIN_RIGHT_BUTTON 5
#define RIGHT_BUTTON_PORT PORTC #define RIGHT_BUTTON_PORT PORTC
#define RIGHT_BUTTON_PORTIN PINC
#define RIGHT_BUTTON_DDR DDRC
#define RIGHT_BUTTON_BIT PORTC6 #define RIGHT_BUTTON_BIT PORTC6
#define PIN_UP_BUTTON 8 #define PIN_UP_BUTTON 8
#define UP_BUTTON_PORT PORTB #define UP_BUTTON_PORT PORTB
#define UP_BUTTON_PORTIN PINB
#define UP_BUTTON_DDR DDRB
#define UP_BUTTON_BIT PORTB4 #define UP_BUTTON_BIT PORTB4
#define PIN_DOWN_BUTTON 10 #define PIN_DOWN_BUTTON 10
#define DOWN_BUTTON_PORT PORTB #define DOWN_BUTTON_PORT PORTB
#define DOWN_BUTTON_PORTIN PINB
#define DOWN_BUTTON_DDR DDRB
#define DOWN_BUTTON_BIT PORTB6 #define DOWN_BUTTON_BIT PORTB6
#define PIN_A_BUTTON A0 #define PIN_A_BUTTON A0
#define A_BUTTON_PORT PORTF #define A_BUTTON_PORT PORTF
#define A_BUTTON_PORTIN PINF
#define A_BUTTON_DDR DDRF
#define A_BUTTON_BIT PORTF7 #define A_BUTTON_BIT PORTF7
#define PIN_B_BUTTON A1 #define PIN_B_BUTTON A1
#define B_BUTTON_PORT PORTF #define B_BUTTON_PORT PORTF
#define B_BUTTON_PORTIN PINF
#define B_BUTTON_DDR DDRF
#define B_BUTTON_BIT PORTF6 #define B_BUTTON_BIT PORTF6
#define PIN_SPEAKER_1 A2 #define PIN_SPEAKER_1 A2
@ -192,13 +197,30 @@
// //
// Reference: https://github.com/Arduboy/Arduboy/issues/108 // Reference: https://github.com/Arduboy/Arduboy/issues/108
#define RAND_SEED_IN A4 // Open analog input used for noise by initRandomSeed() #endif
#define RAND_SEED_IN_PORTF // --------------------
// ----- Pins common on Arduboy and DevKit -----
// Unconnected analog input used for noise by generateRandomSeed()
#define RAND_SEED_IN A4
#define RAND_SEED_IN_PORT PORTF
#define RAND_SEED_IN_BIT PORTF1 #define RAND_SEED_IN_BIT PORTF1
// Value for ADMUX to read the random seed pin: 2.56V reference, ADC1 // Value for ADMUX to read the random seed pin: 2.56V reference, ADC1
#define RAND_SEED_IN_ADMUX (_BV(REFS0) | _BV(REFS1) | _BV(MUX0)) #define RAND_SEED_IN_ADMUX (_BV(REFS0) | _BV(REFS1) | _BV(MUX0))
#endif // SPI interface
#define SPI_MISO_PORT PORTB
#define SPI_MISO_BIT PORTB3
#define SPI_MOSI_PORT PORTB
#define SPI_MOSI_BIT PORTB2
#define SPI_SCK_PORT PORTB
#define SPI_SCK_BIT PORTB1
#define SPI_SS_PORT PORTB
#define SPI_SS_BIT PORTB0
// -------------------- // --------------------
// OLED hardware (SSD1306) // OLED hardware (SSD1306)
@ -223,6 +245,79 @@
#define COLUMN_ADDRESS_END (WIDTH - 1) & 127 // 128 pixels wide #define COLUMN_ADDRESS_END (WIDTH - 1) & 127 // 128 pixels wide
#define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 7 // 8 pages high #define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 7 // 8 pages high
/** \brief
* Eliminate the USB stack to free up code space.
*
* \warning
* Removing the USB code will make it impossible for sketch uploader
* programs to automatically force a reset into the bootloader!
* This means that a user will manually have to invoke a reset in order to
* upload a new sketch, after one without USB has be been installed.
* Be aware that the timing for the point that a reset must be initiated can
* be tricky, which could lead to some frustration on the user's part.
*
* \details
* \parblock
* This macro will cause the USB code, normally included in the sketch as part
* of the standard Arduino environment, to be eliminated. This will free up a
* fair amount of program space, and some RAM space as well, at the expense of
* disabling all USB functionality within the sketch (except as power input).
*
* The macro should be placed before the `setup()` function definition:
*
* \code{.cpp}
* #include <Arduboy2.h>
*
* Arduboy2 arduboy;
*
* // (Other variable declarations, etc.)
*
* // Eliminate the USB stack
* ARDUBOY_NO_USB
*
* void setup() {
* arduboy.begin();
* // any additional setup code
* }
* \endcode
*
* As stated in the warning above, without the USB code an uploader program
* will be unable to automatically force a reset into the bootloader to upload
* a new sketch. The user will have to manually invoke a reset. In addition to
* eliminating the USB code, this macro will check if the DOWN button is held
* when the sketch first starts and, if so, will call `exitToBootloader()` to
* start the bootloader for uploading. This makes it easier for the user than
* having to press the reset button.
*
* However, to make it even more convenient for a user to invoke the bootloader
* it is highly recommended that a sketch using this macro include a menu or
* prompt that allows the user to press the DOWN button within the sketch,
* which should cause `exitToBootloader()` to be called.
*
* At a minimum, the documentation for the sketch should clearly state that a
* manual reset will be required, and give detailed instructions on what the
* user must do to upload a new sketch.
* \endparblock
*
* \see Arduboy2Core::exitToBootloader()
*/
#define ARDUBOY_NO_USB int main() __attribute__ ((OS_main)); \
int main() { \
Arduboy2NoUSB::mainNoUSB(); \
return 0; \
}
// A replacement for the Arduino main() function that eliminates the USB code.
// Used by the ARDUBOY_NO_USB macro.
class Arduboy2NoUSB
{
friend int main();
private:
static void mainNoUSB();
};
/** \brief /** \brief
* Lower level functions generally dealing directly with the hardware. * Lower level functions generally dealing directly with the hardware.
* *
@ -237,12 +332,11 @@
* that this may eliminate the need to create an entire local copy of the * that this may eliminate the need to create an entire local copy of the
* library, in order to extend the functionality, in most circumstances. * library, in order to extend the functionality, in most circumstances.
*/ */
class Arduboy2Core class Arduboy2Core : public Arduboy2NoUSB
{ {
friend class Arduboy2Ex; friend class Arduboy2Ex;
public: public:
Arduboy2Core();
/** \brief /** \brief
* Idle the CPU to save power. * Idle the CPU to save power.
@ -254,7 +348,7 @@ class Arduboy2Core
* app should be able to sleep maybe half the time in between rendering * app should be able to sleep maybe half the time in between rendering
* it's own frames. * it's own frames.
*/ */
void static idle(); static void idle();
/** \brief /** \brief
* Put the display into data mode. * Put the display into data mode.
@ -270,7 +364,7 @@ class Arduboy2Core
* *
* \see LCDCommandMode() SPItransfer() * \see LCDCommandMode() SPItransfer()
*/ */
void static LCDDataMode(); static void LCDDataMode();
/** \brief /** \brief
* Put the display into command mode. * Put the display into command mode.
@ -294,7 +388,7 @@ class Arduboy2Core
* *
* \see LCDDataMode() sendLCDCommand() SPItransfer() * \see LCDDataMode() sendLCDCommand() SPItransfer()
*/ */
void static LCDCommandMode(); static void LCDCommandMode();
/** \brief /** \brief
* Transfer a byte to the display. * Transfer a byte to the display.
@ -307,9 +401,31 @@ class Arduboy2Core
* or as data to be placed on the screen, depending on the command/data * or as data to be placed on the screen, depending on the command/data
* mode. * mode.
* *
* \see LCDDataMode() LCDCommandMode() sendLCDCommand() * \see LCDDataMode() LCDCommandMode() sendLCDCommand() SPItransferAndRead()
*/ */
void static SPItransfer(uint8_t data); static void SPItransfer(uint8_t data);
/** \brief
* Transfer a byte to, and read a byte from, the SPI bus.
*
* \param data The byte to be sent.
*
* \return The byte that was received.
*
* \details
* This function does the same as the `SPItransfer()` function but also
* reads and returns the byte of data that was received during the
* transfer.
*
* This function is of no use for a standard Arduboy, since only the
* display is connected to the SPI bus and data cannot be received from
* the display. It has been provided for use with homemade or expanded
* units that have had additional peripherals added to the SPI bus that
* are capable of sending data.
*
* \see SPItransfer()
*/
static uint8_t SPItransferAndRead(uint8_t data);
/** \brief /** \brief
* Turn the display off. * Turn the display off.
@ -322,7 +438,7 @@ class Arduboy2Core
* *
* \see displayOn() * \see displayOn()
*/ */
void static displayOff(); static void displayOff();
/** \brief /** \brief
* Turn the display on. * Turn the display on.
@ -338,29 +454,21 @@ class Arduboy2Core
* *
* \see displayOff() * \see displayOff()
*/ */
void static displayOn(); static void displayOn();
/** \brief /** \brief
* Get the width of the display in pixels. * Get the width of the display in pixels.
* *
* \return The width of the display in pixels. * \return The width of the display in pixels.
*
* \note
* In most cases, the defined value `WIDTH` would be better to use instead
* of this function.
*/ */
uint8_t static width(); static constexpr uint8_t width() { return WIDTH; }
/** \brief /** \brief
* Get the height of the display in pixels. * Get the height of the display in pixels.
* *
* \return The height of the display in pixels. * \return The height of the display in pixels.
*
* \note
* In most cases, the defined value `HEIGHT` would be better to use instead
* of this function.
*/ */
uint8_t static height(); static constexpr uint8_t height() { return HEIGHT; }
/** \brief /** \brief
* Get the current state of all buttons as a bitmask. * Get the current state of all buttons as a bitmask.
@ -375,7 +483,7 @@ class Arduboy2Core
* *
* LEFT_BUTTON, RIGHT_BUTTON, UP_BUTTON, DOWN_BUTTON, A_BUTTON, B_BUTTON * LEFT_BUTTON, RIGHT_BUTTON, UP_BUTTON, DOWN_BUTTON, A_BUTTON, B_BUTTON
*/ */
uint8_t static buttonsState(); static uint8_t buttonsState();
/** \brief /** \brief
* Paint 8 pixels vertically to the display. * Paint 8 pixels vertically to the display.
@ -407,7 +515,7 @@ class Arduboy2Core
* . . . . . . . . (end of page 1) X . X . . . . . (end of page 1) * . . . . . . . . (end of page 1) X . X . . . . . (end of page 1)
* . . . . . . . . (page 2) . . . . . . . . (page 2) * . . . . . . . . (page 2) . . . . . . . . (page 2)
*/ */
void static paint8Pixels(uint8_t pixels); static void paint8Pixels(uint8_t pixels);
/** \brief /** \brief
* Paints an entire image directly to the display from program memory. * Paints an entire image directly to the display from program memory.
@ -425,7 +533,7 @@ class Arduboy2Core
* *
* \see paint8Pixels() * \see paint8Pixels()
*/ */
void static paintScreen(const uint8_t *image); static void paintScreen(const uint8_t *image);
/** \brief /** \brief
* Paints an entire image directly to the display from an array in RAM. * Paints an entire image directly to the display from an array in RAM.
@ -449,7 +557,7 @@ class Arduboy2Core
* *
* \see paint8Pixels() * \see paint8Pixels()
*/ */
void static paintScreen(uint8_t image[], bool clear = false); static void paintScreen(uint8_t image[], bool clear = false);
/** \brief /** \brief
* Blank the display screen by setting all pixels off. * Blank the display screen by setting all pixels off.
@ -458,13 +566,13 @@ class Arduboy2Core
* All pixels on the screen will be written with a value of 0 to turn * All pixels on the screen will be written with a value of 0 to turn
* them off. * them off.
*/ */
void static blank(); static void blank();
/** \brief /** \brief
* Invert the entire display or set it back to normal. * Invert the entire display or set it back to normal.
* *
* \param inverse `true` will invert the display. `false` will set the * \param inverse `true` will invert the display. `false` will set the
* display to no-inverted. * display to non-inverted.
* *
* \details * \details
* Calling this function with a value of `true` will set the display to * Calling this function with a value of `true` will set the display to
@ -475,7 +583,7 @@ class Arduboy2Core
* until it is set back to non-inverted mode by calling this function with * until it is set back to non-inverted mode by calling this function with
* `false`. * `false`.
*/ */
void static invert(bool inverse); static void invert(bool inverse);
/** \brief /** \brief
* Turn all display pixels on or display the buffer contents. * Turn all display pixels on or display the buffer contents.
@ -496,7 +604,7 @@ class Arduboy2Core
* *
* \see invert() * \see invert()
*/ */
void static allPixelsOn(bool on); static void allPixelsOn(bool on);
/** \brief /** \brief
* Flip the display vertically or set it back to normal. * Flip the display vertically or set it back to normal.
@ -514,7 +622,7 @@ class Arduboy2Core
* *
* \see flipHorizontal() * \see flipHorizontal()
*/ */
void static flipVertical(bool flipped); static void flipVertical(bool flipped);
/** \brief /** \brief
* Flip the display horizontally or set it back to normal. * Flip the display horizontally or set it back to normal.
@ -532,7 +640,7 @@ class Arduboy2Core
* *
* \see flipVertical() * \see flipVertical()
*/ */
void static flipHorizontal(bool flipped); static void flipHorizontal(bool flipped);
/** \brief /** \brief
* Send a single command byte to the display. * Send a single command byte to the display.
@ -548,7 +656,7 @@ class Arduboy2Core
* Sending improper commands to the display can place it into invalid or * Sending improper commands to the display can place it into invalid or
* unexpected states, possibly even causing physical damage. * unexpected states, possibly even causing physical damage.
*/ */
void static sendLCDCommand(uint8_t command); static void sendLCDCommand(uint8_t command);
/** \brief /** \brief
* Set the light output of the RGB LED. * Set the light output of the RGB LED.
@ -580,9 +688,44 @@ class Arduboy2Core
* LEDs will light. * LEDs will light.
* \endparblock * \endparblock
* *
* \see digitalWriteRGB() * \see setRGBled(uint8_t, uint8_t) digitalWriteRGB() freeRGBled()
*/ */
void static setRGBled(uint8_t red, uint8_t green, uint8_t blue); static void setRGBled(uint8_t red, uint8_t green, uint8_t blue);
/** \brief
* Set the brightness of one of the RGB LEDs without affecting the others.
*
* \param color The name of the LED to set. The value given should be one
* of RED_LED, GREEN_LED or BLUE_LED.
*
* \param val The brightness value for the LED, from 0 to 255.
*
* \note
* In order to use this function, the 3 parameter version must first be
* called at least once, in order to initialize the hardware.
*
* \details
* This 2 parameter version of the function will set the brightness of a
* single LED within the RGB LED without affecting the current brightness
* of the other two. See the description of the 3 parameter version of this
* function for more details on the RGB LED.
*
* \see setRGBled(uint8_t, uint8_t, uint8_t) digitalWriteRGB() freeRGBled()
*/
static void setRGBled(uint8_t color, uint8_t val);
/** \brief
* Relinquish analog control of the RGB LED.
*
* \details
* Using the RGB LED in analog mode prevents further use of the LED in
* digital mode. This function will restore the pins used for the LED, so
* it can be used in digital mode.
*
* \see digitalWriteRGB() setRGBled()
*/
static void freeRGBled();
/** \brief /** \brief
* Set the RGB LEDs digitally, to either fully on or fully off. * Set the RGB LEDs digitally, to either fully on or fully off.
@ -597,28 +740,37 @@ class Arduboy2Core
* *
* The colors are as follows: * The colors are as follows:
* *
* RED LED GREEN_LED BLUE_LED COLOR * | RED LED | GREEN LED | BLUE LED | COLOR |
* ------- --------- -------- ----- * | ------- | --------- | -------- | :-----: |
* RGB_OFF RGB_OFF RGB_OFF OFF * | RGB_OFF | RGB_OFF | RGB_OFF | OFF |
* RGB_OFF RGB_OFF RGB_ON Blue * | RGB_OFF | RGB_OFF | RGB_ON | Blue |
* RGB_OFF RGB_ON RGB_OFF Green * | RGB_OFF | RGB_ON | RGB_OFF | Green |
* RGB_OFF RGB_ON RGB_ON Cyan * | RGB_OFF | RGB_ON | RGB_ON | Cyan |
* RGB_ON RGB_OFF RGB_OFF Red * | RGB_ON | RGB_OFF | RGB_OFF | Red |
* RGB_ON RGB_OFF RGB_ON Magenta * | RGB_ON | RGB_OFF | RGB_ON | Magenta |
* RGB_ON RGB_ON RGB_OFF Yellow * | RGB_ON | RGB_ON | RGB_OFF | Yellow |
* RGB_ON RGB_ON RGB_ON White * | RGB_ON | RGB_ON | RGB_ON | White |
* *
* \note * \note
* \parblock
* Using the RGB LED in analog mode will prevent digital control of the
* LED. To restore the ability to control the LED digitally, use the
* `freeRGBled()` function.
* \endparblock
*
* \note
* \parblock
* Many of the Kickstarter Arduboys were accidentally shipped with the * Many of the Kickstarter Arduboys were accidentally shipped with the
* RGB LED installed incorrectly. For these units, the green LED cannot be * RGB LED installed incorrectly. For these units, the green LED cannot be
* lit. As long as the green led is set to off, turning on the red LED will * lit. As long as the green led is set to off, turning on the red LED will
* actually light the blue LED and turning on the blue LED will actually * actually light the blue LED and turning on the blue LED will actually
* light the red LED. If the green LED is turned on, none of the LEDs * light the red LED. If the green LED is turned on, none of the LEDs
* will light. * will light.
* \endparblock
* *
* \see digitalWriteRGB(uint8_t, uint8_t) setRGBled() * \see digitalWriteRGB(uint8_t, uint8_t) setRGBled() freeRGBled()
*/ */
void static digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue); static void digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue);
/** \brief /** \brief
* Set one of the RGB LEDs digitally, to either fully on or fully off. * Set one of the RGB LEDs digitally, to either fully on or fully off.
@ -634,9 +786,9 @@ class Arduboy2Core
* the RGB LED either fully on or fully off. See the description of the * the RGB LED either fully on or fully off. See the description of the
* 3 parameter version of this function for more details on the RGB LED. * 3 parameter version of this function for more details on the RGB LED.
* *
* \see digitalWriteRGB(uint8_t, uint8_t, uint8_t) setRGBled() * \see digitalWriteRGB(uint8_t, uint8_t, uint8_t) setRGBled() freeRGBled()
*/ */
void static digitalWriteRGB(uint8_t color, uint8_t val); static void digitalWriteRGB(uint8_t color, uint8_t val);
/** \brief /** \brief
* Initialize the Arduboy's hardware. * Initialize the Arduboy's hardware.
@ -644,16 +796,23 @@ class Arduboy2Core
* \details * \details
* This function initializes the display, buttons, etc. * This function initializes the display, buttons, etc.
* *
* This function is called by begin() so isn't normally called within a * This function is called by `begin()` so isn't normally called within a
* sketch. However, in order to free up some code space, by eliminating * sketch. However, in order to free up some code space, by eliminating
* some of the start up features, it can be called in place of begin(). * some of the start up features, it can be called in place of begin().
* The functions that begin() would call after boot() can then be called * The functions that `begin()` would call after `boot()` can then be
* to add back in some of the start up features, if desired. * called to add back in some of the start up features as space permits.
* See the README file or documentation on the main page for more details.
* *
* \see Arduboy2Base::begin() * See the README file or main page, in section
* _Substitute or remove boot up features_, for more details.
*
* \warning
* If this function is used, it is recommended that at least `flashlight()`
* or `safeMode()` be called after it to provide a means to upload a new
* sketch if the bootloader "magic number" problem is encountered.
*
* \see Arduboy2::begin() Arduboy2Base::flashlight() safeMode()
*/ */
void static boot(); static void boot();
/** \brief /** \brief
* Allow upload when the bootloader "magic number" could be corrupted. * Allow upload when the bootloader "magic number" could be corrupted.
@ -665,8 +824,8 @@ class Arduboy2Core
* sketch, for sketches that interfere with the bootloader "magic number". * sketch, for sketches that interfere with the bootloader "magic number".
* The problem occurs with certain sketches that use large amounts of RAM. * The problem occurs with certain sketches that use large amounts of RAM.
* *
* This function should be called after `boot()` in sketches that * This function should be called after `boot()` in sketches that don't
* potentially could cause the problem. * call `flashlight()`.
* *
* It is intended to replace the `flashlight()` function when more * It is intended to replace the `flashlight()` function when more
* program space is required. If possible, it is more desirable to use * program space is required. If possible, it is more desirable to use
@ -674,7 +833,26 @@ class Arduboy2Core
* *
* \see Arduboy2Base::flashlight() boot() * \see Arduboy2Base::flashlight() boot()
*/ */
void static safeMode(); static void safeMode();
/** \brief
* Create a seed suitable for use with a pseudorandom number generator.
*
* \return A random value that can be used to seed a
* pseudorandom number generator.
*
* \details
* The returned value will be a random value derived from entropy from an
* ADC reading of a floating pin combined with the microseconds since boot.
*
* \note
* This function will be more effective if called after a semi-random time,
* such as after waiting for the user to press a button to start a game, or
* another event that takes a variable amount of time after boot.
*
* \see Arduboy2Base::initRandomSeed()
*/
static unsigned long generateRandomSeed();
/** \brief /** \brief
* Delay for the number of milliseconds, specified as a 16 bit value. * Delay for the number of milliseconds, specified as a 16 bit value.
@ -687,15 +865,35 @@ class Arduboy2Core
* 65535 milliseconds (about 65.5 seconds). Using this function instead * 65535 milliseconds (about 65.5 seconds). Using this function instead
* of Arduino `delay()` will save a few bytes of code. * of Arduino `delay()` will save a few bytes of code.
*/ */
void static delayShort(uint16_t ms) __attribute__ ((noinline)); static void delayShort(uint16_t ms) __attribute__ ((noinline));
/** \brief
* Exit the sketch and start the bootloader
*
* \details
* The sketch will exit and the bootloader will be started in command mode.
* The effect will be similar to pressing the reset button.
*
* This function is intended to be used to allow uploading a new sketch,
* when the USB code has been removed to gain more code space.
* Ideally, the sketch would present a "New Sketch Upload" menu or prompt
* telling the user to "Press and hold the DOWN button when the procedure
* to upload a new sketch has been initiated". The sketch would then wait
* for the DOWN button to be pressed and then call this function.
*
* \see ARDUBOY_NO_USB
*/
static void exitToBootloader();
protected: protected:
// internals // internals
void static setCPUSpeed8MHz(); static void setCPUSpeed8MHz();
void static bootSPI(); static void bootSPI();
void static bootOLED(); static void bootOLED();
void static bootPins(); static void bootPins();
void static bootPowerSaving(); static void bootPowerSaving();
static const PROGMEM uint8_t lcdBootProgram[];
}; };
#endif #endif

341
src/Arduboy2Data.cpp Normal file
View File

@ -0,0 +1,341 @@
/**
* @file Arduboy2Data.cpp
* \brief
* Constant data definintions for the Arduboy2 and Arduboy2Base classes.
*/
#include "Arduboy2.h"
#include <avr/pgmspace.h>
// arduboy_logo.png
// drawBitmap() format
// 88x16 px (176 bytes)
const PROGMEM uint8_t Arduboy2Base::arduboy_logo[] = {
0xF0, 0xF8, 0x9C, 0x8E, 0x87, 0x83, 0x87, 0x8E, 0x9C, 0xF8,
0xF0, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03,
0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03,
0x03, 0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0xFE, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0xC7,
0xEE, 0x7C, 0x38, 0x00, 0x00, 0xF8, 0xFC, 0x0E, 0x07, 0x03,
0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0x3F, 0x7F,
0xE0, 0xC0, 0x80, 0x80, 0xC0, 0xE0, 0x7F, 0x3F, 0xFF, 0xFF,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0x00,
0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0x0C, 0x0C, 0x1C, 0x3E, 0x77,
0xE3, 0xC1, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x1F, 0x3F, 0x70,
0xE0, 0xC0, 0xC0, 0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00,
0x7F, 0xFF, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xE3, 0x77, 0x3E,
0x1C, 0x00, 0x00, 0x1F, 0x3F, 0x70, 0xE0, 0xC0, 0xC0, 0xC0,
0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00
};
// arduboy_logo.png
// drawCompressed() format
// 88x16 px (151 bytes)
const PROGMEM uint8_t Arduboy2Base::arduboy_logo_compressed[] = {
0x57, 0x0F, 0x9C, 0x53, 0x72, 0x75, 0x29, 0xE5, 0x9C, 0x92,
0xCE, 0x95, 0x52, 0xAD, 0x4E, 0x49, 0xE7, 0x08, 0x09, 0xED,
0x76, 0xBB, 0xDD, 0x2A, 0xAB, 0xAC, 0x55, 0x92, 0x90, 0xD0,
0x6E, 0xB7, 0xDB, 0xAD, 0xB2, 0xCA, 0x5A, 0x25, 0xF9, 0xF8,
0xF0, 0xC6, 0x47, 0x48, 0x28, 0x95, 0x54, 0x52, 0x49, 0x25,
0x9D, 0x3A, 0x95, 0x5A, 0x3A, 0x45, 0x2A, 0xB7, 0x29, 0xA7,
0xE4, 0x76, 0xBB, 0x55, 0x56, 0x59, 0xAB, 0x24, 0x9F, 0x5D,
0x5B, 0x65, 0xD7, 0xE9, 0xEC, 0x92, 0x29, 0x3B, 0xA1, 0x4E,
0xA7, 0xD3, 0xE9, 0x74, 0x9A, 0x8F, 0x8F, 0xEF, 0xED, 0x76,
0xBB, 0x55, 0x4E, 0xAE, 0x52, 0xAD, 0x9C, 0x9C, 0x4F, 0xE7,
0xED, 0x76, 0xBB, 0xDD, 0x2E, 0x95, 0x53, 0xD9, 0x25, 0xA5,
0x54, 0xD6, 0x2A, 0xAB, 0xEC, 0x76, 0xBB, 0x54, 0x4E, 0x65,
0x97, 0x94, 0x3A, 0x22, 0xA9, 0xA4, 0x92, 0x4A, 0x2A, 0xE9,
0x94, 0x4D, 0x2D, 0x9D, 0xA2, 0x94, 0xCA, 0x5A, 0x65, 0x95,
0xDD, 0x6E, 0x97, 0xCA, 0xA9, 0xEC, 0x12, 0x55, 0x69, 0x42,
0x7A
};
// arduboy_logo.png
// Sprites::drawSelfMasked() format
// 88x16 px (178 bytes)
const PROGMEM uint8_t Arduboy2Base::arduboy_logo_sprite[] = {
88, 16,
0xF0, 0xF8, 0x9C, 0x8E, 0x87, 0x83, 0x87, 0x8E, 0x9C, 0xF8,
0xF0, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03,
0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03,
0x03, 0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0xFE, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0xC7,
0xEE, 0x7C, 0x38, 0x00, 0x00, 0xF8, 0xFC, 0x0E, 0x07, 0x03,
0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0x3F, 0x7F,
0xE0, 0xC0, 0x80, 0x80, 0xC0, 0xE0, 0x7F, 0x3F, 0xFF, 0xFF,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0x00,
0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0x0C, 0x0C, 0x1C, 0x3E, 0x77,
0xE3, 0xC1, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x1F, 0x3F, 0x70,
0xE0, 0xC0, 0xC0, 0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00,
0x7F, 0xFF, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xE3, 0x77, 0x3E,
0x1C, 0x00, 0x00, 0x1F, 0x3F, 0x70, 0xE0, 0xC0, 0xC0, 0xC0,
0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00
};
// ASCII based 5x7 font
// IBM PC code page 437
const PROGMEM uint8_t Arduboy2::font5x7[] = {
0x00, 0x00, 0x00, 0x00, 0x00,
0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
0x18, 0x3C, 0x7E, 0x3C, 0x18,
0x1C, 0x57, 0x7D, 0x57, 0x1C,
0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
0x00, 0x18, 0x3C, 0x18, 0x00,
0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
0x00, 0x18, 0x24, 0x18, 0x00,
0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
0x30, 0x48, 0x3A, 0x06, 0x0E,
0x26, 0x29, 0x79, 0x29, 0x26,
0x40, 0x7F, 0x05, 0x05, 0x07,
0x40, 0x7F, 0x05, 0x25, 0x3F,
0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
0x7F, 0x3E, 0x1C, 0x1C, 0x08,
0x08, 0x1C, 0x1C, 0x3E, 0x7F,
0x14, 0x22, 0x7F, 0x22, 0x14,
0x5F, 0x5F, 0x00, 0x5F, 0x5F,
0x06, 0x09, 0x7F, 0x01, 0x7F,
0x00, 0x66, 0x89, 0x95, 0x6A,
0x60, 0x60, 0x60, 0x60, 0x60,
0x94, 0xA2, 0xFF, 0xA2, 0x94,
0x08, 0x04, 0x7E, 0x04, 0x08,
0x10, 0x20, 0x7E, 0x20, 0x10,
0x08, 0x08, 0x2A, 0x1C, 0x08,
0x08, 0x1C, 0x2A, 0x08, 0x08,
0x1E, 0x10, 0x10, 0x10, 0x10,
0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
0x30, 0x38, 0x3E, 0x38, 0x30,
0x06, 0x0E, 0x3E, 0x0E, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5F, 0x00, 0x00,
0x00, 0x07, 0x00, 0x07, 0x00,
0x14, 0x7F, 0x14, 0x7F, 0x14,
0x24, 0x2A, 0x7F, 0x2A, 0x12,
0x23, 0x13, 0x08, 0x64, 0x62,
0x36, 0x49, 0x56, 0x20, 0x50,
0x00, 0x08, 0x07, 0x03, 0x00,
0x00, 0x1C, 0x22, 0x41, 0x00,
0x00, 0x41, 0x22, 0x1C, 0x00,
0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
0x08, 0x08, 0x3E, 0x08, 0x08,
0x00, 0x80, 0x70, 0x30, 0x00,
0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x60, 0x60, 0x00,
0x20, 0x10, 0x08, 0x04, 0x02,
0x3E, 0x51, 0x49, 0x45, 0x3E,
0x00, 0x42, 0x7F, 0x40, 0x00,
0x72, 0x49, 0x49, 0x49, 0x46,
0x21, 0x41, 0x49, 0x4D, 0x33,
0x18, 0x14, 0x12, 0x7F, 0x10,
0x27, 0x45, 0x45, 0x45, 0x39,
0x3C, 0x4A, 0x49, 0x49, 0x31,
0x41, 0x21, 0x11, 0x09, 0x07,
0x36, 0x49, 0x49, 0x49, 0x36,
0x46, 0x49, 0x49, 0x29, 0x1E,
0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x40, 0x34, 0x00, 0x00,
0x00, 0x08, 0x14, 0x22, 0x41,
0x14, 0x14, 0x14, 0x14, 0x14,
0x00, 0x41, 0x22, 0x14, 0x08,
0x02, 0x01, 0x59, 0x09, 0x06,
0x3E, 0x41, 0x5D, 0x59, 0x4E,
0x7C, 0x12, 0x11, 0x12, 0x7C,
0x7F, 0x49, 0x49, 0x49, 0x36,
0x3E, 0x41, 0x41, 0x41, 0x22,
0x7F, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x49, 0x49, 0x49, 0x41,
0x7F, 0x09, 0x09, 0x09, 0x01,
0x3E, 0x41, 0x41, 0x51, 0x73,
0x7F, 0x08, 0x08, 0x08, 0x7F,
0x00, 0x41, 0x7F, 0x41, 0x00,
0x20, 0x40, 0x41, 0x3F, 0x01,
0x7F, 0x08, 0x14, 0x22, 0x41,
0x7F, 0x40, 0x40, 0x40, 0x40,
0x7F, 0x02, 0x1C, 0x02, 0x7F,
0x7F, 0x04, 0x08, 0x10, 0x7F,
0x3E, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x09, 0x09, 0x09, 0x06,
0x3E, 0x41, 0x51, 0x21, 0x5E,
0x7F, 0x09, 0x19, 0x29, 0x46,
0x26, 0x49, 0x49, 0x49, 0x32,
0x03, 0x01, 0x7F, 0x01, 0x03,
0x3F, 0x40, 0x40, 0x40, 0x3F,
0x1F, 0x20, 0x40, 0x20, 0x1F,
0x3F, 0x40, 0x38, 0x40, 0x3F,
0x63, 0x14, 0x08, 0x14, 0x63,
0x03, 0x04, 0x78, 0x04, 0x03,
0x61, 0x59, 0x49, 0x4D, 0x43,
0x00, 0x7F, 0x41, 0x41, 0x41,
0x02, 0x04, 0x08, 0x10, 0x20,
0x00, 0x41, 0x41, 0x41, 0x7F,
0x04, 0x02, 0x01, 0x02, 0x04,
0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x03, 0x07, 0x08, 0x00,
0x20, 0x54, 0x54, 0x78, 0x40,
0x7F, 0x28, 0x44, 0x44, 0x38,
0x38, 0x44, 0x44, 0x44, 0x28,
0x38, 0x44, 0x44, 0x28, 0x7F,
0x38, 0x54, 0x54, 0x54, 0x18,
0x00, 0x08, 0x7E, 0x09, 0x02,
0x18, 0xA4, 0xA4, 0x9C, 0x78,
0x7F, 0x08, 0x04, 0x04, 0x78,
0x00, 0x44, 0x7D, 0x40, 0x00,
0x20, 0x40, 0x40, 0x3D, 0x00,
0x7F, 0x10, 0x28, 0x44, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00,
0x7C, 0x04, 0x78, 0x04, 0x78,
0x7C, 0x08, 0x04, 0x04, 0x78,
0x38, 0x44, 0x44, 0x44, 0x38,
0xFC, 0x18, 0x24, 0x24, 0x18,
0x18, 0x24, 0x24, 0x18, 0xFC,
0x7C, 0x08, 0x04, 0x04, 0x08,
0x48, 0x54, 0x54, 0x54, 0x24,
0x04, 0x04, 0x3F, 0x44, 0x24,
0x3C, 0x40, 0x40, 0x20, 0x7C,
0x1C, 0x20, 0x40, 0x20, 0x1C,
0x3C, 0x40, 0x30, 0x40, 0x3C,
0x44, 0x28, 0x10, 0x28, 0x44,
0x4C, 0x90, 0x90, 0x90, 0x7C,
0x44, 0x64, 0x54, 0x4C, 0x44,
0x00, 0x08, 0x36, 0x41, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00,
0x02, 0x01, 0x02, 0x04, 0x02,
0x3C, 0x26, 0x23, 0x26, 0x3C,
0x1E, 0xA1, 0xA1, 0x61, 0x12,
0x3A, 0x40, 0x40, 0x20, 0x7A,
0x38, 0x54, 0x54, 0x55, 0x59,
0x21, 0x55, 0x55, 0x79, 0x41,
0x21, 0x54, 0x54, 0x78, 0x41,
0x21, 0x55, 0x54, 0x78, 0x40,
0x20, 0x54, 0x55, 0x79, 0x40,
0x0C, 0x1E, 0x52, 0x72, 0x12,
0x39, 0x55, 0x55, 0x55, 0x59,
0x39, 0x54, 0x54, 0x54, 0x59,
0x39, 0x55, 0x54, 0x54, 0x58,
0x00, 0x00, 0x45, 0x7C, 0x41,
0x00, 0x02, 0x45, 0x7D, 0x42,
0x00, 0x01, 0x45, 0x7C, 0x40,
0xF0, 0x29, 0x24, 0x29, 0xF0,
0xF0, 0x28, 0x25, 0x28, 0xF0,
0x7C, 0x54, 0x55, 0x45, 0x00,
0x20, 0x54, 0x54, 0x7C, 0x54,
0x7C, 0x0A, 0x09, 0x7F, 0x49,
0x32, 0x49, 0x49, 0x49, 0x32,
0x32, 0x48, 0x48, 0x48, 0x32,
0x32, 0x4A, 0x48, 0x48, 0x30,
0x3A, 0x41, 0x41, 0x21, 0x7A,
0x3A, 0x42, 0x40, 0x20, 0x78,
0x00, 0x9D, 0xA0, 0xA0, 0x7D,
0x39, 0x44, 0x44, 0x44, 0x39,
0x3D, 0x40, 0x40, 0x40, 0x3D,
0x3C, 0x24, 0xFF, 0x24, 0x24,
0x48, 0x7E, 0x49, 0x43, 0x66,
0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
0xFF, 0x09, 0x29, 0xF6, 0x20,
0xC0, 0x88, 0x7E, 0x09, 0x03,
0x20, 0x54, 0x54, 0x79, 0x41,
0x00, 0x00, 0x44, 0x7D, 0x41,
0x30, 0x48, 0x48, 0x4A, 0x32,
0x38, 0x40, 0x40, 0x22, 0x7A,
0x00, 0x7A, 0x0A, 0x0A, 0x72,
0x7D, 0x0D, 0x19, 0x31, 0x7D,
0x26, 0x29, 0x29, 0x2F, 0x28,
0x26, 0x29, 0x29, 0x29, 0x26,
0x30, 0x48, 0x4D, 0x40, 0x20,
0x38, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x38,
0x2F, 0x10, 0xC8, 0xAC, 0xBA,
0x2F, 0x10, 0x28, 0x34, 0xFA,
0x00, 0x00, 0x7B, 0x00, 0x00,
0x08, 0x14, 0x2A, 0x14, 0x22,
0x22, 0x14, 0x2A, 0x14, 0x08,
0x95, 0x00, 0x22, 0x00, 0x95,
0xAA, 0x00, 0x55, 0x00, 0xAA,
0xAA, 0x55, 0xAA, 0x55, 0xAA,
0x00, 0x00, 0x00, 0xFF, 0x00,
0x10, 0x10, 0x10, 0xFF, 0x00,
0x14, 0x14, 0x14, 0xFF, 0x00,
0x10, 0x10, 0xFF, 0x00, 0xFF,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x14, 0x14, 0x14, 0xFC, 0x00,
0x14, 0x14, 0xF7, 0x00, 0xFF,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x14, 0x14, 0xF4, 0x04, 0xFC,
0x14, 0x14, 0x17, 0x10, 0x1F,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0x1F, 0x00,
0x10, 0x10, 0x10, 0xF0, 0x00,
0x00, 0x00, 0x00, 0x1F, 0x10,
0x10, 0x10, 0x10, 0x1F, 0x10,
0x10, 0x10, 0x10, 0xF0, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0xFF, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x14,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x1F, 0x10, 0x17,
0x00, 0x00, 0xFC, 0x04, 0xF4,
0x14, 0x14, 0x17, 0x10, 0x17,
0x14, 0x14, 0xF4, 0x04, 0xF4,
0x00, 0x00, 0xFF, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x14, 0x14,
0x14, 0x14, 0xF7, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x17, 0x14,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0xF4, 0x14,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x00, 0x00, 0x1F, 0x10, 0x1F,
0x00, 0x00, 0x00, 0x1F, 0x14,
0x00, 0x00, 0x00, 0xFC, 0x14,
0x00, 0x00, 0xF0, 0x10, 0xF0,
0x10, 0x10, 0xFF, 0x10, 0xFF,
0x14, 0x14, 0x14, 0xFF, 0x14,
0x10, 0x10, 0x10, 0x1F, 0x00,
0x00, 0x00, 0x00, 0xF0, 0x10,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x38, 0x44, 0x44, 0x38, 0x44,
0x7C, 0x2A, 0x2A, 0x3E, 0x14,
0x7E, 0x02, 0x02, 0x06, 0x06,
0x02, 0x7E, 0x02, 0x7E, 0x02,
0x63, 0x55, 0x49, 0x41, 0x63,
0x38, 0x44, 0x44, 0x3C, 0x04,
0x40, 0x7E, 0x20, 0x1E, 0x20,
0x06, 0x02, 0x7E, 0x02, 0x02,
0x99, 0xA5, 0xE7, 0xA5, 0x99,
0x1C, 0x2A, 0x49, 0x2A, 0x1C,
0x4C, 0x72, 0x01, 0x72, 0x4C,
0x30, 0x4A, 0x4D, 0x4D, 0x30,
0x30, 0x48, 0x78, 0x48, 0x30,
0xBC, 0x62, 0x5A, 0x46, 0x3D,
0x3E, 0x49, 0x49, 0x49, 0x00,
0x7E, 0x01, 0x01, 0x01, 0x7E,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x44, 0x44, 0x5F, 0x44, 0x44,
0x40, 0x51, 0x4A, 0x44, 0x40,
0x40, 0x44, 0x4A, 0x51, 0x40,
0x00, 0x00, 0xFF, 0x01, 0x03,
0xE0, 0x80, 0xFF, 0x00, 0x00,
0x08, 0x08, 0x6B, 0x6B, 0x08,
0x36, 0x12, 0x36, 0x24, 0x36,
0x06, 0x0F, 0x09, 0x0F, 0x06,
0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x00, 0x10, 0x10, 0x00,
0x30, 0x40, 0xFF, 0x01, 0x01,
0x00, 0x1F, 0x01, 0x01, 0x1E,
0x00, 0x19, 0x1D, 0x17, 0x12,
0x00, 0x3C, 0x3C, 0x3C, 0x3C,
0x00, 0x00, 0x00, 0x00, 0x00,
};

View File

@ -81,13 +81,12 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
// xOffset technically doesn't need to be 16 bit but the math operations // xOffset technically doesn't need to be 16 bit but the math operations
// are measurably faster if it is // are measurably faster if it is
uint16_t xOffset, ofs; uint16_t xOffset, ofs;
int8_t yOffset = abs(y) % 8; int8_t yOffset = y & 7;
int8_t sRow = y / 8; int8_t sRow = y / 8;
uint8_t loop_h, start_h, rendered_width; uint8_t loop_h, start_h, rendered_width;
if (y < 0 && yOffset > 0) { if (y < 0 && yOffset > 0) {
sRow--; sRow--;
yOffset = 8 - yOffset;
} }
// if the left side of the render is offscreen skip those loops // if the left side of the render is offscreen skip those loops
@ -149,10 +148,11 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
Arduboy2Base::sBuffer[ofs] = data; Arduboy2Base::sBuffer[ofs] = data;
} }
if (yOffset != 0 && sRow < 7) { if (yOffset != 0 && sRow < 7) {
data = Arduboy2Base::sBuffer[ofs + WIDTH]; const size_t index = static_cast<uint16_t>(ofs + WIDTH);
data &= (*((unsigned char *) (&mask_data) + 1)); data = Arduboy2Base::sBuffer[index];
data |= (*((unsigned char *) (&bitmap_data) + 1)); data &= (uint8_t)(mask_data >> 8);
Arduboy2Base::sBuffer[ofs + WIDTH] = data; data |= (uint8_t)(bitmap_data >> 8);
Arduboy2Base::sBuffer[index] = data;
} }
ofs++; ofs++;
bofs++; bofs++;
@ -171,7 +171,8 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
Arduboy2Base::sBuffer[ofs] |= (uint8_t)(bitmap_data); Arduboy2Base::sBuffer[ofs] |= (uint8_t)(bitmap_data);
} }
if (yOffset != 0 && sRow < 7) { if (yOffset != 0 && sRow < 7) {
Arduboy2Base::sBuffer[ofs + WIDTH] |= (*((unsigned char *) (&bitmap_data) + 1)); const size_t index = static_cast<uint16_t>(ofs + WIDTH);
Arduboy2Base::sBuffer[index] |= (uint8_t)(bitmap_data >> 8);
} }
ofs++; ofs++;
bofs++; bofs++;
@ -190,7 +191,8 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
Arduboy2Base::sBuffer[ofs] &= ~(uint8_t)(bitmap_data); Arduboy2Base::sBuffer[ofs] &= ~(uint8_t)(bitmap_data);
} }
if (yOffset != 0 && sRow < 7) { if (yOffset != 0 && sRow < 7) {
Arduboy2Base::sBuffer[ofs + WIDTH] &= ~(*((unsigned char *) (&bitmap_data) + 1)); const size_t index = static_cast<uint16_t>(ofs + WIDTH);
Arduboy2Base::sBuffer[index] &= ~(uint8_t)(bitmap_data >> 8);
} }
ofs++; ofs++;
bofs++; bofs++;
@ -224,10 +226,11 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
Arduboy2Base::sBuffer[ofs] = data; Arduboy2Base::sBuffer[ofs] = data;
} }
if (yOffset != 0 && sRow < 7) { if (yOffset != 0 && sRow < 7) {
data = Arduboy2Base::sBuffer[ofs + WIDTH]; const size_t index = static_cast<uint16_t>(ofs + WIDTH);
data &= (*((unsigned char *) (&mask_data) + 1)); data = Arduboy2Base::sBuffer[index];
data |= (*((unsigned char *) (&bitmap_data) + 1)); data &= (uint8_t)(mask_data >> 8);
Arduboy2Base::sBuffer[ofs + WIDTH] = data; data |= (uint8_t)(bitmap_data >> 8);
Arduboy2Base::sBuffer[index] = data;
} }
ofs++; ofs++;
mask_ofs++; mask_ofs++;
@ -245,13 +248,15 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
// *2 because we use double the bits (mask + bitmap) // *2 because we use double the bits (mask + bitmap)
bofs = (uint8_t *)(bitmap + ((start_h * w) + xOffset) * 2); bofs = (uint8_t *)(bitmap + ((start_h * w) + xOffset) * 2);
uint8_t xi = rendered_width; // used for x loop below uint8_t xi = rendered_width; // counter for x loop below
uint8_t yi = loop_h; // used for y loop below
asm volatile( asm volatile(
"push r28\n" // save Y "push r28\n" // save Y
"push r29\n" "push r29\n"
"movw r28, %[buffer_page2_ofs]\n" // Y = buffer page 2 offset "movw r28, %[buffer_ofs]\n" // Y = buffer_ofs_2
"adiw r28, 63\n" // buffer_ofs_2 = buffer_ofs + 128
"adiw r28, 63\n"
"adiw r28, 2\n"
"loop_y:\n" "loop_y:\n"
"loop_x:\n" "loop_x:\n"
// load bitmap and mask data // load bitmap and mask data
@ -266,7 +271,6 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
"mul %A[mask_data], %[mul_amt]\n" "mul %A[mask_data], %[mul_amt]\n"
"movw %[mask_data], r0\n" "movw %[mask_data], r0\n"
// SECOND PAGE // SECOND PAGE
// if yOffset != 0 && sRow < 7 // if yOffset != 0 && sRow < 7
"cpi %[sRow], 7\n" "cpi %[sRow], 7\n"
@ -282,7 +286,6 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
"end_second_page:\n" "end_second_page:\n"
"skip_shifting:\n" "skip_shifting:\n"
// FIRST PAGE // FIRST PAGE
// if sRow >= 0 // if sRow >= 0
"tst %[sRow]\n" "tst %[sRow]\n"
@ -330,26 +333,31 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
"pop r29\n" "pop r29\n"
"pop r28\n" "pop r28\n"
"clr __zero_reg__\n" // just in case "clr __zero_reg__\n" // just in case
: [xi] "+&r" (xi), : [xi] "+&a" (xi),
[yi] "+&r" (yi), [yi] "+&a" (loop_h),
[sRow] "+&d" (sRow), // CPI requires an upper register [sRow] "+&a" (sRow), // CPI requires an upper register (r16-r23)
[data] "=&r" (data), [data] "=&l" (data),
[mask_data] "=&r" (mask_data), [mask_data] "=&l" (mask_data),
[bitmap_data] "=&r" (bitmap_data) [bitmap_data] "=&l" (bitmap_data)
: :
[x_count] "r" (rendered_width), [screen_width] "M" (WIDTH),
[y_count] "r" (loop_h), [x_count] "l" (rendered_width), // lower register
[sprite_ofs] "z" (bofs), [sprite_ofs] "z" (bofs),
[buffer_ofs] "x" (Arduboy2Base::sBuffer+ofs), [buffer_ofs] "x" (Arduboy2Base::sBuffer+ofs),
[buffer_page2_ofs] "r" (Arduboy2Base::sBuffer+ofs+WIDTH), // Y pointer [buffer_ofs_jump] "a" (WIDTH-rendered_width), // upper reg (r16-r23)
[buffer_ofs_jump] "r" (WIDTH-rendered_width), [sprite_ofs_jump] "a" ((w-rendered_width)*2), // upper reg (r16-r23)
[sprite_ofs_jump] "r" ((w-rendered_width)*2),
[yOffset] "r" (yOffset), // [sprite_ofs_jump] "r" (0),
[mul_amt] "r" (mul_amt) [yOffset] "l" (yOffset), // lower register
// declaring an extra high register clobber here for some reason [mul_amt] "l" (mul_amt) // lower register
// prevents a compile error for some sketches: // NOTE: We also clobber r28 and r29 (y) but sometimes the compiler
// can't find a register in class 'LD_REGS' while reloading 'asm' // won't allow us, so in order to make this work we don't tell it
: "r24" // that we clobber them. Instead, we push/pop to preserve them.
// Then we need to guarantee that the the compiler doesn't put one of
// our own variables into r28/r29.
// We do that by specifying all the inputs and outputs use either
// lower registers (l) or simple (r16-r23) upper registers (a).
: // pushes/clobbers/pops r28 and r29 (y)
); );
break; break;
} }

View File

@ -8,14 +8,7 @@
#define Sprites_h #define Sprites_h
#include "Arduboy2.h" #include "Arduboy2.h"
#include "SpritesCommon.h"
#define SPRITE_MASKED 1
#define SPRITE_UNMASKED 2
#define SPRITE_OVERWRITE 2
#define SPRITE_PLUS_MASK 3
#define SPRITE_IS_MASK 250
#define SPRITE_IS_MASK_ERASE 251
#define SPRITE_AUTO_MODE 255
/** \brief /** \brief
* A class for drawing animated sprites from image and mask bitmaps. * A class for drawing animated sprites from image and mask bitmaps.
@ -39,7 +32,7 @@
* and height are not included but must contain data of the same dimensions * and height are not included but must contain data of the same dimensions
* as the corresponding image array. * as the corresponding image array.
* *
* Following the width and height values for an image array, or the from the * Following the width and height values for an image array, or from the
* beginning of a separate mask array, the array contains the image and/or * beginning of a separate mask array, the array contains the image and/or
* mask data for each frame. Each byte represents a vertical column of 8 pixels * mask data for each frame. Each byte represents a vertical column of 8 pixels
* with the least significant bit (bit 0) at the top. The bytes are drawn as * with the least significant bit (bit 0) at the top. The bytes are drawn as
@ -49,6 +42,35 @@
* *
* Data for each frame after the first one immediately follows the previous * Data for each frame after the first one immediately follows the previous
* frame. Frame numbers start at 0. * frame. Frame numbers start at 0.
*
* \note
* \parblock
* A separate `SpritesB` class is available as an alternative to this class.
* The only difference is that the `SpritesB` class is optimized for small
* code size rather than for execution speed. One or the other can be used
* depending on whether size or speed is more important.
*
* Even if the speed is acceptable when using `SpritesB`, you should still try
* using `Sprites`. In some cases `Sprites` will produce less code than
* `SpritesB`, notably when only one of the functions is used.
*
* You can easily switch between using the `Sprites` class or the `SpritesB`
* class by using one or the other to create an object instance:
*
* \code{.cpp}
* Sprites sprites; // Use this to optimize for execution speed
* SpritesB sprites; // Use this to (likely) optimize for code size
* \endcode
* \endparblock
*
* \note
* \parblock
* In the example patterns given in each Sprites function description,
* a # character represents a bit set to 1 and
* a - character represents a bit set to 0.
* \endparblock
*
* \see SpritesB
*/ */
class Sprites class Sprites
{ {
@ -67,25 +89,28 @@ class Sprites
* An array containing the image frames, and another array containing * An array containing the image frames, and another array containing
* corresponding mask frames, are used to draw a sprite. * corresponding mask frames, are used to draw a sprite.
* *
* For the mask array, the width and height are not included but must
* contain data of the same dimensions as the corresponding image array.
*
* Bits set to 1 in the mask indicate that the pixel will be set to the * Bits set to 1 in the mask indicate that the pixel will be set to the
* value of the corresponding image bit. Bits set to 0 in the mask will be * value of the corresponding image bit. Bits set to 0 in the mask will be
* left unchanged. * left unchanged.
* *
* image mask before after * image mask before after (# = 1, - = 0)
* *
* ..... .OOO. ..... ..... * ----- -###- ----- -----
* ..O.. OOOOO ..... ..O.. * --#-- ##### ----- --#--
* OO.OO OO.OO ..... OO.OO * ##-## ##-## ----- ##-##
* ..O.. OOOOO ..... ..O.. * --#-- ##### ----- --#--
* ..... .OOO. ..... ..... * ----- -###- ----- -----
* *
* image mask before after * image mask before after
* *
* ..... .OOO. OOOOO O...O * ----- -###- ##### #---#
* ..O.. OOOOO OOOOO ..O.. * --#-- ##### ##### --#--
* OO.OO OOOOO OOOOO OO.OO * ##-## ##### ##### ##-##
* ..O.. OOOOO OOOOO ..O.. * --#-- ##### ##### --#--
* ..... .OOO. OOOOO O...O * ----- -###- ##### #---#
*/ */
static void drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap, static void drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
const uint8_t *mask, uint8_t frame, uint8_t mask_frame); const uint8_t *mask, uint8_t frame, uint8_t mask_frame);
@ -108,21 +133,21 @@ class Sprites
* value of the corresponding image bit. Bits set to 0 in the mask will be * value of the corresponding image bit. Bits set to 0 in the mask will be
* left unchanged. * left unchanged.
* *
* image mask before after * image mask before after (# = 1, - = 0)
* *
* ..... .OOO. ..... ..... * ----- -###- ----- -----
* ..O.. OOOOO ..... ..O.. * --#-- ##### ----- --#--
* OO.OO OO.OO ..... OO.OO * ##-## ##-## ----- ##-##
* ..O.. OOOOO ..... ..O.. * --#-- ##### ----- --#--
* ..... .OOO. ..... ..... * ----- -###- ----- -----
* *
* image mask before after * image mask before after
* *
* ..... .OOO. OOOOO O...O * ----- -###- ##### #---#
* ..O.. OOOOO OOOOO ..O.. * --#-- ##### ##### --#--
* OO.OO OOOOO OOOOO OO.OO * ##-## ##### ##### ##-##
* ..O.. OOOOO OOOOO ..O.. * --#-- ##### ##### --#--
* ..... .OOO. OOOOO O...O * ----- -###- ##### #---#
*/ */
static void drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); static void drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
@ -139,21 +164,21 @@ class Sprites
* to 1 in the frame will set the pixel to 1 in the buffer, and a 0 in the * to 1 in the frame will set the pixel to 1 in the buffer, and a 0 in the
* array will set a 0 in the buffer. * array will set a 0 in the buffer.
* *
* image before after * image before after (# = 1, - = 0)
* *
* ..... ..... ..... * ----- ----- -----
* ..O.. ..... ..O.. * --#-- ----- --#--
* OO.OO ..... OO.OO * ##-## ----- ##-##
* ..O.. ..... ..O.. * --#-- ----- --#--
* ..... ..... ..... * ----- ----- -----
* *
* image before after * image before after
* *
* ..... OOOOO ..... * ----- ##### -----
* ..O.. OOOOO ..O.. * --#-- ##### --#--
* OO.OO OOOOO OO.OO * ##-## ##### ##-##
* ..O.. OOOOO ..O.. * --#-- ##### --#--
* ..... OOOOO ..... * ----- ##### -----
*/ */
static void drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); static void drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
@ -170,21 +195,21 @@ class Sprites
* corresponding pixel in the buffer to 0. Frame bits set to 0 will remain * corresponding pixel in the buffer to 0. Frame bits set to 0 will remain
* unchanged in the buffer. * unchanged in the buffer.
* *
* image before after * image before after (# = 1, - = 0)
* *
* ..... ..... ..... * ----- ----- -----
* ..O.. ..... ..... * --#-- ----- -----
* OO.OO ..... ..... * ##-## ----- -----
* ..O.. ..... ..... * --#-- ----- -----
* ..... ..... ..... * ----- ----- -----
* *
* image before after * image before after
* *
* ..... OOOOO OOOOO * ----- ##### #####
* ..O.. OOOOO OO.OO * --#-- ##### ##-##
* OO.OO OOOOO ..O.. * ##-## ##### --#--
* ..O.. OOOOO OO.OO * --#-- ##### ##-##
* ..... OOOOO OOOOO * ----- ##### #####
*/ */
static void drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); static void drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
@ -200,21 +225,21 @@ class Sprites
* the corresponding pixel in the buffer to 1. Bits set to 0 in the frame * the corresponding pixel in the buffer to 1. Bits set to 0 in the frame
* will remain unchanged in the buffer. * will remain unchanged in the buffer.
* *
* image before after * image before after (# = 1, - = 0)
* *
* ..... ..... ..... * ----- ----- -----
* ..O.. ..... ..O.. * --#-- ----- --#--
* OO.OO ..... OO.OO * ##-## ----- ##-##
* ..O.. ..... ..O.. * --#-- ----- --#--
* ..... ..... ..... * ----- ----- -----
* *
* image before after * image before after
* *
* ..... OOOOO OOOOO (no change because all pixels were * ----- ##### ##### (no change because all pixels were
* ..O.. OOOOO OOOOO already white) * --#-- ##### ##### already white)
* OO.OO OOOOO OOOOO * ##-## ##### #####
* ..O.. OOOOO OOOOO * --#-- ##### #####
* ..... OOOOO OOOOO * ----- ##### #####
*/ */
static void drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); static void drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);

177
src/SpritesB.cpp Normal file
View File

@ -0,0 +1,177 @@
/**
* @file SpritesB.cpp
* \brief
* A class for drawing animated sprites from image and mask bitmaps.
* Optimized for small code size.
*/
#include "SpritesB.h"
void SpritesB::drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
const uint8_t *mask, uint8_t frame, uint8_t mask_frame)
{
draw(x, y, bitmap, frame, mask, mask_frame, SPRITE_MASKED);
}
void SpritesB::drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
{
draw(x, y, bitmap, frame, NULL, 0, SPRITE_OVERWRITE);
}
void SpritesB::drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
{
draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK_ERASE);
}
void SpritesB::drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
{
draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK);
}
void SpritesB::drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
{
draw(x, y, bitmap, frame, NULL, 0, SPRITE_PLUS_MASK);
}
//common functions
void SpritesB::draw(int16_t x, int16_t y,
const uint8_t *bitmap, uint8_t frame,
const uint8_t *mask, uint8_t sprite_frame,
uint8_t drawMode)
{
unsigned int frame_offset;
if (bitmap == NULL)
return;
uint8_t width = pgm_read_byte(bitmap);
uint8_t height = pgm_read_byte(++bitmap);
bitmap++;
if (frame > 0 || sprite_frame > 0) {
frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));
// sprite plus mask uses twice as much space for each frame
if (drawMode == SPRITE_PLUS_MASK) {
frame_offset *= 2;
} else if (mask != NULL) {
mask += sprite_frame * frame_offset;
}
bitmap += frame * frame_offset;
}
// if we're detecting the draw mode then base it on whether a mask
// was passed as a separate object
if (drawMode == SPRITE_AUTO_MODE) {
drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED;
}
drawBitmap(x, y, bitmap, mask, width, height, drawMode);
}
void SpritesB::drawBitmap(int16_t x, int16_t y,
const uint8_t *bitmap, const uint8_t *mask,
uint8_t w, uint8_t h, uint8_t draw_mode)
{
// no need to draw at all of we're offscreen
if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1)
return;
if (bitmap == NULL)
return;
// xOffset technically doesn't need to be 16 bit but the math operations
// are measurably faster if it is
uint16_t xOffset, ofs;
int8_t yOffset = y & 7;
int8_t sRow = y / 8;
uint8_t loop_h, start_h, rendered_width;
if (y < 0 && yOffset > 0) {
sRow--;
}
// if the left side of the render is offscreen skip those loops
if (x < 0) {
xOffset = abs(x);
} else {
xOffset = 0;
}
// if the right side of the render is offscreen skip those loops
if (x + w > WIDTH - 1) {
rendered_width = ((WIDTH - x) - xOffset);
} else {
rendered_width = (w - xOffset);
}
// if the top side of the render is offscreen skip those loops
if (sRow < -1) {
start_h = abs(sRow) - 1;
} else {
start_h = 0;
}
loop_h = h / 8 + (h % 8 > 0 ? 1 : 0); // divide, then round up
// if (sRow + loop_h - 1 > (HEIGHT/8)-1)
if (sRow + loop_h > (HEIGHT / 8)) {
loop_h = (HEIGHT / 8) - sRow;
}
// prepare variables for loops later so we can compare with 0
// instead of comparing two variables
loop_h -= start_h;
sRow += start_h;
ofs = (sRow * WIDTH) + x + xOffset;
uint8_t mul_amt = 1 << yOffset;
uint16_t mask_data;
uint16_t bitmap_data;
const uint8_t ofs_step = draw_mode == SPRITE_PLUS_MASK ? 2 : 1;
const uint8_t ofs_stride = (w - rendered_width)*ofs_step;
const uint16_t initial_bofs = ((start_h * w) + xOffset)*ofs_step;
const uint8_t *bofs = bitmap + initial_bofs;
const uint8_t *mask_ofs = !mask ? bitmap : mask;
mask_ofs += initial_bofs + ofs_step - 1;
for (uint8_t a = 0; a < loop_h; a++) {
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
uint8_t data;
bitmap_data = pgm_read_byte(bofs) * mul_amt;
mask_data = ~bitmap_data;
if (draw_mode == SPRITE_UNMASKED) {
mask_data = ~(0xFF * mul_amt);
} else if (draw_mode == SPRITE_IS_MASK_ERASE) {
bitmap_data = 0;
} else {
mask_data = ~(pgm_read_byte(mask_ofs) * mul_amt);
}
if (sRow >= 0) {
data = Arduboy2Base::sBuffer[ofs];
data &= (uint8_t)(mask_data);
data |= (uint8_t)(bitmap_data);
Arduboy2Base::sBuffer[ofs] = data;
}
if (yOffset != 0 && sRow < 7) {
const size_t index = static_cast<uint16_t>(ofs + WIDTH);
data = Arduboy2Base::sBuffer[index];
data &= (uint8_t)(mask_data >> 8);
data |= (uint8_t)(bitmap_data >> 8);
Arduboy2Base::sBuffer[index] = data;
}
ofs++;
mask_ofs += ofs_step;
bofs += ofs_step;
}
sRow++;
bofs += ofs_stride;
mask_ofs += ofs_stride;
ofs += WIDTH - rendered_width;
}
}

116
src/SpritesB.h Normal file
View File

@ -0,0 +1,116 @@
/**
* @file SpritesB.h
* \brief
* A class for drawing animated sprites from image and mask bitmaps.
* Optimized for small code size.
*/
#ifndef SpritesB_h
#define SpritesB_h
#include "Arduboy2.h"
#include "SpritesCommon.h"
/** \brief
* A class for drawing animated sprites from image and mask bitmaps.
* Optimized for small code size.
*
* \details
* The functions in this class are identical to the `Sprites` class. The only
* difference is that the functions in this class are optimized for smaller
* code size rather than execution speed.
*
* See the `Sprites` class documentation for details on the use of the
* functions in this class.
*
* Even if the speed is acceptable when using `SpritesB`, you should still try
* using `Sprites`. In some cases `Sprites` will produce less code than
* `SpritesB`, notably when only one of the functions is used.
*
* You can easily switch between using the `Sprites` class or the `SpritesB`
* class by using one or the other to create an object instance:
*
* \code{.cpp}
* Sprites sprites; // Use this to optimize for execution speed
* SpritesB sprites; // Use this to (likely) optimize for code size
* \endcode
*
* \see Sprites
*/
class SpritesB
{
public:
/** \brief
* Draw a sprite using a separate image and mask array.
*
* \param x,y The coordinates of the top left pixel location.
* \param bitmap A pointer to the array containing the image frames.
* \param mask A pointer to the array containing the mask frames.
* \param frame The frame number of the image to draw.
* \param mask_frame The frame number for the mask to use (can be different
* from the image frame number).
*
* \see Sprites::drawExternalMask()
*/
static void drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
const uint8_t *mask, uint8_t frame, uint8_t mask_frame);
/** \brief
* Draw a sprite using an array containing both image and mask values.
*
* \param x,y The coordinates of the top left pixel location.
* \param bitmap A pointer to the array containing the image/mask frames.
* \param frame The frame number of the image to draw.
*
* \see Sprites::drawPlusMask()
*/
static void drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
/** \brief
* Draw a sprite by replacing the existing content completely.
*
* \param x,y The coordinates of the top left pixel location.
* \param bitmap A pointer to the array containing the image frames.
* \param frame The frame number of the image to draw.
*
* \see Sprites::drawOverwrite()
*/
static void drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
/** \brief
* "Erase" a sprite.
*
* \param x,y The coordinates of the top left pixel location.
* \param bitmap A pointer to the array containing the image frames.
* \param frame The frame number of the image to erase.
*
* \see Sprites::drawErase()
*/
static void drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
/** \brief
* Draw a sprite using only the bits set to 1.
*
* \param x,y The coordinates of the top left pixel location.
* \param bitmap A pointer to the array containing the image frames.
* \param frame The frame number of the image to draw.
*
* \see Sprites::drawSelfMasked()
*/
static void drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
// Master function. Needs to be abstracted into separate function for
// every render type.
// (Not officially part of the API)
static void draw(int16_t x, int16_t y,
const uint8_t *bitmap, uint8_t frame,
const uint8_t *mask, uint8_t sprite_frame,
uint8_t drawMode);
// (Not officially part of the API)
static void drawBitmap(int16_t x, int16_t y,
const uint8_t *bitmap, const uint8_t *mask,
uint8_t w, uint8_t h, uint8_t draw_mode);
};
#endif

18
src/SpritesCommon.h Normal file
View File

@ -0,0 +1,18 @@
/**
* @file SpritesCommon.h
* \brief
* Common header file for sprite functions.
*/
#ifndef SpritesCommon_h
#define SpritesCommon_h
#define SPRITE_MASKED 1
#define SPRITE_UNMASKED 2
#define SPRITE_OVERWRITE 2
#define SPRITE_PLUS_MASK 3
#define SPRITE_IS_MASK 250
#define SPRITE_IS_MASK_ERASE 251
#define SPRITE_AUTO_MODE 255
#endif

View File

@ -1,83 +0,0 @@
/**
* @file ab_logo.c
* \brief
* The ARDUBOY logo bitmap.
*/
#include <avr/pgmspace.h>
#ifndef ARDUBOY_LOGO_CREATED
#define ARDUBOY_LOGO_CREATED
// arduboy_logo.png
// drawBitmap() format
// 88x16
const uint8_t arduboy_logo[] PROGMEM = {
0xF0, 0xF8, 0x9C, 0x8E, 0x87, 0x83, 0x87, 0x8E, 0x9C, 0xF8,
0xF0, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03,
0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03,
0x03, 0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0xFE, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0xC7,
0xEE, 0x7C, 0x38, 0x00, 0x00, 0xF8, 0xFC, 0x0E, 0x07, 0x03,
0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0x3F, 0x7F,
0xE0, 0xC0, 0x80, 0x80, 0xC0, 0xE0, 0x7F, 0x3F, 0xFF, 0xFF,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0x00,
0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0x0C, 0x0C, 0x1C, 0x3E, 0x77,
0xE3, 0xC1, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x1F, 0x3F, 0x70,
0xE0, 0xC0, 0xC0, 0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00,
0x7F, 0xFF, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xE3, 0x77, 0x3E,
0x1C, 0x00, 0x00, 0x1F, 0x3F, 0x70, 0xE0, 0xC0, 0xC0, 0xC0,
0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00
};
// arduboy_logo.png
// drawCompressed() format
// 88x16
const uint8_t arduboy_logo_compressed[] PROGMEM = {
0x57, 0x0F, 0x9C, 0x53, 0x72, 0x75, 0x29, 0xE5, 0x9C, 0x92,
0xCE, 0x95, 0x52, 0xAD, 0x4E, 0x49, 0xE7, 0x08, 0x09, 0xED,
0x76, 0xBB, 0xDD, 0x2A, 0xAB, 0xAC, 0x55, 0x92, 0x90, 0xD0,
0x6E, 0xB7, 0xDB, 0xAD, 0xB2, 0xCA, 0x5A, 0x25, 0xF9, 0xF8,
0xF0, 0xC6, 0x47, 0x48, 0x28, 0x95, 0x54, 0x52, 0x49, 0x25,
0x9D, 0x3A, 0x95, 0x5A, 0x3A, 0x45, 0x2A, 0xB7, 0x29, 0xA7,
0xE4, 0x76, 0xBB, 0x55, 0x56, 0x59, 0xAB, 0x24, 0x9F, 0x5D,
0x5B, 0x65, 0xD7, 0xE9, 0xEC, 0x92, 0x29, 0x3B, 0xA1, 0x4E,
0xA7, 0xD3, 0xE9, 0x74, 0x9A, 0x8F, 0x8F, 0xEF, 0xED, 0x76,
0xBB, 0x55, 0x4E, 0xAE, 0x52, 0xAD, 0x9C, 0x9C, 0x4F, 0xE7,
0xED, 0x76, 0xBB, 0xDD, 0x2E, 0x95, 0x53, 0xD9, 0x25, 0xA5,
0x54, 0xD6, 0x2A, 0xAB, 0xEC, 0x76, 0xBB, 0x54, 0x4E, 0x65,
0x97, 0x94, 0x3A, 0x22, 0xA9, 0xA4, 0x92, 0x4A, 0x2A, 0xE9,
0x94, 0x4D, 0x2D, 0x9D, 0xA2, 0x94, 0xCA, 0x5A, 0x65, 0x95,
0xDD, 0x6E, 0x97, 0xCA, 0xA9, 0xEC, 0x12, 0x55, 0x69, 0x42,
0x7A
};
// arduboy_logo.png
// Sprites::drawSelfMasked() format
// 88x16
const uint8_t arduboy_logo_sprite[] PROGMEM = {
88, 16,
0xF0, 0xF8, 0x9C, 0x8E, 0x87, 0x83, 0x87, 0x8E, 0x9C, 0xF8,
0xF0, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03,
0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFE, 0xFF, 0x03, 0x03,
0x03, 0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0xFE, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0xC7,
0xEE, 0x7C, 0x38, 0x00, 0x00, 0xF8, 0xFC, 0x0E, 0x07, 0x03,
0x03, 0x03, 0x07, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0x3F, 0x7F,
0xE0, 0xC0, 0x80, 0x80, 0xC0, 0xE0, 0x7F, 0x3F, 0xFF, 0xFF,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0x00,
0x00, 0xFF, 0xFF, 0x0C, 0x0C, 0x0C, 0x0C, 0x1C, 0x3E, 0x77,
0xE3, 0xC1, 0x00, 0x00, 0x7F, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x1F, 0x3F, 0x70,
0xE0, 0xC0, 0xC0, 0xC0, 0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00,
0x7F, 0xFF, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xE3, 0x77, 0x3E,
0x1C, 0x00, 0x00, 0x1F, 0x3F, 0x70, 0xE0, 0xC0, 0xC0, 0xC0,
0xE0, 0x70, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00
};
#endif

View File

@ -1,274 +0,0 @@
/**
* @file glcdfont.c
* \brief
* The font definitions used to display text characters.
*/
#include <avr/io.h>
#include <avr/pgmspace.h>
#ifndef FONT5X7_H
#define FONT5X7_H
// standard ascii 5x7 font
static const unsigned char font[] PROGMEM =
{
0x00, 0x00, 0x00, 0x00, 0x00,
0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
0x18, 0x3C, 0x7E, 0x3C, 0x18,
0x1C, 0x57, 0x7D, 0x57, 0x1C,
0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
0x00, 0x18, 0x3C, 0x18, 0x00,
0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
0x00, 0x18, 0x24, 0x18, 0x00,
0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
0x30, 0x48, 0x3A, 0x06, 0x0E,
0x26, 0x29, 0x79, 0x29, 0x26,
0x40, 0x7F, 0x05, 0x05, 0x07,
0x40, 0x7F, 0x05, 0x25, 0x3F,
0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
0x7F, 0x3E, 0x1C, 0x1C, 0x08,
0x08, 0x1C, 0x1C, 0x3E, 0x7F,
0x14, 0x22, 0x7F, 0x22, 0x14,
0x5F, 0x5F, 0x00, 0x5F, 0x5F,
0x06, 0x09, 0x7F, 0x01, 0x7F,
0x00, 0x66, 0x89, 0x95, 0x6A,
0x60, 0x60, 0x60, 0x60, 0x60,
0x94, 0xA2, 0xFF, 0xA2, 0x94,
0x08, 0x04, 0x7E, 0x04, 0x08,
0x10, 0x20, 0x7E, 0x20, 0x10,
0x08, 0x08, 0x2A, 0x1C, 0x08,
0x08, 0x1C, 0x2A, 0x08, 0x08,
0x1E, 0x10, 0x10, 0x10, 0x10,
0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
0x30, 0x38, 0x3E, 0x38, 0x30,
0x06, 0x0E, 0x3E, 0x0E, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5F, 0x00, 0x00,
0x00, 0x07, 0x00, 0x07, 0x00,
0x14, 0x7F, 0x14, 0x7F, 0x14,
0x24, 0x2A, 0x7F, 0x2A, 0x12,
0x23, 0x13, 0x08, 0x64, 0x62,
0x36, 0x49, 0x56, 0x20, 0x50,
0x00, 0x08, 0x07, 0x03, 0x00,
0x00, 0x1C, 0x22, 0x41, 0x00,
0x00, 0x41, 0x22, 0x1C, 0x00,
0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
0x08, 0x08, 0x3E, 0x08, 0x08,
0x00, 0x80, 0x70, 0x30, 0x00,
0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x60, 0x60, 0x00,
0x20, 0x10, 0x08, 0x04, 0x02,
0x3E, 0x51, 0x49, 0x45, 0x3E,
0x00, 0x42, 0x7F, 0x40, 0x00,
0x72, 0x49, 0x49, 0x49, 0x46,
0x21, 0x41, 0x49, 0x4D, 0x33,
0x18, 0x14, 0x12, 0x7F, 0x10,
0x27, 0x45, 0x45, 0x45, 0x39,
0x3C, 0x4A, 0x49, 0x49, 0x31,
0x41, 0x21, 0x11, 0x09, 0x07,
0x36, 0x49, 0x49, 0x49, 0x36,
0x46, 0x49, 0x49, 0x29, 0x1E,
0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x40, 0x34, 0x00, 0x00,
0x00, 0x08, 0x14, 0x22, 0x41,
0x14, 0x14, 0x14, 0x14, 0x14,
0x00, 0x41, 0x22, 0x14, 0x08,
0x02, 0x01, 0x59, 0x09, 0x06,
0x3E, 0x41, 0x5D, 0x59, 0x4E,
0x7C, 0x12, 0x11, 0x12, 0x7C,
0x7F, 0x49, 0x49, 0x49, 0x36,
0x3E, 0x41, 0x41, 0x41, 0x22,
0x7F, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x49, 0x49, 0x49, 0x41,
0x7F, 0x09, 0x09, 0x09, 0x01,
0x3E, 0x41, 0x41, 0x51, 0x73,
0x7F, 0x08, 0x08, 0x08, 0x7F,
0x00, 0x41, 0x7F, 0x41, 0x00,
0x20, 0x40, 0x41, 0x3F, 0x01,
0x7F, 0x08, 0x14, 0x22, 0x41,
0x7F, 0x40, 0x40, 0x40, 0x40,
0x7F, 0x02, 0x1C, 0x02, 0x7F,
0x7F, 0x04, 0x08, 0x10, 0x7F,
0x3E, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x09, 0x09, 0x09, 0x06,
0x3E, 0x41, 0x51, 0x21, 0x5E,
0x7F, 0x09, 0x19, 0x29, 0x46,
0x26, 0x49, 0x49, 0x49, 0x32,
0x03, 0x01, 0x7F, 0x01, 0x03,
0x3F, 0x40, 0x40, 0x40, 0x3F,
0x1F, 0x20, 0x40, 0x20, 0x1F,
0x3F, 0x40, 0x38, 0x40, 0x3F,
0x63, 0x14, 0x08, 0x14, 0x63,
0x03, 0x04, 0x78, 0x04, 0x03,
0x61, 0x59, 0x49, 0x4D, 0x43,
0x00, 0x7F, 0x41, 0x41, 0x41,
0x02, 0x04, 0x08, 0x10, 0x20,
0x00, 0x41, 0x41, 0x41, 0x7F,
0x04, 0x02, 0x01, 0x02, 0x04,
0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x03, 0x07, 0x08, 0x00,
0x20, 0x54, 0x54, 0x78, 0x40,
0x7F, 0x28, 0x44, 0x44, 0x38,
0x38, 0x44, 0x44, 0x44, 0x28,
0x38, 0x44, 0x44, 0x28, 0x7F,
0x38, 0x54, 0x54, 0x54, 0x18,
0x00, 0x08, 0x7E, 0x09, 0x02,
0x18, 0xA4, 0xA4, 0x9C, 0x78,
0x7F, 0x08, 0x04, 0x04, 0x78,
0x00, 0x44, 0x7D, 0x40, 0x00,
0x20, 0x40, 0x40, 0x3D, 0x00,
0x7F, 0x10, 0x28, 0x44, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00,
0x7C, 0x04, 0x78, 0x04, 0x78,
0x7C, 0x08, 0x04, 0x04, 0x78,
0x38, 0x44, 0x44, 0x44, 0x38,
0xFC, 0x18, 0x24, 0x24, 0x18,
0x18, 0x24, 0x24, 0x18, 0xFC,
0x7C, 0x08, 0x04, 0x04, 0x08,
0x48, 0x54, 0x54, 0x54, 0x24,
0x04, 0x04, 0x3F, 0x44, 0x24,
0x3C, 0x40, 0x40, 0x20, 0x7C,
0x1C, 0x20, 0x40, 0x20, 0x1C,
0x3C, 0x40, 0x30, 0x40, 0x3C,
0x44, 0x28, 0x10, 0x28, 0x44,
0x4C, 0x90, 0x90, 0x90, 0x7C,
0x44, 0x64, 0x54, 0x4C, 0x44,
0x00, 0x08, 0x36, 0x41, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00,
0x02, 0x01, 0x02, 0x04, 0x02,
0x3C, 0x26, 0x23, 0x26, 0x3C,
0x1E, 0xA1, 0xA1, 0x61, 0x12,
0x3A, 0x40, 0x40, 0x20, 0x7A,
0x38, 0x54, 0x54, 0x55, 0x59,
0x21, 0x55, 0x55, 0x79, 0x41,
0x21, 0x54, 0x54, 0x78, 0x41,
0x21, 0x55, 0x54, 0x78, 0x40,
0x20, 0x54, 0x55, 0x79, 0x40,
0x0C, 0x1E, 0x52, 0x72, 0x12,
0x39, 0x55, 0x55, 0x55, 0x59,
0x39, 0x54, 0x54, 0x54, 0x59,
0x39, 0x55, 0x54, 0x54, 0x58,
0x00, 0x00, 0x45, 0x7C, 0x41,
0x00, 0x02, 0x45, 0x7D, 0x42,
0x00, 0x01, 0x45, 0x7C, 0x40,
0xF0, 0x29, 0x24, 0x29, 0xF0,
0xF0, 0x28, 0x25, 0x28, 0xF0,
0x7C, 0x54, 0x55, 0x45, 0x00,
0x20, 0x54, 0x54, 0x7C, 0x54,
0x7C, 0x0A, 0x09, 0x7F, 0x49,
0x32, 0x49, 0x49, 0x49, 0x32,
0x32, 0x48, 0x48, 0x48, 0x32,
0x32, 0x4A, 0x48, 0x48, 0x30,
0x3A, 0x41, 0x41, 0x21, 0x7A,
0x3A, 0x42, 0x40, 0x20, 0x78,
0x00, 0x9D, 0xA0, 0xA0, 0x7D,
0x39, 0x44, 0x44, 0x44, 0x39,
0x3D, 0x40, 0x40, 0x40, 0x3D,
0x3C, 0x24, 0xFF, 0x24, 0x24,
0x48, 0x7E, 0x49, 0x43, 0x66,
0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
0xFF, 0x09, 0x29, 0xF6, 0x20,
0xC0, 0x88, 0x7E, 0x09, 0x03,
0x20, 0x54, 0x54, 0x79, 0x41,
0x00, 0x00, 0x44, 0x7D, 0x41,
0x30, 0x48, 0x48, 0x4A, 0x32,
0x38, 0x40, 0x40, 0x22, 0x7A,
0x00, 0x7A, 0x0A, 0x0A, 0x72,
0x7D, 0x0D, 0x19, 0x31, 0x7D,
0x26, 0x29, 0x29, 0x2F, 0x28,
0x26, 0x29, 0x29, 0x29, 0x26,
0x30, 0x48, 0x4D, 0x40, 0x20,
0x38, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x38,
0x2F, 0x10, 0xC8, 0xAC, 0xBA,
0x2F, 0x10, 0x28, 0x34, 0xFA,
0x00, 0x00, 0x7B, 0x00, 0x00,
0x08, 0x14, 0x2A, 0x14, 0x22,
0x22, 0x14, 0x2A, 0x14, 0x08,
0x95, 0x00, 0x22, 0x00, 0x95,
0xAA, 0x00, 0x55, 0x00, 0xAA,
0xAA, 0x55, 0xAA, 0x55, 0xAA,
0x00, 0x00, 0x00, 0xFF, 0x00,
0x10, 0x10, 0x10, 0xFF, 0x00,
0x14, 0x14, 0x14, 0xFF, 0x00,
0x10, 0x10, 0xFF, 0x00, 0xFF,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x14, 0x14, 0x14, 0xFC, 0x00,
0x14, 0x14, 0xF7, 0x00, 0xFF,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x14, 0x14, 0xF4, 0x04, 0xFC,
0x14, 0x14, 0x17, 0x10, 0x1F,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0x1F, 0x00,
0x10, 0x10, 0x10, 0xF0, 0x00,
0x00, 0x00, 0x00, 0x1F, 0x10,
0x10, 0x10, 0x10, 0x1F, 0x10,
0x10, 0x10, 0x10, 0xF0, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0xFF, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x14,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x1F, 0x10, 0x17,
0x00, 0x00, 0xFC, 0x04, 0xF4,
0x14, 0x14, 0x17, 0x10, 0x17,
0x14, 0x14, 0xF4, 0x04, 0xF4,
0x00, 0x00, 0xFF, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x14, 0x14,
0x14, 0x14, 0xF7, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x17, 0x14,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0xF4, 0x14,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x00, 0x00, 0x1F, 0x10, 0x1F,
0x00, 0x00, 0x00, 0x1F, 0x14,
0x00, 0x00, 0x00, 0xFC, 0x14,
0x00, 0x00, 0xF0, 0x10, 0xF0,
0x10, 0x10, 0xFF, 0x10, 0xFF,
0x14, 0x14, 0x14, 0xFF, 0x14,
0x10, 0x10, 0x10, 0x1F, 0x00,
0x00, 0x00, 0x00, 0xF0, 0x10,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x38, 0x44, 0x44, 0x38, 0x44,
0x7C, 0x2A, 0x2A, 0x3E, 0x14,
0x7E, 0x02, 0x02, 0x06, 0x06,
0x02, 0x7E, 0x02, 0x7E, 0x02,
0x63, 0x55, 0x49, 0x41, 0x63,
0x38, 0x44, 0x44, 0x3C, 0x04,
0x40, 0x7E, 0x20, 0x1E, 0x20,
0x06, 0x02, 0x7E, 0x02, 0x02,
0x99, 0xA5, 0xE7, 0xA5, 0x99,
0x1C, 0x2A, 0x49, 0x2A, 0x1C,
0x4C, 0x72, 0x01, 0x72, 0x4C,
0x30, 0x4A, 0x4D, 0x4D, 0x30,
0x30, 0x48, 0x78, 0x48, 0x30,
0xBC, 0x62, 0x5A, 0x46, 0x3D,
0x3E, 0x49, 0x49, 0x49, 0x00,
0x7E, 0x01, 0x01, 0x01, 0x7E,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x44, 0x44, 0x5F, 0x44, 0x44,
0x40, 0x51, 0x4A, 0x44, 0x40,
0x40, 0x44, 0x4A, 0x51, 0x40,
0x00, 0x00, 0xFF, 0x01, 0x03,
0xE0, 0x80, 0xFF, 0x00, 0x00,
0x08, 0x08, 0x6B, 0x6B, 0x08,
0x36, 0x12, 0x36, 0x24, 0x36,
0x06, 0x0F, 0x09, 0x0F, 0x06,
0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x00, 0x10, 0x10, 0x00,
0x30, 0x40, 0xFF, 0x01, 0x01,
0x00, 0x1F, 0x01, 0x01, 0x1E,
0x00, 0x19, 0x1D, 0x17, 0x12,
0x00, 0x3C, 0x3C, 0x3C, 0x3C,
0x00, 0x00, 0x00, 0x00, 0x00,
};
#endif