Squash commits up to Arduboy V1.1 release

This commit is contained in:
Arduboy 2015-04-30 22:26:58 -07:00 committed by Scott Allen
parent 7c4b38cb7d
commit 982175ecac
27 changed files with 2784 additions and 944 deletions

14
.gitignore vendored
View File

@ -1 +1,15 @@
# Ignore vim temp and swap files
*~
*.swp
*.swo
# Ignore working files
.build
Makefile
ino.ini
work
*.eep
# Ignore OS bookkeeping
.DS_Store
Thumbs.db

View File

@ -1,681 +0,0 @@
#include "Arduboy.h"
#include "glcdfont.c"
Arduboy::Arduboy() { }
void Arduboy::start()
{
pinMode(DC, OUTPUT);
pinMode(CS, OUTPUT);
pinMode(8, INPUT_PULLUP);
pinMode(9, INPUT_PULLUP);
pinMode(10, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
csport = portOutputRegister(digitalPinToPort(CS));
cspinmask = digitalPinToBitMask(CS);
dcport = portOutputRegister(digitalPinToPort(DC));
dcpinmask = digitalPinToBitMask(DC);
/**
* Setup reset pin direction (used by both SPI and I2C)
*/
pinMode(RST, OUTPUT);
digitalWrite(RST, HIGH);
delay(1); // VDD (3.3V) goes high at start, lets just chill for a ms
digitalWrite(RST, LOW); // bring reset low
delay(10); // wait 10ms
digitalWrite(RST, HIGH); // bring out of reset
*csport |= cspinmask;
*dcport &= ~dcpinmask;
*csport &= ~cspinmask;
SPI.transfer(0xAE); // Display Off
SPI.transfer(0XD5); // Set Display Clock Divisor v
SPI.transfer(0xF0); // 0x80 is default
SPI.transfer(0xA8); // Set Multiplex Ratio v
SPI.transfer(0x3F);
SPI.transfer(0xD3); // Set Display Offset v
SPI.transfer(0x0);
SPI.transfer(0x40); // Set Start Line (0)
SPI.transfer(0x8D); // Charge Pump Setting v
SPI.transfer(0x14); // Enable
SPI.transfer(0x20); // Set Memory Mode v
SPI.transfer(0x00); // Horizontal Addressing
SPI.transfer(0xA1); // Set Segment Re-map (A0) | (b0001)
SPI.transfer(0xC8); // Set COM Output Scan Direction
SPI.transfer(0xDA); // Set COM Pins v
SPI.transfer(0x12);
SPI.transfer(0x81); // Set Contrast v
SPI.transfer(0xCF);
SPI.transfer(0xD9); // Set Precharge
SPI.transfer(0xF1);
SPI.transfer(0xDB); // Set VCom Detect
SPI.transfer(0x40);
SPI.transfer(0xA4); // Entire Display ON
SPI.transfer(0xA6); // Set normal/inverse display
SPI.transfer(0xAF); // Display On
*csport |= cspinmask;
*csport |= cspinmask;
*dcport &= ~dcpinmask;
*csport &= ~cspinmask;
SPI.transfer(0x20); // set display mode
SPI.transfer(0x00); // horizontal addressing mode
SPI.transfer(0x21); // set col address
unsigned char start = 0;
unsigned char end = WIDTH - 1;
SPI.transfer(start & 0x7F);
SPI.transfer(end & 0x7F);
SPI.transfer(0x22); // set page address
start = 0;
end = (HEIGHT/8)-1;
SPI.transfer(start & 0x07);
SPI.transfer(end & 0x07);
*dcport |= dcpinmask;
*csport &= ~cspinmask;
}
void Arduboy::blank()
{
for (int a = 0; a < (HEIGHT*WIDTH)/8; a++) SPI.transfer(0x00);
}
void Arduboy::clearDisplay()
{
for (int a = 0; a < (HEIGHT*WIDTH)/8; a++) sBuffer[a] = 0x00;
}
void Arduboy::drawPixel(int x, int y, uint16_t value)
{
if (x < 0 || x > (WIDTH-1) || y < 0 || y > (HEIGHT-1))
{
return;
}
int row = y / 8;
if (value)
{
sBuffer[(row*WIDTH) + x] |= 1 << (y % 8);
}
else
{
sBuffer[(row*WIDTH) + x] &= ~(1 << (y % 8));
}
}
void Arduboy::drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
{
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
drawPixel(x0, y0+r, color);
drawPixel(x0, y0-r, color);
drawPixel(x0+r, y0, color);
drawPixel(x0-r, y0, color);
while (x<y)
{
if (f >= 0)
{
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
drawPixel(x0 + x, y0 + y, color);
drawPixel(x0 - x, y0 + y, color);
drawPixel(x0 + x, y0 - y, color);
drawPixel(x0 - x, y0 - y, color);
drawPixel(x0 + y, y0 + x, color);
drawPixel(x0 - y, y0 + x, color);
drawPixel(x0 + y, y0 - x, color);
drawPixel(x0 - y, y0 - x, color);
}
}
void Arduboy::drawCircleHelper
(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color)
{
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
while (x<y)
{
if (f >= 0)
{
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
if (cornername & 0x4)
{
drawPixel(x0 + x, y0 + y, color);
drawPixel(x0 + y, y0 + x, color);
}
if (cornername & 0x2)
{
drawPixel(x0 + x, y0 - y, color);
drawPixel(x0 + y, y0 - x, color);
}
if (cornername & 0x8)
{
drawPixel(x0 - y, y0 + x, color);
drawPixel(x0 - x, y0 + y, color);
}
if (cornername & 0x1)
{
drawPixel(x0 - y, y0 - x, color);
drawPixel(x0 - x, y0 - y, color);
}
}
}
void Arduboy::fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
{
drawFastVLine(x0, y0-r, 2*r+1, color);
fillCircleHelper(x0, y0, r, 3, 0, color);
}
void Arduboy::fillCircleHelper
(
int16_t x0,
int16_t y0,
int16_t r,
uint8_t cornername,
int16_t delta,
uint16_t color
)
{
// used to do circles and roundrects!
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
while (x < y)
{
if (f >= 0)
{
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
if (cornername & 0x1)
{
drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
}
if (cornername & 0x2)
{
drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
}
}
}
void Arduboy::drawLine
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color)
{
// bresenham's algorithm - thx wikpedia
int16_t steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
swap(x0, y0);
swap(x1, y1);
}
if (x0 > x1) {
swap(x0, x1);
swap(y0, y1);
}
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t err = dx / 2;
int16_t ystep;
if (y0 < y1)
{
ystep = 1;
}
else
{
ystep = -1;
}
for (; x0 <= x1; x0++)
{
if (steep)
{
drawPixel(y0, x0, color);
}
else
{
drawPixel(x0, y0, color);
}
err -= dy;
if (err < 0)
{
y0 += ystep;
err += dx;
}
}
}
void Arduboy::drawRect
(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
drawFastHLine(x, y, w, color);
drawFastHLine(x, y+h-1, w, color);
drawFastVLine(x, y, h, color);
drawFastVLine(x+w-1, y, h, color);
}
void Arduboy::drawFastVLine
(int16_t x, int16_t y, int16_t h, uint16_t color)
{
int end = y+h;
for (int a = max(0,y); a < min(end,HEIGHT); a++)
{
drawPixel(x,a,color);
}
}
void Arduboy::drawFastHLine
(int16_t x, int16_t y, int16_t w, uint16_t color)
{
int end = x+w;
for (int a = max(0,x); a < min(end,WIDTH); a++)
{
drawPixel(a,y,color);
}
}
void Arduboy::fillRect
(int16_t x, int16_t y, int16_t w, int16_t h, int16_t color)
{
// stupidest version - update in subclasses if desired!
for (int16_t i=x; i<x+w; i++)
{
drawFastVLine(i, y, h, color);
}
}
void Arduboy::fillScreen(uint16_t color)
{
fillRect(0, 0, WIDTH, HEIGHT, color);
}
void Arduboy::drawRoundRect
(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
// smarter version
drawFastHLine(x+r, y, w-2*r, color); // Top
drawFastHLine(x+r, y+h-1, w-2*r, color); // Bottom
drawFastVLine(x, y+r, h-2*r, color); // Left
drawFastVLine(x+w-1, y+r, h-2*r, color); // Right
// draw four corners
drawCircleHelper(x+r, y+r, r, 1, color);
drawCircleHelper(x+w-r-1, y+r, r, 2, color);
drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
drawCircleHelper(x+r, y+h-r-1, r, 8, color);
}
void Arduboy::fillRoundRect
(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color)
{
// smarter version
fillRect(x+r, y, w-2*r, h, color);
// draw four corners
fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
fillCircleHelper(x+r, y+r, r, 2, h-2*r-1, color);
}
void Arduboy::drawTriangle
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
drawLine(x0, y0, x1, y1, color);
drawLine(x1, y1, x2, y2, color);
drawLine(x2, y2, x0, y0, color);
}
void Arduboy::fillTriangle
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
int16_t a, b, y, last;
// Sort coordinates by Y order (y2 >= y1 >= y0)
if (y0 > y1)
{
swap(y0, y1); swap(x0, x1);
}
if (y1 > y2)
{
swap(y2, y1); swap(x2, x1);
}
if (y0 > y1)
{
swap(y0, y1); swap(x0, x1);
}
if(y0 == y2)
{ // Handle awkward all-on-same-line case as its own thing
a = b = x0;
if(x1 < a)
{
a = x1;
}
else if(x1 > b)
{
b = x1;
}
if(x2 < a)
{
a = x2;
}
else if(x2 > b)
{
b = x2;
}
drawFastHLine(a, y0, b-a+1, color);
return;
}
int16_t dx01 = x1 - x0,
dy01 = y1 - y0,
dx02 = x2 - x0,
dy02 = y2 - y0,
dx12 = x2 - x1,
dy12 = y2 - y1,
sa = 0,
sb = 0;
// For upper part of triangle, find scanline crossings for segments
// 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
// is included here (and second loop will be skipped, avoiding a /0
// error there), otherwise scanline y1 is skipped here and handled
// in the second loop...which also avoids a /0 error here if y0=y1
// (flat-topped triangle).
if (y1 == y2)
{
last = y1; // Include y1 scanline
}
else
{
last = y1-1; // Skip it
}
for(y = y0; y <= last; y++)
{
a = x0 + sa / dy01;
b = x0 + sb / dy02;
sa += dx01;
sb += dx02;
if(a > b)
{
swap(a,b);
}
drawFastHLine(a, y, b-a+1, color);
}
// For lower part of triangle, find scanline crossings for segments
// 0-2 and 1-2. This loop is skipped if y1=y2.
sa = dx12 * (y - y1);
sb = dx02 * (y - y0);
for(; y <= y2; y++)
{
a = x1 + sa / dy12;
b = x0 + sb / dy02;
sa += dx12;
sb += dx02;
if(a > b)
{
swap(a,b);
}
drawFastHLine(a, y, b-a+1, color);
}
}
void Arduboy::drawBitmap
(int16_t x,
int16_t y,
const uint8_t *bitmap,
int16_t w,
int16_t h,
uint16_t color
)
{
//bitmap is off screen
if (x+w < 0 || x > WIDTH-1 || y+h < 0 || y > HEIGHT-1) return;
int yOffset = abs(y) % 8;
int sRow = y / 8;
if (y < 0)
{
sRow--;
yOffset = 8 - yOffset;
}
for (int a = 0; a < h/8; a++)
{
int bRow = sRow + a;
if (bRow > (HEIGHT/8)-1) break;
if (bRow > -2) {
for (int iCol = 0; iCol<w; iCol++)
{
if (iCol + x > (WIDTH-1))
{
break;
}
if (iCol + x > 0)
{
if (bRow >= 0)
{
if (color)
{
this->sBuffer[ (bRow*WIDTH) + x + iCol ]
|= pgm_read_byte(bitmap+(a*w)+iCol) << yOffset;
}
else
{
this->sBuffer[ (bRow*WIDTH) + x + iCol ]
&= ~(pgm_read_byte(bitmap+(a*w)+iCol) << yOffset);
}
}
if (yOffset && bRow<(HEIGHT/8)-1 && bRow > -2)
{
if (color)
{
this->sBuffer[ ((bRow+1)*WIDTH) + x + iCol ]
|= pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset);
}
else
{
this->sBuffer[ ((bRow+1)*WIDTH) + x + iCol ]
&= ~(pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset));
}
}
}
}
}
}
}
void Arduboy::drawChar
(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size)
{
if ((x >= WIDTH) || // Clip right
(y >= HEIGHT) || // Clip bottom
((x + 5 * size - 1) < 0) || // Clip left
((y + 8 * size - 1) < 0) // Clip top
)
{
return;
}
for (int8_t i=0; i<6; i++ )
{
uint8_t line;
if (i == 5)
{
line = 0x0;
}
else
{
line = pgm_read_byte(font+(c*5)+i);
}
for (int8_t j = 0; j<8; j++)
{
if (line & 0x1)
{
if (size == 1) // default size
{
drawPixel(x+i, y+j, color);
}
else // big size
{
fillRect(x+(i*size), y+(j*size), size, size, color);
}
}
else if (bg != color)
{
if (size == 1) // default size
{
drawPixel(x+i, y+j, bg);
}
else
{ // big size
fillRect(x+i*size, y+j*size, size, size, bg);
}
}
line >>= 1;
}
}
}
void Arduboy::setCursor(int16_t x, int16_t y)
{
cursor_x = x;
cursor_y = y;
}
void Arduboy::setTextSize(uint8_t s)
{
textsize = (s > 0) ? s : 1;
}
void Arduboy::setTextWrap(boolean w)
{
wrap = w;
}
size_t Arduboy::write(uint8_t c)
{
if (c == '\n')
{
cursor_y += textsize*8;
cursor_x = 0;
}
else if (c == '\r')
{
// skip em
}
else
{
drawChar(cursor_x, cursor_y, c, 1, 0, textsize);
cursor_x += textsize*6;
if (wrap && (cursor_x > (WIDTH - textsize*6)))
{
cursor_y += textsize*8;
cursor_x = 0;
}
}
}
void Arduboy::display()
{
this->drawScreen(sBuffer);
}
void Arduboy::drawScreen(const unsigned char *image)
{
for (int a = 0; a < (HEIGHT*WIDTH)/8; a++)
{
SPI.transfer(pgm_read_byte(image + a));
}
}
void Arduboy::drawScreen(unsigned char image[])
{
for (int a = 0; a < (HEIGHT*WIDTH)/8; a++)
{
SPI.transfer(image[a]);
}
}
uint8_t Arduboy::width() { return WIDTH; }
uint8_t Arduboy::height() { return HEIGHT; }
uint8_t Arduboy::getInput()
{
// b00lurdab
uint8_t value = B00000000;
if (digitalRead(9) == 0) { value = value | B00100000; } // left
if (digitalRead(8) == 0) { value = value | B00010000; } // up
if (digitalRead(5) == 0) { value = value | B00001000; } // right
if (digitalRead(10) == 0) { value = value | B00000100; } // down
if (digitalRead(A0) == 0) { value = value | B00000010; } // a?
if (digitalRead(A1) == 0) { value = value | B00000001; } // b?
return value;
}
void Arduboy::swap(int16_t& a, int16_t& b) {
int temp = a;
a = b;
b = temp;
}

View File

@ -1,64 +0,0 @@
#ifndef Arduboy_h
#define Arduboy_h
#include <SPI.h>
#include <Print.h>
#define CS 6
#define DC 4
#define RST 12
#define WIDTH 128
#define HEIGHT 64
class Arduboy : public Print
{
public:
Arduboy();
uint8_t getInput();
void start();
void blank();
void clearDisplay();
void display();
void drawScreen(const unsigned char *image);
void drawScreen(unsigned char image[]);
void drawPixel(int x, int y, uint16_t value);
void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color);
void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color);
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t color);
void fillScreen(uint16_t color);
void drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
void fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void fillTriangle (int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size);
void setCursor(int16_t x, int16_t y);
void setTextSize(uint8_t s);
void setTextWrap(boolean w);
uint8_t width();
uint8_t height();
virtual size_t write(uint8_t);
void swap(int16_t& a, int16_t& b);
private:
unsigned char sBuffer[(HEIGHT*WIDTH)/8];
uint8_t readCapacitivePin(int pinToMeasure);
uint8_t readCapXtal(int pinToMeasure);
volatile uint8_t *mosiport, *clkport, *csport, *dcport;
uint8_t mosipinmask, clkpinmask, cspinmask, dcpinmask;
// Adafruit stuff
protected:
int16_t cursor_x, cursor_y;
uint8_t textsize;
boolean wrap; // If set, 'wrap' text at right edge of display
};
#endif

20
CONTRIBUTORS.md Normal file
View File

@ -0,0 +1,20 @@
# Individual Contributors
- Kevin "Arduboy" Bates (@Arduboy)
- arduboychris (@arduboychris)
- Ross (@rogosher)
- Andrew (@ace-dent)
- Josh Goebel (@yyyc514)
# Included code from other open source projects
- Original SSD1306 library
https://github.com/adafruit/Adafruit_SSD1306
BSD License
Copyright (c) 2012, Adafruit Industries
- arduino-playtune
https://github.com/LenShustek/arduino-playtune
GNU General Public License v3
(C) Copyright 2011, 2015, Len Shustek

32
LICENSE Normal file
View File

@ -0,0 +1,32 @@
Software License Agreement (BSD License)
Copyright (c) 2015, Kevin "Arduboy" Bates
Copyright (c) 2015, Chris Martinez
Copyright (c) 2015, Josh Goebel
All rights reserved.
Please see CONTRIBUTORS.md for license information and copyright
notices for any libraries we built on or redistribute.
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.

View File

@ -1 +1,68 @@
#ArduBreak
Arduboy Library
===============
This library provides Arduboy's core functionality.
## Using the Library
To use the Arduboy library, it must be installed and then included in your project.
### Install
Install the library by cloning its repository.
```
$ git clone https://github.com/Arduboy/Arduboy.git
```
#### Where to Install
The library should be installed into your user's home Arduino `libraries` directory. Refer to the following list for the location of the Arduino `libraries` folder.
**Linux**
```
/home/username/Documents/Arduino/libraries
```
**Mac**
```
/Users/username/Documents/Arduino/libraries
```
**Windows**
```
C:\Users\username\My Documents\Arduino\libraries
```
If you don't find the `libraries` folder in one of the above locations, you can determine its location using the navigation menu `File > Preferences`. In the `Settings` tab will be a `Sketchbook location:` field. The `libraries` folder will be in the folder set in this field.
### Include
To use the Arduboy library in your own sketches, include the `Arduboy.h` header file. To do so, add the following line to the top of your `.ino` file.
```C
#include "Arduboy.h"
```
You can have the Arduino IDE add `#include "Arduboy.h"` to your sketch automatically by using the navigation menu `Sketch > Include Library > Arduboy`.
### Board Selection
Select the **Leonardo** board as the target platform.
### Examples
Example games and source can be found in the `examples` directory.
#### Playing Examples
Find and play an example by opening it through the Arduino IDE, compiling, and uploading the example to the Arduboy.
Examples can be found in the Arduino IDE in the navigation menu under, `File > Examples > Arduboy > Example_Name`.
### Running on a Development Board
To run this library on a development Arduboy board, edit `src/core/core.h` so that `#define AB_DEVKIT` is uncommented and `#define ARDUBOY_10` is comment out.
```cpp
//#define ARDUBOY_10 //< compile for the production Arduboy v1.0
#define AB_DEVKIT //< compile for the official dev kit
```
### Sketches Already Including the Arduboy Library
Sketches that include copies of the Arduboy library may not compile if the Arduboy library has been installed system wide. In these cases the Arduino compiler will try and link the system Arduboy library source with the local header file, which can cause compilation errors if the local library source differs from the system's Arduboy source.
To compile sketches that have included copies of the Aruboy Library,
>Remove the local `Arduboy.cpp` and `Arduboy.h` files and try recompiling. This will only work in some cases.
>
>**OR**
>
>Rename `Arduboy.h` to `CustomArduboy.h` (or a similar name) and add `#include "CustomArduboy.h"` to the `.ino` sketch file.

View File

@ -9,23 +9,13 @@
version 2.1 of the License, or (at your option) any later version.
*/
#include <SPI.h>
#include <Wire.h>
#include "Arduboy.h"
#include <EEPROM.h>
#include "breakout_bitmaps.h"
#define OLED_DC 8
#define OLED_CS 10 // SPI slave-select
#define OLED_CLK 13 // hardware SPI clock
#define OLED_MOSI 11 // hardware SPI MOSI
#define OLED_RESET 7
Arduboy arduboy;
Arduboy display;
const byte width = 128; //Width of screen
const byte height = 64; //Hight of screen
const unsigned int COLUMNS = 13; //Columns of bricks
const unsigned int ROWS = 4; //Rows of bricks
int dx = -1; //Initial movement of ball
int dy = -1; //Initial movement of ball
int xb; //Balls starting possition
@ -33,7 +23,7 @@ int yb; //Balls starting possition
boolean released; //If the ball has been released by the player
boolean paused = false; //If the game has been paused
byte xPaddle; //X position of paddle
boolean isHit[5][12]; //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
byte lives = 3; //Amount of lives
byte level = 1; //Current level
@ -58,71 +48,61 @@ byte rightBrick;
byte topBrick;
byte bottomBrick;
int ballclock = 0;
byte tick;
#include "pins_arduino.h" // Arduino pre-1.0 needs this
PROGMEM const unsigned char arduino [] =
{
0x3F, 0xFF, 0xFF, 0xFC, 0x40, 0x00, 0x00, 0x02, 0x89, 0x99,0x54,
0x91, 0x95, 0x55, 0x56, 0xA9, 0x9D, 0x95, 0x55, 0xA9, 0x95, 0x59,
0xD4, 0x91, 0x40, 0x00, 0x00, 0x02, 0x3F, 0xFF, 0xFF, 0xFC
};
void intro()
{
for(int i = -8; i < 28; i = i + 2)
{
display.clearDisplay();
display.setCursor(46, i);
display.print("ARDUBOY");
display.display();
arduboy.clear();
arduboy.setCursor(46, i);
arduboy.print("ARDUBOY");
arduboy.display();
}
tone(A2, 987, 160);
arduboy.tunes.tone(987, 160);
delay(160);
tone(A2, 1318, 400);
arduboy.tunes.tone(1318, 400);
delay(2000);
}
void setup()
{
SPI.begin();
display.start();
display.setTextSize(1);
display.setCursor(0, 0);
display.print("Hello World!");
display.display();
intro();
}
void movePaddle()
{
//Move right
if(xPaddle < width - 12)
if(xPaddle < WIDTH - 12)
{
if( !digitalRead(5) )
if (arduboy.pressed(RIGHT_BUTTON))
{
xPaddle++;
xPaddle+=2;
}
}
//Move left
if(xPaddle > 0)
{
if( !digitalRead(9))
if (arduboy.pressed(LEFT_BUTTON))
{
xPaddle--;
xPaddle-=2;
}
}
}
void moveBall()
{
tick++;
if(released)
{
//Move ball
xb=xb + dx;
if (abs(dx)==2) {
xb += dx/2;
// 2x speed is really 1.5 speed
if (tick%2==0)
xb += dx/2;
} else {
xb += dx;
}
yb=yb + dy;
//Set bounds
@ -136,19 +116,19 @@ void moveBall()
{
yb = 2;
dy = -dy;
tone(A2, 523, 250);
arduboy.tunes.tone(523, 250);
}
//Lose a life if bottom edge hit
if (yb >= 64)
{
display.drawRect(xPaddle, 63, 11, 1, 0);
arduboy.drawRect(xPaddle, 63, 11, 1, 0);
xPaddle = 54;
yb=60;
released = false;
lives--;
drawLives();
tone(A2, 175, 250);
arduboy.tunes.tone(175, 250);
if (random(0, 2) == 0)
{
dx = 1;
@ -164,15 +144,15 @@ void moveBall()
{
xb = 2;
dx = -dx;
tone(A2, 523, 250);
arduboy.tunes.tone(523, 250);
}
//Bounce off right side
if (xb >= width - 2)
if (xb >= WIDTH - 2)
{
xb = width - 4;
xb = WIDTH - 4;
dx = -dx;
tone(A2, 523, 250);
arduboy.tunes.tone(523, 250);
}
//Bounce off paddle
@ -180,13 +160,17 @@ void moveBall()
{
dy = -dy;
dx = ((xb-(xPaddle+6))/3); //Applies spin on the ball
tone(A2, 200, 250);
// prevent straight bounce
if (dx == 0) {
dx = (random(0,2) == 1) ? 1 : -1;
}
arduboy.tunes.tone(200, 250);
}
//Bounce off Bricks
for (byte row = 0; row < 4; row++)
for (byte row = 0; row < ROWS; row++)
{
for (byte column = 0; column < 14; column++)
for (byte column = 0; column < COLUMNS; column++)
{
if (!isHit[row][column])
{
@ -203,7 +187,7 @@ void moveBall()
Score();
brickCount++;
isHit[row][column] = true;
display.drawRect(10*column, 2+6*row, 8, 4, 0);
arduboy.drawRect(10*column, 2+6*row, 8, 4, 0);
//Vertical collision
if (bottomBall > bottomBrick || topBall < topBrick)
@ -214,7 +198,7 @@ void moveBall()
dy =- dy;
yb += dy;
bounced = true;
tone(A2, 261, 250);
arduboy.tunes.tone(261, 250);
}
}
@ -227,7 +211,7 @@ void moveBall()
dx =- dx;
xb += dx;
bounced = true;
tone(A2, 261, 250);
arduboy.tunes.tone(261, 250);
}
}
}
@ -243,7 +227,7 @@ void moveBall()
xb=xPaddle + 5;
//Release ball if FIRE pressed
pad3 = !digitalRead(A0);
pad3 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
if (pad3 == 1 && oldpad3 == 0)
{
released=true;
@ -266,50 +250,47 @@ void moveBall()
void drawBall()
{
display.drawPixel(xb, yb, 0);
display.drawPixel(xb+1, yb, 0);
display.drawPixel(xb, yb+1, 0);
display.drawPixel(xb+1, yb+1, 0);
// arduboy.setCursor(0,0);
// arduboy.print(arduboy.cpuLoad());
// arduboy.print(" ");
arduboy.drawPixel(xb, yb, 0);
arduboy.drawPixel(xb+1, yb, 0);
arduboy.drawPixel(xb, yb+1, 0);
arduboy.drawPixel(xb+1, yb+1, 0);
if(ballclock>4)
{
moveBall();
ballclock=0;
}
ballclock++;
display.drawPixel(xb, yb, 1);
display.drawPixel(xb+1, yb, 1);
display.drawPixel(xb, yb+1, 1);
display.drawPixel(xb+1, yb+1, 1);
arduboy.drawPixel(xb, yb, 1);
arduboy.drawPixel(xb+1, yb, 1);
arduboy.drawPixel(xb, yb+1, 1);
arduboy.drawPixel(xb+1, yb+1, 1);
}
void drawPaddle()
{
display.drawRect(xPaddle, 63, 11, 1, 0);
arduboy.drawRect(xPaddle, 63, 11, 1, 0);
movePaddle();
display.drawRect(xPaddle, 63, 11, 1, 1);
arduboy.drawRect(xPaddle, 63, 11, 1, 1);
}
void drawLives()
{
sprintf(text, "LIVES:%u", lives);
display.setCursor(0, 90);
display.print(text);
arduboy.setCursor(0, 90);
arduboy.print(text);
}
void drawGameOver()
{
display.drawPixel(xb, yb, 0);
display.drawPixel(xb+1, yb, 0);
display.drawPixel(xb, yb+1, 0);
display.drawPixel(xb+1, yb+1, 0);
display.setCursor(52, 42);
display.print( "Game");
display.setCursor(52, 54);
display.print("Over");
display.display();
arduboy.drawPixel(xb, yb, 0);
arduboy.drawPixel(xb+1, yb, 0);
arduboy.drawPixel(xb, yb+1, 0);
arduboy.drawPixel(xb+1, yb+1, 0);
arduboy.setCursor(52, 42);
arduboy.print( "Game");
arduboy.setCursor(52, 54);
arduboy.print("Over");
arduboy.display();
delay(4000);
}
@ -317,17 +298,17 @@ void pause()
{
paused = true;
//Draw pause to the screen
display.setCursor(52, 45);
display.print("PAUSE");
display.display();
arduboy.setCursor(52, 45);
arduboy.print("PAUSE");
arduboy.display();
while (paused)
{
delay(150);
//Unpause if FIRE is pressed
pad2 = !digitalRead(A0);
pad2 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
if (pad2 > 1 && oldpad2 == 0 && released)
{
display.fillRect(52, 45, 30, 11, 0);
arduboy.fillRect(52, 45, 30, 11, 0);
paused=false;
}
@ -339,19 +320,19 @@ void Score()
{
score += (level*10);
sprintf(text, "SCORE:%u", score);
display.setCursor(80, 90);
display.print(text);
arduboy.setCursor(80, 90);
arduboy.print(text);
}
void newLevel(){
//Undraw paddle
display.drawRect(xPaddle, 63, 11, 1, 0);
arduboy.drawRect(xPaddle, 63, 11, 1, 0);
//Undraw ball
display.drawPixel(xb, yb, 0);
display.drawPixel(xb+1, yb, 0);
display.drawPixel(xb, yb+1, 0);
display.drawPixel(xb+1, yb+1, 0);
arduboy.drawPixel(xb, yb, 0);
arduboy.drawPixel(xb+1, yb, 0);
arduboy.drawPixel(xb, yb+1, 0);
arduboy.drawPixel(xb+1, yb+1, 0);
//Alter various variables to reset the game
xPaddle = 54;
@ -364,7 +345,7 @@ void newLevel(){
for (byte column = 0; column < 13; column++)
{
isHit[row][column] = false;
display.drawRect(10*column, 2+6*row, 8, 4, 1);
arduboy.drawRect(10*column, 2+6*row, 8, 4, 1);
}
}
@ -373,8 +354,8 @@ void newLevel(){
//Draws the initial score
sprintf(text, "SCORE:%u", score);
display.setCursor(80, 90);
display.print(text);
arduboy.setCursor(80, 90);
arduboy.print(text);
}
//Used to delay images while reading button input
@ -383,7 +364,7 @@ boolean pollFireButton(int n)
for(int i = 0; i < n; i++)
{
delay(15);
pad = !digitalRead(A0);
pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
if(pad == 1 && oldpad == 0)
{
oldpad3 = 1; //Forces pad loop 3 to run once
@ -403,17 +384,17 @@ boolean displayHighScores(byte file)
// is 5 bytes long: 3 bytes for initials and two bytes for score.
int address = file*10*5;
byte hi, lo;
display.clearDisplay();
display.setCursor(32, 0);
display.print("HIGH SCORES");
display.display();
arduboy.clear();
arduboy.setCursor(32, 0);
arduboy.print("HIGH SCORES");
arduboy.display();
for(int i = 0; i < 10; i++)
{
sprintf(text, "%2d", i+1);
display.setCursor(x,y+(i*8));
display.print( text);
display.display();
arduboy.setCursor(x,y+(i*8));
arduboy.print( text);
arduboy.display();
hi = EEPROM.read(address + (5*i));
lo = EEPROM.read(address + (5*i) + 1);
@ -433,9 +414,9 @@ boolean displayHighScores(byte file)
if (score > 0)
{
sprintf(text, "%c%c%c %u", initials[0], initials[1], initials[2], score);
display.setCursor(x + 24, y + (i*8));
display.print(text);
display.display();
arduboy.setCursor(x + 24, y + (i*8));
arduboy.print(text);
arduboy.display();
}
}
if (pollFireButton(300))
@ -443,18 +424,18 @@ boolean displayHighScores(byte file)
return true;
}
return false;
display.display();
arduboy.display();
}
boolean titleScreen()
{
//Clears the screen
display.clearDisplay();
display.setCursor(16,22);
display.setTextSize(2);
display.print("ARAKNOID");
display.setTextSize(1);
display.display();
arduboy.clear();
arduboy.setCursor(16,22);
arduboy.setTextSize(2);
arduboy.print("ARAKNOID");
arduboy.setTextSize(1);
arduboy.display();
if (pollFireButton(25))
{
return true;
@ -464,24 +445,24 @@ boolean titleScreen()
for(byte i = 0; i < 5; i++)
{
//Draws "Press FIRE"
//display.bitmap(31, 53, fire); display.display();
display.setCursor(31, 53);
display.print("PRESS FIRE!");
display.display();
//arduboy.bitmap(31, 53, fire); arduboy.display();
arduboy.setCursor(31, 53);
arduboy.print("PRESS FIRE!");
arduboy.display();
if (pollFireButton(50))
{
return true;
}
//Removes "Press FIRE"
display.clearDisplay();
display.setCursor(16,22);
display.setTextSize(2);
display.print("ARAKNOID");
display.setTextSize(1);
display.display();
arduboy.clear();
arduboy.setCursor(16,22);
arduboy.setTextSize(2);
arduboy.print("ARAKNOID");
arduboy.setTextSize(1);
arduboy.display();
display.display();
arduboy.display();
if (pollFireButton(25))
{
return true;
@ -496,7 +477,7 @@ void enterInitials()
{
char index = 0;
display.clearDisplay();
arduboy.clear();
initials[0] = ' ';
initials[1] = ' ';
@ -504,29 +485,29 @@ void enterInitials()
while (true)
{
display.display();
display.clearDisplay();
arduboy.display();
arduboy.clear();
display.setCursor(16,0);
display.print("HIGH SCORE");
arduboy.setCursor(16,0);
arduboy.print("HIGH SCORE");
sprintf(text, "%u", score);
display.setCursor(88, 0);
display.print(text);
display.setCursor(56, 20);
display.print(initials[0]);
display.setCursor(64, 20);
display.print(initials[1]);
display.setCursor(72, 20);
display.print(initials[2]);
arduboy.setCursor(88, 0);
arduboy.print(text);
arduboy.setCursor(56, 20);
arduboy.print(initials[0]);
arduboy.setCursor(64, 20);
arduboy.print(initials[1]);
arduboy.setCursor(72, 20);
arduboy.print(initials[2]);
for(byte i = 0; i < 3; i++)
{
display.drawLine(56 + (i*8), 27, 56 + (i*8) + 6, 27, 1);
arduboy.drawLine(56 + (i*8), 27, 56 + (i*8) + 6, 27, 1);
}
display.drawLine(56, 28, 88, 28, 0);
display.drawLine(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1);
arduboy.drawLine(56, 28, 88, 28, 0);
arduboy.drawLine(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1);
delay(150);
if (!digitalRead(5))
if (arduboy.pressed(LEFT_BUTTON) || arduboy.pressed(B_BUTTON))
{
index--;
if (index < 0)
@ -534,25 +515,25 @@ void enterInitials()
index = 0;
} else
{
tone(A2, 1046, 250);
arduboy.tunes.tone(1046, 250);
}
}
if (!digitalRead(9))
if (arduboy.pressed(RIGHT_BUTTON))
{
index++;
if (index > 2)
{
index = 2;
} else {
tone(A2, 1046, 250);
arduboy.tunes.tone(1046, 250);
}
}
if (!digitalRead(8))
if (arduboy.pressed(DOWN_BUTTON))
{
initials[index]++;
tone(A2, 523, 250);
arduboy.tunes.tone(523, 250);
// A-Z 0-9 :-? !-/ ' '
if (initials[index] == '0')
{
@ -572,10 +553,10 @@ void enterInitials()
}
}
if (!digitalRead(10))
if (arduboy.pressed(UP_BUTTON))
{
initials[index]--;
tone(A2, 523, 250);
arduboy.tunes.tone(523, 250);
if (initials[index] == ' ') {
initials[index] = '?';
}
@ -590,14 +571,14 @@ void enterInitials()
}
}
if (!digitalRead(A0))
if (arduboy.pressed(A_BUTTON))
{
if (index < 2)
{
index++;
tone(A2, 1046, 250);
arduboy.tunes.tone(1046, 250);
} else {
tone(A2, 1046, 250);
arduboy.tunes.tone(1046, 250);
return;
}
}
@ -675,13 +656,24 @@ void enterHighScore(byte file)
}
void setup()
{
arduboy.begin();
arduboy.setFrameRate(60);
arduboy.print("Hello World!");
arduboy.display();
intro();
}
void loop()
{
display.display();
// pause render until it's time for the next frame
if (!(arduboy.nextFrame()))
return;
//Title screen loop switches from title screen
//and high scores utill FIRE is pressed
//and high scores until FIRE is pressed
while (!start)
{
start = titleScreen();
@ -691,12 +683,12 @@ void loop()
}
}
//Inital level draw
//Initial level draw
if (!initialDraw)
{
//Clears the screen
display.display();
display.clearDisplay();
arduboy.display();
arduboy.clear();
//Selects Font
//Draws the new level
newLevel();
@ -708,7 +700,7 @@ void loop()
drawPaddle();
//Pause game if FIRE pressed
pad = !digitalRead(A0);
pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
if(pad >1 && oldpad==0 && released)
{
@ -719,7 +711,7 @@ void loop()
oldpad=pad;
drawBall();
if(brickCount==60)
if(brickCount == ROWS * COLUMNS)
{
level++;
newLevel();
@ -733,13 +725,15 @@ void loop()
enterHighScore(2);
}
display.clearDisplay();
arduboy.clear();
initialDraw=false;
start=false;
lives=3;
score=0;
newLevel();
}
arduboy.display();
}

View File

@ -0,0 +1,7 @@
# ArduBreakout
Brick breaking game in the vein of Atari's *Breakout*.
Control the paddle with the directional keys to keep a ball bouncing against a brick wall until all of the bricks are broken.
High scores are saved to EEPROM and can be saved through Arduboy restarts.

View File

@ -1,5 +1,5 @@
#ifndef ASTEROID_BITMAPS_H
#define ASTEROID_BITMAPS_H
#ifndef BREAKOUT_BITMAPS_H
#define BREAKOUT_BITMAPS_H
#include <avr/pgmspace.h>
@ -8,6 +8,3 @@ extern const unsigned char title[];
extern const unsigned char arrow[];
#endif

View File

@ -0,0 +1,106 @@
/*
Buttons example
June 11, 2015
Copyright (C) 2015 David Martinez
All rights reserved.
This code is the most basic barebones code for showing how to use buttons in
Arduboy.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
*/
#include "Arduboy.h"
// Make an instance of arduboy used for many functions
Arduboy arduboy;
// Variables for your game go here.
char text[] = "Press Buttons!";
byte x;
byte y;
// Width of each charcter including inter-character space
#define CHAR_WIDTH 6
// Height of each charater
#define CHAR_HEIGHT 8
// To get the number of characters, we subtract 1 from the length of
// the array because there will be a NULL terminator at the end.
#define NUM_CHARS (sizeof(text) - 1)
// This is the highest value that x can be without the end of the text
// going farther than the right side of the screen. We add one because
// there will be a 1 pixel space at the end of the last character.
// WIDTH and HEIGHT are defined in the Arduboy library.
#define X_MAX (WIDTH - (NUM_CHARS * CHAR_WIDTH) + 1)
// This is the highest value that y can be without the text going below
// the bottom of the screen.
#define Y_MAX (HEIGHT - CHAR_HEIGHT)
// This function runs once in your game.
// use it for anything that needs to be set only once in your game.
void setup() {
//initiate arduboy instance
arduboy.begin();
// here we set the framerate to 30, we do not need to run at default 60 and
// it saves us battery life.
arduboy.setFrameRate(30);
// set x and y to the middle of the screen
x = (WIDTH / 2) - (NUM_CHARS * CHAR_WIDTH / 2);
y = (HEIGHT / 2) - (CHAR_HEIGHT / 2);
}
// our main game loop, this runs once every cycle/frame.
// this is where our game logic goes.
void loop() {
// pause render until it's time for the next frame
if (!(arduboy.nextFrame()))
return;
// the next couple of lines will deal with checking if the D-pad buttons
// are pressed and move our text accordingly.
// We check to make sure that x and y stay within a range that keeps the
// text on the screen.
// if the right button is pressed move 1 pixel to the right every frame
if(arduboy.pressed(RIGHT_BUTTON) && (x < X_MAX)) {
x++;
}
// if the left button is pressed move 1 pixel to the left every frame
if(arduboy.pressed(LEFT_BUTTON) && (x > 0)) {
x--;
}
// if the up button or B button is pressed move 1 pixel up every frame
if((arduboy.pressed(UP_BUTTON) || arduboy.pressed(B_BUTTON)) && (y > 0)) {
y--;
}
// if the down button or A button is pressed move 1 pixel down every frame
if((arduboy.pressed(DOWN_BUTTON) || arduboy.pressed(A_BUTTON)) && (y < Y_MAX)) {
y++;
}
// we clear our screen to black
arduboy.clear();
// we set our cursor x pixels to the right and y down from the top
arduboy.setCursor(x, y);
// then we print to screen what is stored in our text variable we declared earlier
arduboy.print(text);
// then we finaly we tell the arduboy to display what we just wrote to the display.
arduboy.display();
}

View File

@ -0,0 +1,51 @@
/*
Hello, World! example
June 11, 2015
Copyright (C) 2015 David Martinez
All rights reserved.
This code is the most basic barebones code for writing a program for Arduboy.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
*/
#include "Arduboy.h"
// make an instance of arduboy used for many functions
Arduboy arduboy;
// This function runs once in your game.
// use it for anything that needs to be set only once in your game.
void setup() {
// initiate arduboy instance
arduboy.begin();
// here we set the framerate to 15, we do not need to run at
// default 60 and it saves us battery life
arduboy.setFrameRate(15);
}
// our main game loop, this runs once every cycle/frame.
// this is where our game logic goes.
void loop() {
// pause render until it's time for the next frame
if (!(arduboy.nextFrame()))
return;
// first we clear our screen to black
arduboy.clear();
// we set our cursor 5 pixels to the right and 10 down from the top
// (positions start at 0, 0)
arduboy.setCursor(4, 9);
// then we print to screen what is in the Quotation marks ""
arduboy.print(F("Hello, world!"));
// then we finaly we tell the arduboy to display what we just wrote to the display
arduboy.display();
}

View File

@ -0,0 +1,17 @@
### Benchmark run as of June 2015
- drawPixel
- drawTriangle
- fillTriangle
- drawRect
- fillRect
- drawCircle
- fillCircle
- drawRoundRect
- fillRoundRect
- drawLine
- drawFastHLine
- drawFastVLine
- drawBitmap
- drawSlowBitmap
- paint

4
examples/Tunes/README.md Normal file
View File

@ -0,0 +1,4 @@
# Tunes
Play a musical composition using the Arduboy.
A small composition is stored by `byte PROGMEM score`. The score is played in the sketch loop using `playScore(score)`.

201
examples/Tunes/Tunes.ino Normal file
View File

@ -0,0 +1,201 @@
#include "Arduboy.h"
const byte PROGMEM score [] = {
// Sinfonia No.12 in A major BWV.798 J.S.Bach
// Conductor Track
7,208, 0x90,0x45, 0x91,0x39, 1,77, 0x80, 0x81, 0x90,0x44, 0,166, 0x80, 0x90,0x45, 0,166, 0x80, 0x90,0x47,
0x91,0x38, 1,77, 0x80, 0x81, 0x90,0x45, 0,166, 0x80, 0x90,0x44, 0,166, 0x80, 0x90,0x45, 0x91,0x36, 1,77,
0x81, 1,77, 0x91,0x31, 0,166, 0x80, 0x90,0x47, 0,166, 0x80, 0x81, 0x90,0x44, 0,166, 0x80, 0x90,0x45,
0,166, 0x80, 0x90,0x47, 0x91,0x32, 0,166, 0x80, 0x90,0x40, 0,166, 0x80, 0x81, 0x90,0x49, 0,166, 0x80,
0x90,0x40, 0,166, 0x80, 0x90,0x4A, 0x91,0x34, 0,166, 0x80, 0x90,0x40, 0,166, 0x80, 0x81, 0x90,0x4C, 1,77,
0x80, 0x90,0x49, 0x91,0x2D, 0,166, 0x80, 0x90,0x47, 0,166, 0x80, 0x81, 0x90,0x45, 0x91,0x39, 0,166, 0x80,
0x90,0x47, 0,166, 0x80, 0x81, 0x90,0x49, 0x91,0x38, 0,166, 0x80, 0x90,0x4B, 0,166, 0x80, 0x81, 0x90,0x4C,
0x91,0x36, 0,166, 0x80, 0x90,0x4E, 0,166, 0x80, 0x81, 0x90,0x50, 0x91,0x34, 0x92,0x40, 0,166, 0x80, 0x90,0x51,
0,166, 0x80, 0x81, 0x82, 0x90,0x53, 0x91,0x3F, 0,166, 0x81, 0x91,0x40, 0,166, 0x81, 0x91,0x3F, 0x92,0x42,
0,166, 0x80, 0x90,0x51, 0,166, 0x80, 0x81, 0x82, 0x90,0x50, 0x91,0x40, 0,166, 0x80, 0x81, 0x90,0x4E, 0x91,0x3F,
0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x3D, 0x92,0x40, 0,166, 0x80, 0x90,0x4B, 0,166, 0x80, 0x81, 0x90,0x49,
0,166, 0x80, 0x90,0x47, 0,166, 0x80, 0x90,0x4C, 0x91,0x38, 0,166, 0x82, 0x92,0x42, 0,166, 0x81, 0x82,
0x91,0x3F, 0,166, 0x81, 0x91,0x40, 0,166, 0x81, 0x91,0x42, 0x92,0x39, 0,166, 0x81, 0x91,0x3B, 0,166,
0x81, 0x82, 0x91,0x44, 0,166, 0x81, 0x91,0x3B, 0,166, 0x80, 0x81, 0x90,0x45, 0x91,0x4B, 0x92,0x3B, 0,166,
0x80, 0x82, 0x90,0x3B, 0,166, 0x80, 0x90,0x47, 1,77, 0x80, 0x81, 0x90,0x4C, 0x91,0x44, 0x92,0x40, 0,166,
0x81, 0x91,0x45, 0,166, 0x80, 0x81, 0x82, 0x90,0x4A, 0x91,0x47, 0,166, 0x80, 0x81, 0x90,0x49, 0x91,0x45, 0,166,
0x80, 0x81, 0x90,0x4A, 0x91,0x44, 0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x42, 0,166, 0x80, 0x81, 0x90,0x4E, 0x91,0x40,
0,166, 0x80, 0x81, 0x90,0x50, 0x91,0x3E, 0,166, 0x80, 0x81, 0x90,0x51, 0x91,0x3D, 0x92,0x39, 0,166, 0x81,
0x91,0x49, 0,166, 0x81, 0x82, 0x91,0x4E, 0x92,0x38, 0,166, 0x80, 0x82, 0x90,0x51, 0x92,0x39, 0,166, 0x80,
0x82, 0x90,0x50, 0x92,0x3B, 0,166, 0x81, 0x91,0x4B, 0,166, 0x81, 0x82, 0x91,0x4C, 0x92,0x39, 0,166, 0x80,
0x82, 0x90,0x50, 0x92,0x38, 0,166, 0x80, 0x82, 0x90,0x4E, 0x92,0x39, 0,166, 0x81, 0x91,0x49, 0,166, 0x81,
0x91,0x4A, 0,166, 0x80, 0x90,0x4E, 0,166, 0x80, 0x90,0x4C, 0,166, 0x81, 0x82, 0x91,0x47, 0x92,0x3B, 0,166,
0x81, 0x82, 0x91,0x49, 0x92,0x38, 0,166, 0x80, 0x82, 0x90,0x4C, 0x92,0x39, 0,166, 0x80, 0x81, 0x82, 0x90,0x3B,
0x91,0x4A, 0x92,0x44, 0,166, 0x80, 0x90,0x34, 0,166, 0x80, 0x81, 0x82, 0x90,0x3D, 0x91,0x49, 0x92,0x45, 0,166,
0x80, 0x90,0x34, 0,166, 0x80, 0x81, 0x90,0x3E, 0x91,0x47, 0,166, 0x80, 0x90,0x34, 0,166, 0x80, 0x82, 0x90,0x44,
0x92,0x40, 1,77, 0x80, 0x81, 0x82, 0x90,0x3D, 0x91,0x45, 0,166, 0x80, 0x90,0x3B, 0,166, 0x80, 0x90,0x39,
0x92,0x49, 0,166, 0x80, 0x90,0x3B, 0,166, 0x80, 0x82, 0x90,0x3D, 0x92,0x4E, 0,166, 0x80, 0x90,0x3F, 0,166,
0x80, 0x81, 0x90,0x45, 0x91,0x40, 0,166, 0x81, 0x91,0x42, 0,166, 0x80, 0x81, 0x90,0x44, 0x91,0x3B, 0,166,
0x81, 0x91,0x39, 0,166, 0x81, 0x82, 0x91,0x47, 0x92,0x38, 0,166, 0x82, 0x92,0x39, 0,166, 0x81, 0x82, 0x91,0x4C,
0x92,0x3B, 0,166, 0x82, 0x92,0x3D, 0,166, 0x80, 0x82, 0x90,0x44, 0x92,0x3F, 0,166, 0x82, 0x92,0x40, 0,166,
0x80, 0x82, 0x90,0x42, 0x92,0x39, 0,166, 0x82, 0x92,0x38, 0,166, 0x81, 0x82, 0x91,0x4B, 0x92,0x36, 0,166,
0x81, 0x82, 0x91,0x4C, 0x92,0x38, 0,166, 0x81, 0x82, 0x91,0x4E, 0x92,0x39, 0,166, 0x82, 0x92,0x38, 0,166,
0x81, 0x82, 0x91,0x4C, 0x92,0x39, 0,166, 0x81, 0x82, 0x91,0x4B, 0x92,0x3B, 0,166, 0x80, 0x81, 0x82, 0x90,0x4C,
0x91,0x38, 0,166, 0x92,0x4B, 0,166, 0x81, 0x82, 0x91,0x49, 0x92,0x39, 0,166, 0x81, 0x82, 0x91,0x47, 0x92,0x3B,
0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x3D, 0,166, 0x80, 0x81, 0x90,0x4E, 0x91,0x44, 0,166, 0x80, 0x81, 0x90,0x4B,
0x91,0x42, 0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x40, 0,166, 0x80, 0x81, 0x90,0x4E, 0x91,0x3F, 0,166, 0x80,
0x82, 0x90,0x47, 0x92,0x3B, 0,166, 0x80, 0x81, 0x82, 0x90,0x50, 0x91,0x39, 0x92,0x40, 0,166, 0x80, 0x81, 0x90,0x47,
0x91,0x38, 0,166, 0x80, 0x81, 0x90,0x51, 0x91,0x36, 0,166, 0x80, 0x90,0x47, 0,166, 0x80, 0x81, 0x82, 0x90,0x53,
0x91,0x3F, 0x92,0x3B, 1,77, 0x80, 0x81, 0x82, 0x90,0x50, 0x91,0x34, 0x92,0x40, 0,166, 0x80, 0x90,0x51, 0,166,
0x80, 0x90,0x53, 0,166, 0x80, 0x90,0x51, 0,166, 0x80, 0x81, 0x82, 0x90,0x50, 0,166, 0x80, 0x90,0x4E, 0x91,0x39,
0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x38, 0,166, 0x80, 0x81, 0x90,0x4A, 0x91,0x36, 0,166, 0x80, 0x81, 0x90,0x49,
0x91,0x35, 0,166, 0x81, 0x91,0x31, 0,166, 0x81, 0x91,0x44, 0x92,0x36, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x31,
0,166, 0x81, 0x82, 0x91,0x47, 0x92,0x38, 0,166, 0x82, 0x92,0x31, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x35,
0,166, 0x81, 0x82, 0x91,0x44, 0x92,0x31, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x36, 0,166, 0x82, 0x92,0x31,
0,166, 0x80, 0x82, 0x90,0x38, 0x92,0x49, 0,166, 0x80, 0x82, 0x90,0x31, 0x92,0x4A, 0,166, 0x80, 0x82, 0x90,0x36,
0x92,0x4C, 0,166, 0x80, 0x90,0x31, 0,166, 0x80, 0x82, 0x90,0x34, 0x92,0x4A, 0,166, 0x80, 0x82, 0x90,0x31,
0x92,0x49, 0,166, 0x80, 0x82, 0x90,0x33, 0x92,0x47, 0,166, 0x80, 0x90,0x2F, 0,166, 0x80, 0x81, 0x90,0x34,
0x91,0x42, 0,166, 0x80, 0x81, 0x90,0x2F, 0x91,0x44, 0,166, 0x80, 0x81, 0x90,0x36, 0x91,0x45, 0,166, 0x80,
0x90,0x2F, 0,166, 0x80, 0x81, 0x90,0x33, 0x91,0x44, 0,166, 0x80, 0x81, 0x90,0x2F, 0x91,0x42, 0,166, 0x80,
0x81, 0x90,0x44, 0x91,0x34, 0,166, 0x81, 0x91,0x2F, 0,166, 0x81, 0x82, 0x91,0x47, 0x92,0x36, 0,166, 0x81,
0x82, 0x91,0x49, 0x92,0x2F, 0,166, 0x81, 0x82, 0x91,0x4A, 0x92,0x34, 0,166, 0x82, 0x92,0x2F, 0,166, 0x81,
0x82, 0x91,0x49, 0x92,0x32, 0,166, 0x81, 0x82, 0x91,0x47, 0x92,0x2F, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x31,
0,166, 0x82, 0x92,0x2D, 0,166, 0x80, 0x82, 0x90,0x32, 0x92,0x41, 0,166, 0x80, 0x82, 0x90,0x42, 0x92,0x2D,
0,166, 0x80, 0x82, 0x90,0x44, 0x92,0x34, 0,166, 0x82, 0x92,0x2D, 0,166, 0x80, 0x82, 0x90,0x42, 0x92,0x31,
0,166, 0x80, 0x82, 0x90,0x2D, 0x92,0x41, 0,166, 0x80, 0x82, 0x90,0x42, 0x92,0x32, 0,166, 0x82, 0x92,0x2D,
0,166, 0x81, 0x82, 0x91,0x34, 0x92,0x45, 0,166, 0x81, 0x82, 0x91,0x2D, 0x92,0x47, 0,166, 0x81, 0x82, 0x91,0x32,
0x92,0x49, 0,166, 0x81, 0x91,0x2D, 0,166, 0x81, 0x82, 0x91,0x31, 0x92,0x47, 0,166, 0x81, 0x82, 0x91,0x2D,
0x92,0x45, 0,166, 0x81, 0x82, 0x91,0x30, 0x92,0x44, 0,166, 0x81, 0x91,0x2C, 0,166, 0x80, 0x81, 0x90,0x31,
0x91,0x3F, 0,166, 0x80, 0x81, 0x90,0x2C, 0x91,0x41, 0,166, 0x80, 0x81, 0x90,0x33, 0x91,0x42, 0,166, 0x80,
0x90,0x2C, 0,166, 0x80, 0x81, 0x90,0x30, 0x91,0x41, 0,166, 0x80, 0x81, 0x90,0x2C, 0x91,0x3F, 0,166, 0x80,
0x81, 0x90,0x31, 0x91,0x41, 1,77, 0x81, 0x82, 0x91,0x44, 0x92,0x41, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x42,
0,166, 0x81, 0x82, 0x91,0x47, 0x92,0x44, 1,77, 0x81, 0x82, 0x91,0x45, 0x92,0x42, 0,166, 0x81, 0x82, 0x91,0x44,
0x92,0x41, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x42, 1,77, 0x81, 0x91,0x46, 0,166, 0x81, 0x91,0x48, 0,166,
0x81, 0x82, 0x91,0x49, 0x92,0x40, 1,77, 0x81, 0x91,0x48, 0,166, 0x81, 0x91,0x46, 0,166, 0x81, 0x82, 0x91,0x3F,
0x92,0x48, 1,77, 0x82, 0x92,0x48, 0,166, 0x82, 0x92,0x49, 0,166, 0x81, 0x82, 0x91,0x4B, 0x92,0x42, 1,77,
0x81, 0x91,0x49, 0,166, 0x81, 0x91,0x48, 0,166, 0x80, 0x81, 0x90,0x49, 0,166, 0x91,0x3E, 0,166, 0x80,
0x81, 0x90,0x44, 0x91,0x3D, 0,166, 0x80, 0x81, 0x90,0x45, 0x91,0x3B, 0,166, 0x80, 0x81, 0x82, 0x90,0x47, 0x91,0x3D,
0x92,0x41, 0,166, 0x81, 0x91,0x39, 0,166, 0x80, 0x81, 0x90,0x45, 0x91,0x3B, 0,166, 0x80, 0x81, 0x90,0x44,
0x91,0x3D, 0,166, 0x80, 0x81, 0x82, 0x90,0x45, 0x91,0x42, 0x92,0x36, 0,166, 0x80, 0x81, 0x90,0x42, 0,166,
0x80, 0x82, 0x90,0x44, 0x91,0x35, 0,166, 0x80, 0x81, 0x90,0x45, 0x91,0x36, 0,166, 0x80, 0x81, 0x90,0x47, 0x91,0x38,
0,166, 0x80, 0x90,0x49, 0,166, 0x80, 0x81, 0x90,0x4A, 0x91,0x36, 0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x35,
0,166, 0x80, 0x81, 0x90,0x4A, 0x91,0x36, 0,166, 0x92,0x49, 0,166, 0x82, 0x92,0x47, 0,166, 0x80, 0x90,0x4A,
0,166, 0x80, 0x90,0x49, 0,166, 0x81, 0x82, 0x91,0x47, 0x92,0x38, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x35,
0,166, 0x80, 0x82, 0x90,0x49, 0x92,0x36, 0,166, 0x80, 0x81, 0x82, 0x90,0x38, 0x91,0x47, 0x92,0x41, 0,166,
0x80, 0x90,0x31, 0,166, 0x80, 0x81, 0x82, 0x90,0x39, 0x91,0x42, 0x92,0x45, 0,166, 0x80, 0x90,0x31, 0,166,
0x80, 0x82, 0x90,0x3B, 0x92,0x44, 0,166, 0x80, 0x90,0x31, 0,166, 0x80, 0x81, 0x90,0x3D, 0x91,0x41, 0,166,
0x80, 0x90,0x3B, 0,166, 0x80, 0x81, 0x82, 0x90,0x39, 0x91,0x42, 0,166, 0x80, 0x90,0x3B, 0,166, 0x80, 0x90,0x3D,
0x92,0x49, 0,166, 0x80, 0x90,0x3B, 0,166, 0x80, 0x82, 0x90,0x39, 0x92,0x4E, 0,166, 0x80, 0x90,0x38, 0,166,
0x80, 0x81, 0x90,0x36, 0x91,0x45, 0,166, 0x80, 0x90,0x34, 0,166, 0x80, 0x81, 0x90,0x4A, 0x91,0x3B, 0,166,
0x81, 0x91,0x3D, 0,166, 0x81, 0x82, 0x91,0x44, 0x92,0x3E, 0,166, 0x82, 0x92,0x3D, 0,166, 0x81, 0x82, 0x91,0x50,
0x92,0x3B, 0,166, 0x82, 0x92,0x39, 0,166, 0x80, 0x82, 0x90,0x44, 0x92,0x38, 0,166, 0x82, 0x92,0x36, 0,166,
0x80, 0x82, 0x90,0x4C, 0x92,0x3D, 0,166, 0x82, 0x92,0x3E, 0,166, 0x81, 0x82, 0x91,0x49, 0x92,0x40, 0,166,
0x82, 0x92,0x3E, 0,166, 0x81, 0x82, 0x91,0x51, 0x92,0x3D, 0,166, 0x82, 0x92,0x3B, 0,166, 0x80, 0x82, 0x90,0x49,
0x92,0x39, 0,166, 0x82, 0x92,0x38, 0,166, 0x80, 0x82, 0x90,0x3F, 0x92,0x4E, 0,166, 0x80, 0x90,0x40, 0,166,
0x80, 0x81, 0x90,0x42, 0x91,0x4B, 0,166, 0x80, 0x90,0x40, 0,166, 0x80, 0x81, 0x90,0x3F, 0x91,0x53, 0,166,
0x80, 0x90,0x3D, 0,166, 0x80, 0x81, 0x90,0x3B, 0x91,0x51, 0,166, 0x80, 0x90,0x39, 0,166, 0x80, 0x81, 0x90,0x50,
0x91,0x40, 1,77, 0x80, 0x82, 0x90,0x50, 0x92,0x47, 0,166, 0x80, 0x82, 0x90,0x51, 0x92,0x49, 0,166, 0x80,
0x82, 0x90,0x53, 0x92,0x4A, 1,77, 0x80, 0x82, 0x90,0x51, 0x92,0x49, 0,166, 0x80, 0x82, 0x90,0x50, 0x92,0x47,
0,166, 0x80, 0x82, 0x90,0x51, 0x92,0x49, 1,77, 0x82, 0x92,0x49, 0,166, 0x82, 0x92,0x4B, 0,166, 0x80,
0x82, 0x90,0x50, 0x92,0x4C, 1,77, 0x82, 0x92,0x4B, 0,166, 0x82, 0x92,0x49, 0,166, 0x80, 0x82, 0x90,0x4E,
0x92,0x4B, 1,77, 0x82, 0x92,0x4B, 0,166, 0x82, 0x92,0x4C, 0,166, 0x80, 0x82, 0x90,0x51, 0x92,0x4E, 1,77,
0x82, 0x92,0x4C, 0,166, 0x82, 0x92,0x4B, 0,166, 0x82, 0x92,0x4C, 0,166, 0x81, 0x91,0x3D, 0,166, 0x80,
0x81, 0x90,0x50, 0x91,0x3B, 0,166, 0x80, 0x81, 0x90,0x51, 0x91,0x39, 0,166, 0x80, 0x81, 0x90,0x53, 0x91,0x38,
0,166, 0x80, 0x81, 0x90,0x51, 0x91,0x36, 0,166, 0x80, 0x81, 0x90,0x50, 0x91,0x34, 0,166, 0x80, 0x81, 0x90,0x4E,
0x91,0x32, 0,166, 0x80, 0x81, 0x82, 0x90,0x4C, 0x91,0x31, 0,166, 0x80, 0x81, 0x90,0x4A, 0x91,0x39, 0,166,
0x80, 0x81, 0x90,0x49, 0x91,0x32, 0,166, 0x80, 0x81, 0x90,0x47, 0x91,0x39, 0,166, 0x80, 0x81, 0x90,0x45, 0x91,0x34,
0,166, 0x80, 0x81, 0x90,0x49, 0x91,0x39, 0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x31, 0,166, 0x80, 0x81, 0x90,0x4F,
0x91,0x39, 0,166, 0x80, 0x81, 0x90,0x32, 0x91,0x4E, 0,166, 0x80, 0x90,0x39, 0,166, 0x80, 0x90,0x34, 0x92,0x45,
0,166, 0x80, 0x82, 0x90,0x39, 0x92,0x47, 0,166, 0x80, 0x82, 0x90,0x32, 0x92,0x49, 0,166, 0x80, 0x90,0x39,
0,166, 0x80, 0x82, 0x90,0x31, 0x92,0x47, 0,166, 0x80, 0x82, 0x90,0x39, 0x92,0x45, 0,166, 0x80, 0x82, 0x90,0x2F,
0x92,0x44, 0,166, 0x80, 0x90,0x38, 0,166, 0x80, 0x81, 0x90,0x31, 0x91,0x4A, 0,166, 0x80, 0x81, 0x90,0x38,
0x91,0x4C, 0,166, 0x80, 0x81, 0x90,0x32, 0x91,0x4E, 0,166, 0x80, 0x90,0x38, 0,166, 0x80, 0x81, 0x90,0x2F,
0x91,0x4C, 0,166, 0x80, 0x81, 0x90,0x38, 0x91,0x4A, 0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x31, 0,166, 0x81,
0x91,0x38, 0,166, 0x81, 0x82, 0x91,0x44, 0x92,0x32, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x38, 0,166, 0x81,
0x82, 0x91,0x47, 0x92,0x31, 0,166, 0x82, 0x92,0x38, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x2F, 0,166, 0x81,
0x82, 0x91,0x44, 0x92,0x38, 0,166, 0x81, 0x82, 0x91,0x42, 0x92,0x2E, 0,166, 0x82, 0x92,0x36, 0,166, 0x80,
0x82, 0x90,0x49, 0x92,0x2F, 0,166, 0x80, 0x82, 0x90,0x4A, 0x92,0x36, 0,166, 0x80, 0x82, 0x90,0x4C, 0x92,0x31,
0,166, 0x82, 0x92,0x36, 0,166, 0x80, 0x82, 0x90,0x4A, 0x92,0x2E, 0,166, 0x80, 0x82, 0x90,0x49, 0x92,0x36,
0,166, 0x80, 0x82, 0x90,0x4A, 0x92,0x2F, 0,166, 0x82, 0x92,0x36, 0,166, 0x81, 0x82, 0x91,0x42, 0x92,0x31,
0,166, 0x81, 0x82, 0x91,0x44, 0x92,0x36, 0,166, 0x81, 0x82, 0x91,0x45, 0x92,0x2F, 0,166, 0x82, 0x92,0x36,
0,166, 0x81, 0x82, 0x91,0x44, 0x92,0x2D, 0,166, 0x81, 0x82, 0x91,0x42, 0x92,0x36, 0,166, 0x81, 0x82, 0x91,0x2C,
0x92,0x40, 0,166, 0x81, 0x91,0x34, 0,166, 0x80, 0x81, 0x90,0x47, 0x91,0x28, 0,166, 0x80, 0x81, 0x90,0x49,
0x91,0x34, 0,166, 0x80, 0x81, 0x90,0x4A, 0x91,0x2A, 0,166, 0x81, 0x91,0x34, 0,166, 0x80, 0x81, 0x90,0x49,
0x91,0x2C, 0,166, 0x80, 0x81, 0x90,0x47, 0x91,0x34, 0,166, 0x80, 0x81, 0x90,0x49, 0x91,0x2D, 0,166, 0x80,
0x90,0x45, 0,166, 0x80, 0x81, 0x82, 0x90,0x4E, 0x91,0x2C, 0,166, 0x81, 0x91,0x45, 0x92,0x2D, 0,166, 0x81,
0x82, 0x91,0x44, 0x92,0x2F, 0,166, 0x80, 0x90,0x4B, 0,166, 0x80, 0x82, 0x90,0x4C, 0x92,0x2D, 0,166, 0x81,
0x82, 0x91,0x44, 0x92,0x2C, 0,166, 0x81, 0x82, 0x91,0x42, 0x92,0x2D, 0,166, 0x80, 0x90,0x49, 0,166, 0x80,
0x90,0x4A, 0,166, 0x81, 0x91,0x42, 0,166, 0x81, 0x91,0x40, 0,166, 0x80, 0x82, 0x90,0x47, 0x92,0x2F, 0,166,
0x80, 0x82, 0x90,0x49, 0x92,0x2C, 0,166, 0x81, 0x82, 0x91,0x2D, 0x92,0x40, 0,166, 0x80, 0x81, 0x82, 0x90,0x2F,
0x91,0x44, 0x92,0x3E, 0,166, 0x80, 0x90,0x28, 0,166, 0x80, 0x81, 0x82, 0x90,0x31, 0x91,0x3D, 0x92,0x45, 0,166,
0x80, 0x90,0x28, 0,166, 0x80, 0x81, 0x90,0x32, 0x91,0x3B, 0,166, 0x80, 0x90,0x28, 0,166, 0x80, 0x82, 0x90,0x34,
0x92,0x44, 1,77, 0x80, 0x81, 0x82, 0x90,0x31, 0x91,0x45, 0x92,0x40, 0,166, 0x82, 0x92,0x3D, 0,166, 0x80,
0x81, 0x82, 0x90,0x36, 0x91,0x44, 0x92,0x3E, 0,166, 0x81, 0x91,0x45, 0,166, 0x81, 0x91,0x47, 0,166, 0x82,
0x92,0x3B, 0,166, 0x80, 0x81, 0x82, 0x90,0x35, 0x91,0x45, 0x92,0x3D, 0,166, 0x81, 0x91,0x44, 0,166, 0x80,
0x81, 0x90,0x45, 0x91,0x36, 0,166, 0x82, 0x92,0x39, 0,166, 0x82, 0x92,0x3B, 0,166, 0x82, 0x92,0x38, 0,166,
0x82, 0x92,0x39, 0,166, 0x80, 0x90,0x47, 0,166, 0x80, 0x81, 0x90,0x44, 0x91,0x34, 0,166, 0x80, 0x90,0x45,
0,166, 0x80, 0x81, 0x90,0x47, 0x91,0x32, 0,166, 0x80, 0x82, 0x90,0x38, 0x92,0x40, 0,166, 0x80, 0x81, 0x82,
0x90,0x49, 0x91,0x31, 0x92,0x39, 0,166, 0x80, 0x90,0x40, 0,166, 0x80, 0x81, 0x90,0x4A, 0x91,0x2F, 0,166,
0x80, 0x90,0x40, 0,166, 0x80, 0x81, 0x82, 0x90,0x4C, 0x91,0x34, 0x92,0x38, 1,77, 0x80, 0x81, 0x82, 0x90,0x49,
0x91,0x39, 0x92,0x2D, 0,166, 0x80, 0x90,0x47, 0,166, 0x80, 0x81, 0x90,0x45, 0x91,0x3D, 0,166, 0x80, 0x90,0x47,
0,166, 0x80, 0x81, 0x82, 0x90,0x49, 0x91,0x42, 0,166, 0x80, 0x90,0x4B, 0,166, 0x80, 0x90,0x4C, 0x92,0x39,
0,166, 0x80, 0x90,0x4E, 0,166, 0x80, 0x82, 0x90,0x47, 0x92,0x38, 0,166, 0x80, 0x90,0x45, 0,166, 0x80,
0x81, 0x90,0x44, 0x91,0x3B, 0,166, 0x80, 0x90,0x45, 0,166, 0x80, 0x81, 0x90,0x47, 0x91,0x40, 0,166, 0x80,
0x90,0x49, 0,166, 0x80, 0x82, 0x90,0x4A, 0x92,0x38, 0,166, 0x80, 0x90,0x4C, 0,166, 0x80, 0x82, 0x90,0x45,
0x92,0x36, 0,166, 0x80, 0x90,0x44, 0,166, 0x80, 0x81, 0x90,0x42, 0x91,0x39, 0,166, 0x80, 0x90,0x44, 0,166,
0x80, 0x81, 0x90,0x45, 0x91,0x3E, 0,166, 0x80, 0x90,0x47, 0,166, 0x80, 0x82, 0x90,0x49, 0x92,0x36, 0,166,
0x80, 0x90,0x4A, 0,166, 0x80, 0x82, 0x90,0x44, 0x92,0x34, 0,166, 0x80, 0x90,0x47, 0,166, 0x80, 0x81, 0x90,0x44,
0x91,0x3B, 0,166, 0x80, 0x81, 0x90,0x3D, 0x91,0x40, 0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x3E, 1,77, 0x81,
0x91,0x3D, 0,166, 0x81, 0x91,0x3B, 0,166, 0x81, 0x91,0x3D, 0,166, 0x80, 0x90,0x4C, 0,166, 0x80, 0x81,
0x90,0x49, 0x91,0x39, 0,166, 0x80, 0x81, 0x90,0x45, 0x91,0x3B, 0,166, 0x80, 0x81, 0x90,0x51, 0x91,0x3D, 1,77,
0x81, 0x91,0x3B, 0,166, 0x81, 0x91,0x39, 0,166, 0x81, 0x91,0x3B, 0,166, 0x80, 0x90,0x47, 0,166, 0x80,
0x81, 0x90,0x4A, 0x91,0x38, 0,166, 0x80, 0x81, 0x90,0x4E, 0x91,0x39, 0,166, 0x80, 0x81, 0x90,0x50, 0x91,0x3B,
1,77, 0x81, 0x91,0x39, 0,166, 0x81, 0x91,0x38, 0,166, 0x81, 0x91,0x39, 0,166, 0x80, 0x90,0x45, 0,166,
0x80, 0x81, 0x90,0x49, 0x91,0x36, 0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x38, 0,166, 0x80, 0x81, 0x90,0x4E, 0x91,0x39,
1,77, 0x81, 0x91,0x38, 0,166, 0x81, 0x91,0x36, 0,166, 0x81, 0x91,0x38, 0,166, 0x80, 0x82, 0x90,0x4C,
0x92,0x34, 0,166, 0x80, 0x81, 0x82, 0x90,0x4B, 0x91,0x38, 0,166, 0x80, 0x81, 0x90,0x4C, 0x91,0x3B, 0,166,
0x80, 0x81, 0x90,0x4E, 0x91,0x45, 0x92,0x3C, 0,166, 0x82, 0x92,0x38, 0,166, 0x80, 0x81, 0x82, 0x90,0x39, 0,166,
0x80, 0x90,0x33, 0,166, 0x80, 0x90,0x49, 0x91,0x45, 0x92,0x34, 0,13, 1,29, 0,246, 0x80, 0x81, 0x90,0x4A,
0x91,0x47, 0,53, 0,141, 0x80, 0x81, 0x82, 0x90,0x47, 0x91,0x44, 0x92,0x28, 0,95, 1,77, 0,202,
0x80, 0x81, 0x90,0x45, 0,91, 0,136, 0x80, 0x82, 0x90,0x45, 0x91,0x2D, 7,83, 0x80, 0x81, 0xf0};
Arduboy arduboy;
void setup()
{
arduboy.begin();
arduboy.setTextSize(4);
arduboy.setCursor(0,0);
arduboy.print("Music\nDemo");
arduboy.display();
}
int x = 0, y = 0;
void loop ()
{
// pause render until it's time for the next frame
if (!(arduboy.nextFrame()))
return;
if (arduboy.pressed(UP_BUTTON)) {
y-=1;
} else if (arduboy.pressed(DOWN_BUTTON)) {
y+=1;
} else if (arduboy.pressed(LEFT_BUTTON)) {
x-=1;
} else if (arduboy.pressed(RIGHT_BUTTON)) {
x+=1;
}
if (arduboy.pressed(A_BUTTON)) {
arduboy.invert(true);
} else if (arduboy.pressed(B_BUTTON)) {
arduboy.invert(false);
}
arduboy.clear();
arduboy.setCursor(x,y);
arduboy.print("Music\nDemo");
arduboy.display();
// play the tune if we aren't already
if (!arduboy.tunes.playing())
arduboy.tunes.playScore(score);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

61
keywords.txt Normal file
View File

@ -0,0 +1,61 @@
#######################################
# Syntax Coloring Map For Arduboy
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Arduboy KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
allPixelsOn KEYWORD2
blank KEYWORD2
clear KEYWORD2
cpuLoad KEYWORD2
display KEYWORD2
drawBitmap KEYWORD2
drawChar KEYWORD2
drawCircle KEYWORD2
drawFastHLine KEYWORD2
drawFastVLine KEYWORD2
drawLine KEYWORD2
drawPixel KEYWORD2
drawRect KEYWORD2
drawRoundRect KEYWORD2
drawSlowXYBitmap KEYWORD2
drawTriangle KEYWORD2
everyXFrames KEYWORD2
fillCircle KEYWORD2
fillRect KEYWORD2
fillRoundRect KEYWORD2
fillScreen KEYWORD2
fillTriangle KEYWORD2
flipVertical KEYWORD2
flipHorizontal KEYWORD2
getBuffer KEYWORD2
getInput KEYWORD2
idle KEYWORD2
initRandomSeed KEYWORD2
invert KEYWORD2
nextFrame KEYWORD2
notPressed KEYWORD2
paint8Pixels KEYWORD2
paintScreen KEYWORD2
pressed KEYWORD2
setCursor KEYWORD2
setFrameRate KEYWORD2
setRGBled KEYWORD2
setTextSize KEYWORD2
setTextWrap KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
BLACK LITERAL1
WHITE LITERAL1

9
library.properties Normal file
View File

@ -0,0 +1,9 @@
name=Arduboy
version=1.0.0
author=Chris J. Martinez, Kevin Bates, Josh Goebel, Scott Allen, Ross O. Shoger
maintainer=Ross O. Shoger ros@arduboy.com
sentence=The Arduboy core library.
paragraph=This library is for content creation on the Arduboy, a portable gaming platform. The library provides access to the sound, display, and input of the Arduboy.
category=Other
url=https://github.com/arduboy/arduboy
architectures=avr

758
src/Arduboy.cpp Normal file
View File

@ -0,0 +1,758 @@
#include "Arduboy.h"
#include "glcdfont.c"
#include "ab_logo.c"
Arduboy::Arduboy()
{
// frame management
setFrameRate(60);
frameCount = 0;
nextFrameStart = 0;
post_render = false;
// init not necessary, will be reset after first use
// lastFrameStart
// lastFrameDurationMs
// font rendering
cursor_x = 0;
cursor_y = 0;
textsize = 1;
}
void Arduboy::start() // deprecated
{
begin();
}
void Arduboy::begin()
{
boot(); // required
bootUtils();
bootLogo();
// Audio
tunes.initChannel(PIN_SPEAKER_1);
tunes.initChannel(PIN_SPEAKER_2);
audio.begin();
}
// this is pusposely duplicated (without logo) so that
// whichever is actually used is linked and the one
// that is not is gone without wasting any space in flash
void Arduboy::beginNoLogo()
{
boot(); // required
bootUtils();
// Audio
tunes.initChannel(PIN_SPEAKER_1);
tunes.initChannel(PIN_SPEAKER_2);
audio.begin();
}
void Arduboy::bootUtils()
{
// flashlight
if(pressed(UP_BUTTON)) {
// sendLCDCommand(OLED_ALL_PIXELS_ON); // smaller than allPixelsOn()
blank();
setRGBled(255,255,255);
while(true) {}
}
}
void Arduboy::bootLogo()
{
// setRGBled(10,0,0);
for(int8_t y = -18; y<=24; y++) {
setRGBled(24-y, 0, 0);
clear();
drawBitmap(20,y, arduboy_logo, 88, 16, WHITE);
display();
delay(27);
// longer delay post boot, we put it inside the loop to
// save the flash calling clear/delay again outside the loop
if (y==-16) {
delay(250);
}
}
delay(750);
setRGBled(0,0,0);
}
/* Frame management */
void Arduboy::setFrameRate(uint8_t rate)
{
frameRate = rate;
eachFrameMillis = 1000/rate;
}
bool Arduboy::everyXFrames(uint8_t frames)
{
return frameCount % frames == 0;
}
bool Arduboy::nextFrame()
{
long now = millis();
uint8_t remaining;
// post render
if (post_render) {
lastFrameDurationMs = now - lastFrameStart;
frameCount++;
post_render = false;
}
// if it's not time for the next frame yet
if (now < nextFrameStart) {
remaining = nextFrameStart - now;
// if we have more than 1ms to spare, lets sleep
// we should be woken up by timer0 every 1ms, so this should be ok
if (remaining > 1)
idle();
return false;
}
// pre-render
// technically next frame should be last frame + each frame but if we're
// running a slow render we would constnatly be behind the clock
// keep an eye on this and see how it works. If it works well the
// lastFrameStart variable could be eliminated completely
nextFrameStart = now + eachFrameMillis;
lastFrameStart = now;
post_render = true;
return post_render;
}
int Arduboy::cpuLoad()
{
return lastFrameDurationMs*100 / eachFrameMillis;
}
void Arduboy::initRandomSeed()
{
power_adc_enable(); // ADC on
randomSeed(~rawADC(ADC_TEMP) * ~rawADC(ADC_VOLTAGE) * ~micros() + micros());
power_adc_disable(); // ADC off
}
uint16_t Arduboy::rawADC(byte adc_bits)
{
ADMUX = adc_bits;
// we also need MUX5 for temperature check
if (adc_bits == ADC_TEMP) {
ADCSRB = _BV(MUX5);
}
delay(2); // Wait for ADMUX setting to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
return ADC;
}
/* Graphics */
void Arduboy::clearDisplay() // deprecated
{
clear();
}
void Arduboy::clear()
{
fillScreen(BLACK);
}
void Arduboy::drawPixel(int x, int y, uint8_t color)
{
#ifdef PIXEL_SAFE_MODE
if (x < 0 || x > (WIDTH-1) || y < 0 || y > (HEIGHT-1))
{
return;
}
#endif
uint8_t row = (uint8_t)y / 8;
if (color)
{
sBuffer[(row*WIDTH) + (uint8_t)x] |= _BV((uint8_t)y % 8);
}
else
{
sBuffer[(row*WIDTH) + (uint8_t)x] &= ~ _BV((uint8_t)y % 8);
}
}
uint8_t Arduboy::getPixel(uint8_t x, uint8_t y)
{
uint8_t row = y / 8;
uint8_t bit_position = y % 8;
return (sBuffer[(row*WIDTH) + x] & _BV(bit_position)) >> bit_position;
}
void Arduboy::drawCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color)
{
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
drawPixel(x0, y0+r, color);
drawPixel(x0, y0-r, color);
drawPixel(x0+r, y0, color);
drawPixel(x0-r, y0, color);
while (x<y)
{
if (f >= 0)
{
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
drawPixel(x0 + x, y0 + y, color);
drawPixel(x0 - x, y0 + y, color);
drawPixel(x0 + x, y0 - y, color);
drawPixel(x0 - x, y0 - y, color);
drawPixel(x0 + y, y0 + x, color);
drawPixel(x0 - y, y0 + x, color);
drawPixel(x0 + y, y0 - x, color);
drawPixel(x0 - y, y0 - x, color);
}
}
void Arduboy::drawCircleHelper
(int16_t x0, int16_t y0, uint8_t r, uint8_t cornername, uint8_t color)
{
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
while (x<y)
{
if (f >= 0)
{
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
if (cornername & 0x4)
{
drawPixel(x0 + x, y0 + y, color);
drawPixel(x0 + y, y0 + x, color);
}
if (cornername & 0x2)
{
drawPixel(x0 + x, y0 - y, color);
drawPixel(x0 + y, y0 - x, color);
}
if (cornername & 0x8)
{
drawPixel(x0 - y, y0 + x, color);
drawPixel(x0 - x, y0 + y, color);
}
if (cornername & 0x1)
{
drawPixel(x0 - y, y0 - x, color);
drawPixel(x0 - x, y0 - y, color);
}
}
}
void Arduboy::fillCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color)
{
drawFastVLine(x0, y0-r, 2*r+1, color);
fillCircleHelper(x0, y0, r, 3, 0, color);
}
void Arduboy::fillCircleHelper
(int16_t x0, int16_t y0, uint8_t r, uint8_t cornername, int16_t delta,
uint8_t color)
{
// used to do circles and roundrects!
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
while (x < y)
{
if (f >= 0)
{
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
if (cornername & 0x1)
{
drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
}
if (cornername & 0x2)
{
drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
}
}
}
void Arduboy::drawLine
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color)
{
// bresenham's algorithm - thx wikpedia
boolean steep = abs(y1 - y0) > abs(x1 - x0);
if (steep) {
swap(x0, y0);
swap(x1, y1);
}
if (x0 > x1) {
swap(x0, x1);
swap(y0, y1);
}
int16_t dx, dy;
dx = x1 - x0;
dy = abs(y1 - y0);
int16_t err = dx / 2;
int8_t ystep;
if (y0 < y1)
{
ystep = 1;
}
else
{
ystep = -1;
}
for (; x0 <= x1; x0++)
{
if (steep)
{
drawPixel(y0, x0, color);
}
else
{
drawPixel(x0, y0, color);
}
err -= dy;
if (err < 0)
{
y0 += ystep;
err += dx;
}
}
}
void Arduboy::drawRect
(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color)
{
drawFastHLine(x, y, w, color);
drawFastHLine(x, y+h-1, w, color);
drawFastVLine(x, y, h, color);
drawFastVLine(x+w-1, y, h, color);
}
void Arduboy::drawFastVLine
(int16_t x, int16_t y, uint8_t h, uint8_t color)
{
int end = y+h;
for (int a = max(0,y); a < min(end,HEIGHT); a++)
{
drawPixel(x,a,color);
}
}
void Arduboy::drawFastHLine
(int16_t x, int16_t y, uint8_t w, uint8_t color)
{
int end = x+w;
for (int a = max(0,x); a < min(end,WIDTH); a++)
{
drawPixel(a,y,color);
}
}
void Arduboy::fillRect
(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color)
{
// stupidest version - update in subclasses if desired!
for (int16_t i=x; i<x+w; i++)
{
drawFastVLine(i, y, h, color);
}
}
void Arduboy::fillScreen(uint8_t color)
{
// C version :
//
// if (color) color = 0xFF; //change any nonzero argument to b11111111 and insert into screen array.
// for(int16_t i=0; i<1024; i++) { sBuffer[i] = color; } //sBuffer = (128*64) = 8192/8 = 1024 bytes.
asm volatile
(
// load color value into r27
"mov r27, %1 \n\t"
// if value is zero, skip assigning to 0xff
"cpse r27, __zero_reg__ \n\t"
"ldi r27, 0xff \n\t"
// load sBuffer pointer into Z
"movw r30, %0\n\t"
// counter = 0
"clr __tmp_reg__ \n\t"
"loopto: \n\t"
// (4x) push zero into screen buffer,
// then increment buffer position
"st Z+, r27 \n\t"
"st Z+, r27 \n\t"
"st Z+, r27 \n\t"
"st Z+, r27 \n\t"
// increase counter
"inc __tmp_reg__ \n\t"
// repeat for 256 loops
// (until counter rolls over back to 0)
"brne loopto \n\t"
// input: sBuffer, color
// modified: Z (r30, r31), r27
:
: "r" (sBuffer), "r" (color)
: "r30", "r31", "r27"
);
}
void Arduboy::drawRoundRect
(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color)
{
// smarter version
drawFastHLine(x+r, y, w-2*r, color); // Top
drawFastHLine(x+r, y+h-1, w-2*r, color); // Bottom
drawFastVLine(x, y+r, h-2*r, color); // Left
drawFastVLine(x+w-1, y+r, h-2*r, color); // Right
// draw four corners
drawCircleHelper(x+r, y+r, r, 1, color);
drawCircleHelper(x+w-r-1, y+r, r, 2, color);
drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
drawCircleHelper(x+r, y+h-r-1, r, 8, color);
}
void Arduboy::fillRoundRect
(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color)
{
// smarter version
fillRect(x+r, y, w-2*r, h, color);
// draw four corners
fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
fillCircleHelper(x+r, y+r, r, 2, h-2*r-1, color);
}
void Arduboy::drawTriangle
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
{
drawLine(x0, y0, x1, y1, color);
drawLine(x1, y1, x2, y2, color);
drawLine(x2, y2, x0, y0, color);
}
void Arduboy::fillTriangle
(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
{
int16_t a, b, y, last;
// Sort coordinates by Y order (y2 >= y1 >= y0)
if (y0 > y1)
{
swap(y0, y1); swap(x0, x1);
}
if (y1 > y2)
{
swap(y2, y1); swap(x2, x1);
}
if (y0 > y1)
{
swap(y0, y1); swap(x0, x1);
}
if(y0 == y2)
{ // Handle awkward all-on-same-line case as its own thing
a = b = x0;
if(x1 < a)
{
a = x1;
}
else if(x1 > b)
{
b = x1;
}
if(x2 < a)
{
a = x2;
}
else if(x2 > b)
{
b = x2;
}
drawFastHLine(a, y0, b-a+1, color);
return;
}
int16_t dx01 = x1 - x0,
dy01 = y1 - y0,
dx02 = x2 - x0,
dy02 = y2 - y0,
dx12 = x2 - x1,
dy12 = y2 - y1,
sa = 0,
sb = 0;
// For upper part of triangle, find scanline crossings for segments
// 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
// is included here (and second loop will be skipped, avoiding a /0
// error there), otherwise scanline y1 is skipped here and handled
// in the second loop...which also avoids a /0 error here if y0=y1
// (flat-topped triangle).
if (y1 == y2)
{
last = y1; // Include y1 scanline
}
else
{
last = y1-1; // Skip it
}
for(y = y0; y <= last; y++)
{
a = x0 + sa / dy01;
b = x0 + sb / dy02;
sa += dx01;
sb += dx02;
if(a > b)
{
swap(a,b);
}
drawFastHLine(a, y, b-a+1, color);
}
// For lower part of triangle, find scanline crossings for segments
// 0-2 and 1-2. This loop is skipped if y1=y2.
sa = dx12 * (y - y1);
sb = dx02 * (y - y0);
for(; y <= y2; y++)
{
a = x1 + sa / dy12;
b = x0 + sb / dy02;
sa += dx12;
sb += dx02;
if(a > b)
{
swap(a,b);
}
drawFastHLine(a, y, b-a+1, color);
}
}
void Arduboy::drawBitmap
(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h,
uint8_t color)
{
// no need to dar at all of we're offscreen
if (x+w < 0 || x > WIDTH-1 || y+h < 0 || y > HEIGHT-1)
return;
int yOffset = abs(y) % 8;
int sRow = y / 8;
if (y < 0) {
sRow--;
yOffset = 8 - yOffset;
}
int rows = h/8;
if (h%8!=0) rows++;
for (int a = 0; a < rows; a++) {
int bRow = sRow + a;
if (bRow > (HEIGHT/8)-1) break;
if (bRow > -2) {
for (int iCol = 0; iCol<w; iCol++) {
if (iCol + x > (WIDTH-1)) break;
if (iCol + x >= 0) {
if (bRow >= 0) {
if (color == WHITE) this->sBuffer[ (bRow*WIDTH) + x + iCol ] |= pgm_read_byte(bitmap+(a*w)+iCol) << yOffset;
else if (color == BLACK) this->sBuffer[ (bRow*WIDTH) + x + iCol ] &= ~(pgm_read_byte(bitmap+(a*w)+iCol) << yOffset);
else this->sBuffer[ (bRow*WIDTH) + x + iCol ] ^= pgm_read_byte(bitmap+(a*w)+iCol) << yOffset;
}
if (yOffset && bRow<(HEIGHT/8)-1 && bRow > -2) {
if (color == WHITE) this->sBuffer[ ((bRow+1)*WIDTH) + x + iCol ] |= pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset);
else if (color == BLACK) this->sBuffer[ ((bRow+1)*WIDTH) + x + iCol ] &= ~(pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset));
else this->sBuffer[ ((bRow+1)*WIDTH) + x + iCol ] ^= pgm_read_byte(bitmap+(a*w)+iCol) >> (8-yOffset);
}
}
}
}
}
}
void Arduboy::drawSlowXYBitmap
(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color)
{
// no need to dar at all of we're offscreen
if (x+w < 0 || x > WIDTH-1 || y+h < 0 || y > HEIGHT-1)
return;
int16_t xi, yi, byteWidth = (w + 7) / 8;
for(yi = 0; yi < h; yi++) {
for(xi = 0; xi < w; xi++ ) {
if(pgm_read_byte(bitmap + yi * byteWidth + xi / 8) & (128 >> (xi & 7))) {
drawPixel(x + xi, y + yi, color);
}
}
}
}
void Arduboy::drawChar
(int16_t x, int16_t y, unsigned char c, uint8_t color, uint8_t bg, uint8_t size)
{
boolean draw_background = bg != color;
if ((x >= WIDTH) || // Clip right
(y >= HEIGHT) || // Clip bottom
((x + 5 * size - 1) < 0) || // Clip left
((y + 8 * size - 1) < 0) // Clip top
)
{
return;
}
for (int8_t i=0; i<6; i++ )
{
uint8_t line;
if (i == 5)
{
line = 0x0;
}
else
{
line = pgm_read_byte(font+(c*5)+i);
}
for (int8_t j = 0; j<8; j++)
{
uint8_t draw_color = (line & 0x1) ? color : bg;
if (draw_color || draw_background) {
for (uint8_t a = 0; a < size; a++ ) {
for (uint8_t b = 0; b < size; b++ ) {
drawPixel(x + (i * size) + a, y + (j * size) + b, draw_color);
}
}
}
line >>= 1;
}
}
}
void Arduboy::setCursor(int16_t x, int16_t y)
{
cursor_x = x;
cursor_y = y;
}
void Arduboy::setTextSize(uint8_t s)
{
// textsize must always be 1 or higher
textsize = max(1,s);
}
void Arduboy::setTextWrap(boolean w)
{
wrap = w;
}
size_t Arduboy::write(uint8_t c)
{
if (c == '\n')
{
cursor_y += textsize*8;
cursor_x = 0;
}
else if (c == '\r')
{
// skip em
}
else
{
drawChar(cursor_x, cursor_y, c, 1, 0, textsize);
cursor_x += textsize*6;
if (wrap && (cursor_x > (WIDTH - textsize*6)))
{
// calling ourselves recursively for 'newline' is
// 12 bytes smaller than doing the same math here
write('\n');
}
}
}
void Arduboy::display()
{
this->paintScreen(sBuffer);
}
unsigned char* Arduboy::getBuffer()
{
return sBuffer;
}
boolean Arduboy::pressed(uint8_t buttons)
{
return (buttonsState() & buttons) == buttons;
}
boolean Arduboy::notPressed(uint8_t buttons)
{
return (buttonsState() & buttons) == 0;
}
void Arduboy::swap(int16_t& a, int16_t& b)
{
int temp = a;
a = b;
b = temp;
}

205
src/Arduboy.h Normal file
View File

@ -0,0 +1,205 @@
#ifndef Arduboy_h
#define Arduboy_h
#include "core/core.h"
#include <SPI.h>
#include <Print.h>
#include <limits.h>
// EEPROM settings
#define EEPROM_VERSION 0
#define EEPROM_BRIGHTNESS 1
#define EEPROM_AUDIO_ON_OFF 2
// we reserve the first 16 byte of EEPROM for system use
#define EEPROM_STORAGE_SPACE_START 16 // and onward
// eeprom settings above are neded for audio
#include "audio/audio.h"
#define PIXEL_SAFE_MODE
// compare Vcc to 1.1 bandgap
#define ADC_VOLTAGE (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1))
// compare temperature to 2.5 internal reference and _BV(MUX5)
#define ADC_TEMP (_BV(REFS0) | _BV(REFS1) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0))
class Arduboy : public Print, public ArduboyCore
{
public:
Arduboy();
/// Returns true if the button mask passed in is pressed.
/**
* if (pressed(LEFT_BUTTON + A_BUTTON))
*/
boolean pressed(uint8_t buttons);
/// Returns true if the button mask passed in not pressed.
/**
* if (notPressed(LEFT_BUTTON))
*/
boolean notPressed(uint8_t buttons);
/// Initializes the hardware
void begin();
/// Initializes the hardware (but with no boot logo)
void beginNoLogo();
void start() __attribute__ ((deprecated("use begin() instead")));
/// Scrolls in the Arduboy logo
void bootLogo();
/// Boot utils such as flashlight, etc
void inline bootUtils() __attribute__((always_inline));
/// Clears display.
void clear();
void clearDisplay() __attribute__ ((deprecated("use clear() instead")));
/// Copies the contents of the screen buffer to the screen.
/**
* X and Y positions on the display are from the top left corner, thus a Y of 64
* is the bottom of the screen and an X of 128 is the right side of the screen.
* "Color" or "value" means choosing whether a pixel is lit or not - if color is
* 0, the pixel is off (black), if color is 1, the pixel is on (white).
*/
void display();
/// Sets a single pixel on the screen buffer to white or black.
void drawPixel(int x, int y, uint8_t color);
uint8_t getPixel(uint8_t x, uint8_t y);
/// Draw a circle of a defined radius.
/**
* Draws a circle in white or black. X and Y are the center point of the circle.
*/
void drawCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color);
/// Draws one or more "corners" of a circle.
void drawCircleHelper(int16_t x0, int16_t y0, uint8_t r, uint8_t cornername, uint8_t color);
/// Draws a filled-in circle.
void fillCircle(int16_t x0, int16_t y0, uint8_t r, uint8_t color);
/// Draws one or both vertical halves of a filled-in circle.
void fillCircleHelper(int16_t x0, int16_t y0, uint8_t r, uint8_t cornername, int16_t delta, uint8_t color);
/// Draws a line between two points.
/**
* Uses Bresenham's algorithm.
*/
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint8_t color);
/// Draws a rectangle of a width and height.
void drawRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color);
/// Draws vertical line.
void drawFastVLine(int16_t x, int16_t y, uint8_t h, uint8_t color);
/// Draws a horizontal line.
void drawFastHLine(int16_t x, int16_t y, uint8_t w, uint8_t color);
/// Draws a filled-in rectangle.
void fillRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color);
/// Fills the screen buffer with white or black.
void fillScreen(uint8_t color);
/// Draws a rectangle with rounded edges.
void drawRoundRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color);
/// Draws a filled-in rectangle with rounded edges.
void fillRoundRect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t r, uint8_t color);
/// Draws the outline of a triangle.
void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color);
/// Draws a filled-in triangle.
void fillTriangle (int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color);
/// Draws a bitmap from program memory to a specific X/Y
void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color);
/// Draws images that are bit-oriented horizontally.
/**
* This requires a lot of additional CPU power and will draw images slower
* than drawBitmap, where the images are stored in a format that
* allows them to be directly written to the screen. It is
* recommended you use drawBitmap when possible.
*/
void drawSlowXYBitmap(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color);
/// Draws an ASCII character at a point.
void drawChar(int16_t x, int16_t y, unsigned char c, uint8_t color, uint8_t bg, uint8_t size);
/// Sets the location of the screen cursor.
void setCursor(int16_t x, int16_t y);
/// Set text size
/**
* As mentioned in drawChar(), individual ASCII characters are 6x8 pixels
* (5x7 with spacing on two edges). The size is a pixel multiplier,
* so a size of 2 means each character will be 12x16, etc.
*/
void setTextSize(uint8_t s);
/// Sets whether text will wrap at screen edges.
void setTextWrap(boolean w);
unsigned char* getBuffer();
/// Writes a single ASCII character to the screen.
virtual size_t write(uint8_t);
/// Seeds the random number generator with entropy from the temperature, voltage reading, and microseconds since boot.
/**
* This method is still most effective when called semi-randomly such
* as after a user hits a button to start a game or other semi-random
* events
*/
void initRandomSeed();
/// Swap the references of two pointers.
void swap(int16_t& a, int16_t& b);
ArduboyTunes tunes;
ArduboyAudio audio;
void setFrameRate(uint8_t rate);
bool nextFrame();
bool everyXFrames(uint8_t frames);
/// Returns the load on the CPU as a percentage.
/**
* This is based on how much of the time your app is spends rendering
* frames. This number can be higher than 100 if your app is rendering
* really slowly.
*/
int cpuLoad();
uint8_t frameRate;
uint16_t frameCount;
uint8_t eachFrameMillis;
long lastFrameStart;
long nextFrameStart;
bool post_render;
uint8_t lastFrameDurationMs;
/// useful for getting raw approximate voltage values
uint16_t rawADC(byte adc_bits);
protected:
unsigned char sBuffer[(HEIGHT*WIDTH)/8];
// Adafruit stuff
protected:
int16_t cursor_x;
int16_t cursor_y;
uint8_t textsize;
boolean wrap; // If set, 'wrap' text at right edge of display
};
#endif

30
src/ab_logo.c Normal file
View File

@ -0,0 +1,30 @@
#include <avr/pgmspace.h>
#ifndef ARDUBOY_LOGO_CREATED
#define ARDUBOY_LOGO_CREATED
// arduboy_logo.png
// 88x16
PROGMEM const unsigned char 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
};
#endif

319
src/audio/audio.cpp Normal file
View File

@ -0,0 +1,319 @@
#include "Arduboy.h"
#include "audio.h"
const byte PROGMEM tune_pin_to_timer_PGM[] = { 3, 1 };
volatile byte *_tunes_timer1_pin_port;
volatile byte _tunes_timer1_pin_mask;
volatile int32_t timer1_toggle_count;
volatile byte *_tunes_timer3_pin_port;
volatile byte _tunes_timer3_pin_mask;
byte _tune_pins[AVAILABLE_TIMERS];
byte _tune_num_chans = 0;
volatile boolean tune_playing; // is the score still playing?
volatile unsigned wait_timer_frequency2; /* its current frequency */
volatile boolean wait_timer_playing = false; /* is it currently playing a note? */
volatile boolean tonePlaying = false;
volatile unsigned long wait_toggle_count; /* countdown score waits */
// pointers to your musical score and your position in said score
volatile const byte *score_start = 0;
volatile const byte *score_cursor = 0;
// Table of midi note frequencies * 2
// They are times 2 for greater accuracy, yet still fits in a word.
// Generated from Excel by =ROUND(2*440/32*(2^((x-9)/12)),0) for 0<x<128
// The lowest notes might not work, depending on the Arduino clock frequency
// Ref: http://www.phy.mtu.edu/~suits/notefreqs.html
const uint8_t PROGMEM _midi_byte_note_frequencies[48] = {
16,17,18,19,21,22,23,24,26,28,29,31,33,35,37,39,41,44,46,49,52,55,58,62,65,
69,73,78,82,87,92,98,104,110,117,123,131,139,147,156,165,175,185,196,208,220,
233,247
};
const unsigned int PROGMEM _midi_word_note_frequencies[80] = {
262,277,294,311,330,349,370,392,415,440,466,494,523,554,587,622,659,
698,740,784,831,880,932,988,1047,1109,1175,1245,1319,1397,1480,1568,1661,1760,
1865,1976,2093,2217,2349,2489,2637,2794,2960,3136,3322,3520,3729,3951,4186,
4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902,8372,8870,9397,9956,
10548,11175,11840,12544,13290,14080,14917,15804,16744,17740,18795,19912,21096,
22351,23680,25088 };
/* AUDIO */
bool ArduboyAudio::audio_enabled = false;
void ArduboyAudio::on()
{
power_timer1_enable();
power_timer3_enable();
audio_enabled = true;
}
bool ArduboyAudio::enabled()
{
return audio_enabled;
}
void ArduboyAudio::off()
{
audio_enabled = false;
power_timer1_disable();
power_timer3_disable();
}
void ArduboyAudio::saveOnOff()
{
EEPROM.write(EEPROM_AUDIO_ON_OFF, audio_enabled);
}
void ArduboyAudio::begin()
{
tune_playing = false;
if (EEPROM.read(EEPROM_AUDIO_ON_OFF))
on();
}
/* TUNES */
void ArduboyTunes::initChannel(byte pin)
{
byte timer_num;
// we are all out of timers
if (_tune_num_chans == AVAILABLE_TIMERS)
return;
timer_num = pgm_read_byte(tune_pin_to_timer_PGM + _tune_num_chans);
_tune_pins[_tune_num_chans] = pin;
_tune_num_chans++;
pinMode(pin, OUTPUT);
switch (timer_num) {
case 1: // 16 bit timer
TCCR1A = 0;
TCCR1B = 0;
bitWrite(TCCR1B, WGM12, 1);
bitWrite(TCCR1B, CS10, 1);
_tunes_timer1_pin_port = portOutputRegister(digitalPinToPort(pin));
_tunes_timer1_pin_mask = digitalPinToBitMask(pin);
break;
case 3: // 16 bit timer
TCCR3A = 0;
TCCR3B = 0;
bitWrite(TCCR3B, WGM32, 1);
bitWrite(TCCR3B, CS30, 1);
_tunes_timer3_pin_port = portOutputRegister(digitalPinToPort(pin));
_tunes_timer3_pin_mask = digitalPinToBitMask(pin);
playNote(0, 60); /* start and stop channel 0 (timer 3) on middle C so wait/delay works */
stopNote(0);
break;
}
}
void ArduboyTunes::playNote(byte chan, byte note)
{
byte timer_num;
byte prescalar_bits;
unsigned int frequency2; /* frequency times 2 */
unsigned long ocr;
// we can't plan on a channel that does not exist
if (chan >= _tune_num_chans)
return;
// we only have frequencies for 128 notes
if (note > 127) {
return;
}
timer_num = pgm_read_byte(tune_pin_to_timer_PGM + chan);
if (note < 48) {
frequency2 = pgm_read_byte(_midi_byte_note_frequencies + note);
} else {
frequency2 = pgm_read_word(_midi_word_note_frequencies + note - 48);
}
//****** 16-bit timer *********
// two choices for the 16 bit timers: ck/1 or ck/64
ocr = F_CPU / frequency2 - 1;
prescalar_bits = 0b001;
if (ocr > 0xffff) {
ocr = F_CPU / frequency2 / 64 - 1;
prescalar_bits = 0b011;
}
// Set the OCR for the given timer, then turn on the interrupts
switch (timer_num) {
case 1:
TCCR1B = (TCCR1B & 0b11111000) | prescalar_bits;
OCR1A = ocr;
bitWrite(TIMSK1, OCIE1A, 1);
break;
case 3:
TCCR3B = (TCCR3B & 0b11111000) | prescalar_bits;
OCR3A = ocr;
wait_timer_frequency2 = frequency2; // for "tune_delay" function
wait_timer_playing = true;
bitWrite(TIMSK3, OCIE3A, 1);
break;
}
}
void ArduboyTunes::stopNote(byte chan)
{
byte timer_num;
timer_num = pgm_read_byte(tune_pin_to_timer_PGM + chan);
switch (timer_num) {
case 1:
TIMSK1 &= ~(1 << OCIE1A); // disable the interrupt
*_tunes_timer1_pin_port &= ~(_tunes_timer1_pin_mask); // keep pin low after stop
break;
case 3:
wait_timer_playing = false;
*_tunes_timer3_pin_port &= ~(_tunes_timer3_pin_mask); // keep pin low after stop
break;
}
}
void ArduboyTunes::playScore(const byte *score)
{
score_start = score;
score_cursor = score_start;
step(); /* execute initial commands */
tune_playing = true; /* release the interrupt routine */
}
void ArduboyTunes::stopScore (void)
{
for (uint8_t i = 0; i < _tune_num_chans; i++)
stopNote(i);
tune_playing = false;
}
bool ArduboyTunes::playing()
{
return tune_playing;
}
/* Do score commands until a "wait" is found, or the score is stopped.
This is called initially from tune_playcore, but then is called
from the interrupt routine when waits expire.
*/
/* if CMD < 0x80, then the other 7 bits and the next byte are a 15-bit big-endian number of msec to wait */
void ArduboyTunes::step()
{
byte command, opcode, chan;
unsigned duration;
while (1) {
command = pgm_read_byte(score_cursor++);
opcode = command & 0xf0;
chan = command & 0x0f;
if (opcode == TUNE_OP_STOPNOTE) { /* stop note */
stopNote(chan);
}
else if (opcode == TUNE_OP_PLAYNOTE) { /* play note */
playNote(chan, pgm_read_byte(score_cursor++));
}
else if (opcode == TUNE_OP_RESTART) { /* restart score */
score_cursor = score_start;
}
else if (opcode == TUNE_OP_STOP) { /* stop score */
tune_playing = false;
break;
}
else if (opcode < 0x80) { /* wait count in msec. */
duration = ((unsigned)command << 8) | (pgm_read_byte(score_cursor++));
wait_toggle_count = ((unsigned long) wait_timer_frequency2 * duration + 500) / 1000;
if (wait_toggle_count == 0) wait_toggle_count = 1;
break;
}
}
}
void ArduboyTunes::closeChannels(void)
{
byte timer_num;
for (uint8_t chan=0; chan < _tune_num_chans; chan++) {
timer_num = pgm_read_byte(tune_pin_to_timer_PGM + chan);
switch (timer_num) {
case 1:
TIMSK1 &= ~(1 << OCIE1A);
break;
case 3:
TIMSK3 &= ~(1 << OCIE3A);
break;
}
digitalWrite(_tune_pins[chan], 0);
}
_tune_num_chans = 0;
tune_playing = false;
}
void ArduboyTunes::soundOutput()
{
if (wait_timer_playing) { // toggle the pin if we're sounding a note
*_tunes_timer3_pin_port ^= _tunes_timer3_pin_mask;
}
if (tune_playing && wait_toggle_count && --wait_toggle_count == 0) {
// end of a score wait, so execute more score commands
ArduboyTunes::step(); // execute commands
}
}
void ArduboyTunes::tone(unsigned int frequency, unsigned long duration)
{
tonePlaying = true;
uint8_t prescalarbits = 0b001;
int32_t toggle_count = 0;
uint32_t ocr = 0;
// two choices for the 16 bit timers: ck/1 or ck/64
ocr = F_CPU / frequency / 2 - 1;
prescalarbits = 0b001;
if (ocr > 0xffff) {
ocr = F_CPU / frequency / 2 / 64 - 1;
prescalarbits = 0b011;
}
TCCR1B = (TCCR1B & 0b11111000) | prescalarbits;
// Calculate the toggle count
if (duration > 0) {
toggle_count = 2 * frequency * duration / 1000;
}
else {
toggle_count = -1;
}
// Set the OCR for the given timer,
// set the toggle count,
// then turn on the interrupts
OCR1A = ocr;
timer1_toggle_count = toggle_count;
bitWrite(TIMSK1, OCIE1A, 1);
}
// TIMER 1
ISR(TIMER1_COMPA_vect)
{
if (tonePlaying) {
if (timer1_toggle_count != 0) {
// toggle the pin
*_tunes_timer1_pin_port ^= _tunes_timer1_pin_mask;
if (timer1_toggle_count > 0) timer1_toggle_count--;
}
else {
tonePlaying = false;
TIMSK1 &= ~(1 << OCIE1A); // disable the interrupt
*_tunes_timer1_pin_port &= ~(_tunes_timer1_pin_mask); // keep pin low after stop
}
}
else {
*_tunes_timer1_pin_port ^= _tunes_timer1_pin_mask; // toggle the pin
}
}
// TIMER 3
ISR(TIMER3_COMPA_vect)
{
// Timer 3 is the one assigned first, so we keep it running always
// and use it to time score waits, whether or not it is playing a note.
ArduboyTunes::soundOutput();
}

61
src/audio/audio.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef ArduboyAudio_h
#define ArduboyAudio_h
#include <Arduino.h>
#include <EEPROM.h>
#include <avr/pgmspace.h>
#include <avr/power.h>
#define AVAILABLE_TIMERS 2
#define TUNE_OP_PLAYNOTE 0x90 /* play a note: low nibble is generator #, note is next byte */
#define TUNE_OP_STOPNOTE 0x80 /* stop a note: low nibble is generator # */
#define TUNE_OP_RESTART 0xe0 /* restart the score from the beginning */
#define TUNE_OP_STOP 0xf0 /* stop playing */
class ArduboyAudio
{
public:
void static begin();
void static on();
void static off();
void static saveOnOff();
bool static enabled();
protected:
bool static audio_enabled;
};
class ArduboyTunes
{
public:
// Playtune Functions
/// Assign a timer to an output pin.
void initChannel(byte pin);
/// Start playing a polyphonic score.
void playScore(const byte *score);
/// Stop playing the score.
void stopScore();
/// Delay in milliseconds.
void delay(unsigned msec);
/// Stop all timers.
void closeChannels();
bool playing();
void tone(unsigned int frequency, unsigned long duration);
// called via interrupt
void static step();
void static soundOutput();
private:
void static playNote (byte chan, byte note);
void static stopNote (byte chan);
};
#endif

333
src/core/core.cpp Normal file
View File

@ -0,0 +1,333 @@
#include "core.h"
// need to redeclare these here since we declare them static in .h
volatile uint8_t *ArduboyCore::mosiport,
*ArduboyCore::csport, *ArduboyCore::dcport;
uint8_t ArduboyCore::mosipinmask,
ArduboyCore::cspinmask, ArduboyCore::dcpinmask;
const uint8_t PROGMEM pinBootProgram[] = {
// buttons
PIN_LEFT_BUTTON, INPUT_PULLUP,
PIN_RIGHT_BUTTON, INPUT_PULLUP,
PIN_UP_BUTTON, INPUT_PULLUP,
PIN_DOWN_BUTTON, INPUT_PULLUP,
PIN_A_BUTTON, INPUT_PULLUP,
PIN_B_BUTTON, INPUT_PULLUP,
// OLED SPI
DC, OUTPUT,
CS, OUTPUT,
RST, OUTPUT,
0
};
const uint8_t PROGMEM lcdBootProgram[] = {
// boot defaults are commented out but left here incase they
// might prove useful for reference
//
// Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf
//
// Display Off
// 0xAE,
// Set Display Clock Divisor v = 0xF0
// default is 0x80
0xD5, 0xF0,
// Set Multiplex Ratio v = 0x3F
// 0xA8, 0x3F,
// Set Display Offset v = 0
// 0xD3, 0x00,
// Set Start Line (0)
// 0x40,
// Charge Pump Setting v = enable (0x14)
// default is disabled
0x8D, 0x14,
// Set Segment Re-map (A0) | (b0001)
// default is (b0000)
0xA1,
// Set COM Output Scan Direction
0xC8,
// Set COM Pins v
// 0xDA, 0x12,
// Set Contrast v = 0xCF
0x81, 0xCF,
// Set Precharge = 0xF1
0xD9, 0xF1,
// Set VCom Detect
// 0xDB, 0x40,
// Entire Display ON
// 0xA4,
// Set normal/inverse display
// 0xA6,
// Display On
0xAF,
// set display mode = horizontal addressing mode (0x00)
0x20, 0x00,
// set col address range
// 0x21, 0x00, COLUMN_ADDRESS_END,
// set page address range
// 0x22, 0x00, PAGE_ADDRESS_END
};
ArduboyCore::ArduboyCore() {}
void ArduboyCore::boot()
{
#if F_CPU == 8000000L
slowCPU();
#endif
SPI.begin();
bootPins();
bootLCD();
#ifdef SAFE_MODE
if (buttonsState() == (LEFT_BUTTON | UP_BUTTON))
safeMode();
#endif
saveMuchPower();
}
#if F_CPU == 8000000L
// if we're compiling for 8Mhz we need to slow the CPU down because the
// hardware clock on the Arduboy is 16MHz
void ArduboyCore::slowCPU()
{
uint8_t oldSREG = SREG;
cli(); // suspend interrupts
CLKPR = _BV(CLKPCE); // allow reprogramming clock
CLKPR = 1; // set clock divisor to 2 (0b0001)
SREG = oldSREG; // restore interrupts
}
#endif
void ArduboyCore::bootPins()
{
uint8_t pin, mode;
const uint8_t *i = pinBootProgram;
while(true) {
pin = pgm_read_byte(i++);
mode = pgm_read_byte(i++);
if (pin==0) break;
pinMode(pin, mode);
}
digitalWrite(RST, HIGH);
delay(1); // VDD (3.3V) goes high at start, lets just chill for a ms
digitalWrite(RST, LOW); // bring reset low
delay(10); // wait 10ms
digitalWrite(RST, HIGH); // bring out of reset
}
void ArduboyCore::bootLCD()
{
// setup the ports we need to talk to the OLED
csport = portOutputRegister(digitalPinToPort(CS));
cspinmask = digitalPinToBitMask(CS);
dcport = portOutputRegister(digitalPinToPort(DC));
dcpinmask = digitalPinToBitMask(DC);
SPI.setClockDivider(SPI_CLOCK_DIV2);
LCDCommandMode();
// run our customized boot-up command sequence against the
// OLED to initialize it properly for Arduboy
for (int8_t i=0; i < sizeof(lcdBootProgram); i++) {
SPI.transfer(pgm_read_byte(lcdBootProgram + i));
}
LCDDataMode();
}
void ArduboyCore::LCDDataMode()
{
*dcport |= dcpinmask;
*csport &= ~cspinmask;
}
void ArduboyCore::LCDCommandMode()
{
*csport |= cspinmask;
*dcport &= ~dcpinmask;
*csport &= ~cspinmask;
}
void ArduboyCore::safeMode()
{
blank(); // too avoid random gibberish
while (true) {
asm volatile("nop \n");
}
}
/* Power Management */
void ArduboyCore::idle()
{
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_mode();
}
void ArduboyCore::saveMuchPower()
{
power_adc_disable();
power_usart0_disable();
power_twi_disable();
// timer 0 is for millis()
// timers 1 and 3 are for music and sounds
power_timer2_disable();
power_usart1_disable();
// we need USB, for now (to allow triggered reboots to reprogram)
// power_usb_disable()
}
uint8_t ArduboyCore::width() { return WIDTH; }
uint8_t ArduboyCore::height() { return HEIGHT; }
/* Drawing */
void ArduboyCore::paint8Pixels(uint8_t pixels)
{
SPI.transfer(pixels);
}
void ArduboyCore::paintScreen(const unsigned char *image)
{
for (int i = 0; i < (HEIGHT*WIDTH)/8; i++)
{
SPI.transfer(pgm_read_byte(image + i));
}
}
// paint from a memory buffer, this should be FAST as it's likely what
// will be used by any buffer based subclass
void ArduboyCore::paintScreen(unsigned char image[])
{
for (int i = 0; i < (HEIGHT*WIDTH)/8; i++)
{
// SPI.transfer(image[i]);
// we need to burn 18 cycles between sets of SPDR
// 4 clock cycles
SPDR = image[i];
// 7 clock cycles
asm volatile(
"mul __zero_reg__, __zero_reg__ \n" // 2 cycles
"mul __zero_reg__, __zero_reg__ \n" // 2 cycles
"mul __zero_reg__, __zero_reg__ \n" // 2 cycles
);
}
}
void ArduboyCore::blank()
{
for (int i = 0; i < (HEIGHT*WIDTH)/8; i++)
SPI.transfer(0x00);
}
void ArduboyCore::sendLCDCommand(uint8_t command)
{
LCDCommandMode();
SPI.transfer(command);
LCDDataMode();
}
// invert the display or set to normal
// when inverted, a pixel set to 0 will be on
void ArduboyCore::invert(boolean inverse)
{
sendLCDCommand(inverse ? OLED_PIXELS_INVERTED : OLED_PIXELS_NORMAL);
}
// turn all display pixels on, ignoring buffer contents
// or set to normal buffer display
void ArduboyCore::allPixelsOn(boolean on)
{
sendLCDCommand(on ? OLED_ALL_PIXELS_ON : OLED_PIXELS_FROM_RAM);
}
// flip the display vertically or set to normal
void ArduboyCore::flipVertical(boolean flipped)
{
sendLCDCommand(flipped ? OLED_VERTICAL_FLIPPED : OLED_VERTICAL_NORMAL);
}
#define OLED_HORIZ_FLIPPED 0xA0 // reversed segment re-map
#define OLED_HORIZ_NORMAL 0xA1 // normal segment re-map
// flip the display horizontally or set to normal
void ArduboyCore::flipHorizontal(boolean flipped)
{
sendLCDCommand(flipped ? OLED_HORIZ_FLIPPED : OLED_HORIZ_NORMAL);
}
/* RGB LED */
void ArduboyCore::setRGBled(uint8_t red, uint8_t green, uint8_t blue)
{
#ifdef ARDUBOY_10 // RGB, all the pretty colors
// inversion is necessary because these are common annode LEDs
analogWrite(RED_LED, 255 - red);
analogWrite(GREEN_LED, 255 - green);
analogWrite(BLUE_LED, 255 - blue);
#elif defined(AB_DEVKIT)
// only blue on devkit
digitalWrite(BLUE_LED, ~blue);
#endif
}
/* Buttons */
uint8_t ArduboyCore::getInput()
{
return buttonsState();
}
uint8_t ArduboyCore::buttonsState()
{
uint8_t buttons;
// using ports here is ~100 bytes smaller than digitalRead()
#ifdef AB_DEVKIT
// down, left, up
buttons = ((~PINB) & B01110000);
// right button
buttons = buttons | (((~PINC) & B01000000) >> 4);
// A and B
buttons = buttons | (((~PINF) & B11000000) >> 6);
#elif defined(ARDUBOY_10)
// down, up, left right
buttons = ((~PINF) & B11110000);
// A (left)
buttons = buttons | (((~PINE) & B01000000) >> 3);
// B (right)
buttons = buttons | (((~PINB) & B00010000) >> 2);
#endif
return buttons;
}

298
src/core/core.h Normal file
View File

@ -0,0 +1,298 @@
#ifndef ArduboyCore_h
#define ArduboyCore_h
#include <avr/power.h>
#include <SPI.h>
#include <avr/sleep.h>
#include <limits.h>
// main hardware compile flags
#if !defined(ARDUBOY_10) && !defined(AB_DEVKIT)
/// defaults to Arduboy Release 1.0 if not using a boards.txt file
/**
* we default to Arduboy Release 1.0 if a compile flag has not been
* passed to us from a boards.txt file
*
* if you wish to compile for the devkit without using a boards.txt
* file simply comment out the ARDUBOY_10 define and uncomment
* the AB_DEVKIT define like this:
*
* // #define ARDUBOY_10
* #define AB_DEVKIT
*/
#define ARDUBOY_10 //< compile for the production Arduboy v1.0
// #define AB_DEVKIT //< compile for the official dev kit
#endif
#ifdef AB_DEVKIT
#define DEVKIT //< for compatibilty with older sketches
#define SAFE_MODE //< include safe mode (44 bytes)
#endif
#ifdef ARDUBOY_10
#define CS 12
#define DC 4
#define RST 6
#define RED_LED 10
#define GREEN_LED 11
#define BLUE_LED 9
#define TX_LED 30
#define RX_LED 17
// pin values for buttons, probably shouldn't use these
#define PIN_LEFT_BUTTON A2
#define PIN_RIGHT_BUTTON A1
#define PIN_UP_BUTTON A0
#define PIN_DOWN_BUTTON A3
#define PIN_A_BUTTON 7
#define PIN_B_BUTTON 8
// bit values for button states
#define LEFT_BUTTON _BV(5)
#define RIGHT_BUTTON _BV(6)
#define UP_BUTTON _BV(7)
#define DOWN_BUTTON _BV(4)
#define A_BUTTON _BV(3)
#define B_BUTTON _BV(2)
#define PIN_SPEAKER_1 5
#define PIN_SPEAKER_2 13
#define PIN_SPEAKER_1_PORT &PORTC
#define PIN_SPEAKER_2_PORT &PORTC
#define PIN_SPEAKER_1_BITMASK _BV(6)
#define PIN_SPEAKER_2_BITMASK _BV(7)
#elif defined(AB_DEVKIT)
#define CS 6
#define DC 4
#define RST 12
// map all LEDs to the single TX LED on DEVKIT
#define RED_LED 17
#define GREEN_LED 17
#define BLUE_LED 17
#define TX_LED 17
#define RX_LED 17
// pin values for buttons, probably shouldn't use these
#define PIN_LEFT_BUTTON 9
#define PIN_RIGHT_BUTTON 5
#define PIN_UP_BUTTON 8
#define PIN_DOWN_BUTTON 10
#define PIN_A_BUTTON A0
#define PIN_B_BUTTON A1
// bit values for button states
#define LEFT_BUTTON _BV(5)
#define RIGHT_BUTTON _BV(2)
#define UP_BUTTON _BV(4)
#define DOWN_BUTTON _BV(6)
#define A_BUTTON _BV(1)
#define B_BUTTON _BV(0)
#define PIN_SPEAKER_1 A2
#define PIN_SPEAKER_2 A3
#define PIN_SPEAKER_1_PORT &PORTF
#define PIN_SPEAKER_2_PORT &PORTF
#define PIN_SPEAKER_1_BITMASK _BV(5)
#define PIN_SPEAKER_2_BITMASK _BV(4)
#endif
// OLED hardware (SSD1306)
#define OLED_PIXELS_INVERTED 0xA7 // All pixels inverted
#define OLED_PIXELS_NORMAL 0xA6 // All pixels normal
#define OLED_ALL_PIXELS_ON 0xA5 // all pixels on
#define OLED_PIXELS_FROM_RAM 0xA4 // pixels mapped to display RAM contents
#define OLED_VERTICAL_FLIPPED 0xC0 // reversed COM scan direction
#define OLED_VERTICAL_NORMAL 0xC8 // normal COM scan direction
// -----
#define COLUMN_ADDRESS_END (WIDTH - 1) & 0x7F // 128 pixels wide
#define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 0x07 // 8 pages high
#define WIDTH 128
#define HEIGHT 64
#define INVERT 2 //< lit/unlit pixel
#define WHITE 1 //< lit pixel
#define BLACK 0 //< unlit pixel
class ArduboyCore
{
public:
ArduboyCore();
/// allows the CPU to idle between frames
/**
* This puts the CPU in "Idle" sleep mode. You should call this as often
* as you can for the best power savings. The timer 0 overflow interrupt
* will wake up the chip every 1ms - so even at 60 FPS a well written
* app should be able to sleep maybe half the time in between rendering
* it's own frames.
*
* See the Arduboy class nextFrame() for an example of how to use idle()
* in a frame loop.
*/
void static idle();
void static LCDDataMode(); //< put the display in data mode
/// put the display in command mode
/**
* See SSD1306 documents for available commands and command sequences.
*
* Links:
* - https://www.adafruit.com/datasheets/SSD1306.pdf
* - http://www.eimodule.com/download/SSD1306-OLED-Controller.pdf
*/
void static LCDCommandMode();
uint8_t static width(); //< return display width
uint8_t static height(); // < return display height
/// get current state of all buttons (bitmask)
/**
* Bit mask that is returned:
*
* Hi Low
* DevKit 00000000 - reserved
* -DLU-RAB D down
* U up
* 1.0 00000000 L left
* URLDAB-- R right
*
* Of course you shouldn't worry about bits (they may change with future
* hardware revisions) and should instead use the button defines:
* LEFT_BUTTON, A_BUTTON, UP_BUTTON, etc.
*/
uint8_t static getInput(); __attribute__ ((deprecated("use buttonsState() instead")));
uint8_t static buttonsState();
// paints 8 pixels (vertically) from a single byte
// - 1 is lit, 0 is unlit
//
// NOTE: You probably wouldn't actually use this, you'd build something
// higher level that does it's own calls to SPI.transfer(). It's
// included for completeness since it seems there should be some very
// rudimentary low-level draw function in the core that supports the
// minimum unit that the hardware allows (which is a strip of 8 pixels)
//
// This routine starts in the top left and then across the screen.
// After each "page" (row) of 8 pixels is drawn it will shift down
// to start drawing the next page. To paint the full screen you call
// this function 1,024 times.
//
// Example:
//
// X = painted pixels, . = unpainted
//
// blank() paint8Pixels() 0xFF, 0, 0x0F, 0, 0xF0
// v TOP LEFT corner (8x9) v TOP LEFT corner
// ........ (page 1) X...X... (page 1)
// ........ X...X...
// ........ X...X...
// ........ X...X...
// ........ X.X.....
// ........ X.X.....
// ........ X.X.....
// ........ (end of page 1) X.X..... (end of page 1)
// ........ (page 2) ........ (page 2)
void static paint8Pixels(uint8_t pixels);
/// paints an entire image directly to hardware (from PROGMEM)
/*
* Each byte will be 8 vertical pixels, painted in the same order as
* explained above in paint8Pixels.
*/
void static paintScreen(const unsigned char *image);
/// paints an entire image directly to hardware (from RAM)
/*
* Each byte will be 8 vertical pixels, painted in the same order as
* explained above in paint8Pixels.
*/
void static paintScreen(unsigned char image[]);
/// paints a blank (black) screen to hardware
void static blank();
/// invert the display or set to normal
/**
* when inverted, a pixel set to 0 will be on
*/
void static invert(boolean inverse);
/// turn all display pixels on, or display the buffer contents
/**
* when set to all pixels on, the display buffer will be
* ignored but not altered
*/
void static allPixelsOn(boolean on);
/// flip the display vertically or set to normal
void static flipVertical(boolean flipped);
/// flip the display horizontally or set to normal
void static flipHorizontal(boolean flipped);
/// send a single byte command to the OLED
void static sendLCDCommand(uint8_t command);
/// set the light output of the RGB LEB
void setRGBled(uint8_t red, uint8_t green, uint8_t blue);
protected:
/// boots the hardware
/**
* - sets input/output/pullup mode for pins
* - powers up the OLED screen and initializes it properly
* - sets up power saving
* - kicks CPU down to 8Mhz if needed
* - allows Safe mode to be entered
*/
void static boot();
/// Safe mode
/**
* Safe Mode is engaged by holding down both the LEFT button and UP button
* when plugging the device into USB. It puts your device into a tight
* loop and allows it to be reprogrammed even if you have uploaded a very
* broken sketch that interferes with the normal USB triggered auto-reboot
* functionality of the device.
*
* This is most useful on Devkits because they lack a built-in reset
* button.
*/
void static inline safeMode() __attribute__((always_inline));
// internals
void static inline bootLCD() __attribute__((always_inline));
void static inline bootPins() __attribute__((always_inline));
void static inline slowCPU() __attribute__((always_inline));
void static inline saveMuchPower(); __attribute__((always_inline));
private:
volatile static uint8_t *mosiport, *csport, *dcport;
uint8_t static mosipinmask, cspinmask, dcpinmask;
};
#endif

View File

@ -183,6 +183,7 @@ const static unsigned char font[] PROGMEM =
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,