mirror of https://github.com/MLXXXp/Arduboy2.git
Multiple OLED support
added multiple oled display support added flexible width and height support optimized buttonsState with optional bootlkey support
This commit is contained in:
parent
6360eb2b37
commit
a0c58545e1
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,696 @@
|
||||||
|
/**
|
||||||
|
* @file Arduboy2Core.cpp
|
||||||
|
* \brief
|
||||||
|
* The Arduboy2Core class for Arduboy hardware initilization and control.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Arduboy2Core.h"
|
||||||
|
#include <avr/wdt.h>
|
||||||
|
|
||||||
|
const uint8_t PROGMEM lcdBootProgram[] = {
|
||||||
|
// boot defaults are commented out but left here in case they
|
||||||
|
// might prove useful for reference
|
||||||
|
// Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf
|
||||||
|
|
||||||
|
#ifdef OLED_SH1106
|
||||||
|
0x8D, 0x14, // Charge Pump Setting v = enable (0x14)
|
||||||
|
0xA1, // Set Segment Re-map
|
||||||
|
0xC8, // Set COM Output Scan Direction
|
||||||
|
0x81, 0xCF, // Set Contrast v = 0xCF
|
||||||
|
0xD9, 0xF1, // Set Precharge = 0xF1
|
||||||
|
OLED_SET_COLUMN_ADDRESS_LO, //Set column address for left most pixel
|
||||||
|
0xAF // Display On
|
||||||
|
#elif defined(OLED_96X96)
|
||||||
|
0x15, 0x10, 0x3f, //Set column start and end address
|
||||||
|
0x75, 0x00, 0x5f, //Set row start and end address
|
||||||
|
0xA0, 0x55, //set re-map: split odd-even COM signals|COM remap|vertical address increment|column address remap
|
||||||
|
0xA1, 0x00, //set display start line
|
||||||
|
0xA2, 0x60, //set display offset
|
||||||
|
//0xA4, //Normal display
|
||||||
|
0xA8, 0x5F, //Set MUX ratio 96MUX
|
||||||
|
//0xB2, 0x23,
|
||||||
|
//0xB3, 0xF0, //set devider clock | oscillator frequency
|
||||||
|
//0x81, 0x0F, //Set contrast
|
||||||
|
//0xBC, 0x1F, //set precharge voltage
|
||||||
|
//0x82, 0xFE, //set second Precharge speed
|
||||||
|
0xB1, 0x21, //reset and 1st precharge phase length phase 2:2 DCLKs, Phase 1: 1 DCLKs
|
||||||
|
//0xBB, 0x0F, //set 2nd precharge period: 15 DCLKs
|
||||||
|
//0xbe, 0x1F, //output level high voltage com signal
|
||||||
|
//0xB8, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, //set gray scale table
|
||||||
|
0xAF //Display on
|
||||||
|
#else
|
||||||
|
// for SSD1306 and compatible displays
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined OLED_SSD1309 //required additionally for SSD1309
|
||||||
|
0x21, 0x00, COLUMN_ADDRESS_END
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
Arduboy2Core::Arduboy2Core() { }
|
||||||
|
|
||||||
|
void Arduboy2Core::boot()
|
||||||
|
{
|
||||||
|
#ifdef ARDUBOY_SET_CPU_8MHZ
|
||||||
|
// ARDUBOY_SET_CPU_8MHZ will be set by the IDE using boards.txt
|
||||||
|
setCPUSpeed8MHz();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Select the ADC input here so a delay isn't required in initRandomSeed()
|
||||||
|
ADMUX = RAND_SEED_IN_ADMUX;
|
||||||
|
|
||||||
|
bootPins();
|
||||||
|
bootSPI();
|
||||||
|
bootOLED();
|
||||||
|
bootPowerSaving();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUBOY_SET_CPU_8MHZ
|
||||||
|
// If we're compiling for 8MHz we need to slow the CPU down because the
|
||||||
|
// hardware clock on the Arduboy is 16MHz.
|
||||||
|
// We also need to readjust the PLL prescaler because the Arduino USB code
|
||||||
|
// likely will have incorrectly set it for an 8MHz hardware clock.
|
||||||
|
void Arduboy2Core::setCPUSpeed8MHz()
|
||||||
|
{
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli(); // suspend interrupts
|
||||||
|
PLLCSR = _BV(PINDIV); // dissable the PLL and set prescale for 16MHz)
|
||||||
|
CLKPR = _BV(CLKPCE); // allow reprogramming clock
|
||||||
|
CLKPR = 1; // set clock divisor to 2 (0b0001)
|
||||||
|
PLLCSR = _BV(PLLE) | _BV(PINDIV); // enable the PLL (with 16MHz prescale)
|
||||||
|
SREG = oldSREG; // restore interrupts
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Pins are set to the proper modes and levels for the specific hardware.
|
||||||
|
// This routine must be modified if any pins are moved to a different port
|
||||||
|
void Arduboy2Core::bootPins()
|
||||||
|
{
|
||||||
|
#ifdef ARDUBOY_10
|
||||||
|
|
||||||
|
// Port B INPUT_PULLUP or HIGH
|
||||||
|
PORTB |= _BV(RED_LED_BIT) |
|
||||||
|
#ifndef ARDUINO_AVR_PROMICRO
|
||||||
|
_BV(GREEN_LED_BIT) |
|
||||||
|
#endif
|
||||||
|
_BV(BLUE_LED_BIT) | _BV(B_BUTTON_BIT);
|
||||||
|
// Port B INPUT or LOW (none)
|
||||||
|
// Port B inputs
|
||||||
|
DDRB &= ~(_BV(B_BUTTON_BIT));
|
||||||
|
// Port B outputs
|
||||||
|
DDRB |= _BV(RED_LED_BIT) |
|
||||||
|
#ifndef ARDUINO_AVR_PROMICRO
|
||||||
|
_BV(GREEN_LED_BIT) |
|
||||||
|
#endif
|
||||||
|
_BV(BLUE_LED_BIT) | _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT);
|
||||||
|
|
||||||
|
// Port C
|
||||||
|
// Speaker: Not set here. Controlled by audio class
|
||||||
|
|
||||||
|
// Port D INPUT_PULLUP or HIGH
|
||||||
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
|
PORTD |= _BV(CS_BIT) | _BV(GREEN_LED_BIT);
|
||||||
|
#else
|
||||||
|
PORTD |= _BV(CS_BIT);
|
||||||
|
#endif
|
||||||
|
// Port D INPUT or LOW
|
||||||
|
PORTD &= ~(_BV(RST_BIT));
|
||||||
|
// Port D inputs (none)
|
||||||
|
// Port D outputs
|
||||||
|
DDRD |= _BV(RST_BIT) | _BV(CS_BIT) |
|
||||||
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
|
_BV(GREEN_LED_BIT) |
|
||||||
|
#endif
|
||||||
|
_BV(DC_BIT);
|
||||||
|
|
||||||
|
// Port E INPUT_PULLUP or HIGH
|
||||||
|
PORTE |= _BV(A_BUTTON_BIT);
|
||||||
|
// Port E INPUT or LOW (none)
|
||||||
|
// Port E inputs
|
||||||
|
DDRE &= ~(_BV(A_BUTTON_BIT));
|
||||||
|
// Port E outputs (none)
|
||||||
|
|
||||||
|
// Port F INPUT_PULLUP or HIGH
|
||||||
|
PORTF |= _BV(LEFT_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) |
|
||||||
|
_BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT);
|
||||||
|
// Port F INPUT or LOW
|
||||||
|
PORTF &= ~(_BV(RAND_SEED_IN_BIT));
|
||||||
|
// Port F inputs
|
||||||
|
DDRF &= ~(_BV(LEFT_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) |
|
||||||
|
_BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT) |
|
||||||
|
_BV(RAND_SEED_IN_BIT));
|
||||||
|
// Port F outputs (none)
|
||||||
|
|
||||||
|
#elif defined(AB_DEVKIT)
|
||||||
|
|
||||||
|
// Port B INPUT_PULLUP or HIGH
|
||||||
|
PORTB |= _BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT) |
|
||||||
|
_BV(BLUE_LED_BIT);
|
||||||
|
// Port B INPUT or LOW (none)
|
||||||
|
// Port B inputs
|
||||||
|
DDRB &= ~(_BV(LEFT_BUTTON_BIT) | _BV(UP_BUTTON_BIT) | _BV(DOWN_BUTTON_BIT));
|
||||||
|
// Port B outputs
|
||||||
|
DDRB |= _BV(BLUE_LED_BIT) | _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT);
|
||||||
|
|
||||||
|
// Port C INPUT_PULLUP or HIGH
|
||||||
|
PORTC |= _BV(RIGHT_BUTTON_BIT);
|
||||||
|
// Port C INPUT or LOW (none)
|
||||||
|
// Port C inputs
|
||||||
|
DDRC &= ~(_BV(RIGHT_BUTTON_BIT));
|
||||||
|
// Port C outputs (none)
|
||||||
|
|
||||||
|
// Port D INPUT_PULLUP or HIGH
|
||||||
|
PORTD |= _BV(CS_BIT);
|
||||||
|
// Port D INPUT or LOW
|
||||||
|
PORTD &= ~(_BV(RST_BIT));
|
||||||
|
// Port D inputs (none)
|
||||||
|
// Port D outputs
|
||||||
|
DDRD |= _BV(RST_BIT) | _BV(CS_BIT) | _BV(DC_BIT);
|
||||||
|
|
||||||
|
// Port E (none)
|
||||||
|
|
||||||
|
// Port F INPUT_PULLUP or HIGH
|
||||||
|
PORTF |= _BV(A_BUTTON_BIT) | _BV(B_BUTTON_BIT);
|
||||||
|
// Port F INPUT or LOW
|
||||||
|
PORTF &= ~(_BV(RAND_SEED_IN_BIT));
|
||||||
|
// Port F inputs
|
||||||
|
DDRF &= ~(_BV(A_BUTTON_BIT) | _BV(B_BUTTON_BIT) | _BV(RAND_SEED_IN_BIT));
|
||||||
|
// Port F outputs (none)
|
||||||
|
// Speaker: Not set here. Controlled by audio class
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::bootOLED()
|
||||||
|
{
|
||||||
|
// reset the display
|
||||||
|
delayShort(5); // reset pin should be low here. let it stay low a while
|
||||||
|
bitSet(RST_PORT, RST_BIT); // set high to come out of reset
|
||||||
|
delayShort(5); // wait a while
|
||||||
|
|
||||||
|
// select the display (permanently, since nothing else is using SPI)
|
||||||
|
bitClear(CS_PORT, CS_BIT);
|
||||||
|
|
||||||
|
// run our customized boot-up command sequence against the
|
||||||
|
// OLED to initialize it properly for Arduboy
|
||||||
|
LCDCommandMode();
|
||||||
|
for (uint8_t i = 0; i < sizeof(lcdBootProgram); i++) {
|
||||||
|
SPItransfer(pgm_read_byte(lcdBootProgram + i));
|
||||||
|
}
|
||||||
|
LCDDataMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::LCDDataMode()
|
||||||
|
{
|
||||||
|
bitSet(DC_PORT, DC_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::LCDCommandMode()
|
||||||
|
{
|
||||||
|
bitClear(DC_PORT, DC_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the SPI interface for the display
|
||||||
|
void Arduboy2Core::bootSPI()
|
||||||
|
{
|
||||||
|
// master, mode 0, MSB first, CPU clock / 2 (8MHz)
|
||||||
|
SPCR = _BV(SPE) | _BV(MSTR);
|
||||||
|
SPSR = _BV(SPI2X);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to the SPI bus (MOSI pin)
|
||||||
|
void Arduboy2Core::SPItransfer(uint8_t data)
|
||||||
|
{
|
||||||
|
SPDR = data;
|
||||||
|
/*
|
||||||
|
* The following NOP introduces a small delay that can prevent the wait
|
||||||
|
* loop form iterating when running at the maximum speed. This gives
|
||||||
|
* about 10% more speed, even if it seems counter-intuitive. At lower
|
||||||
|
* speeds it is unnoticed.
|
||||||
|
*/
|
||||||
|
asm volatile("nop");
|
||||||
|
while (!(SPSR & _BV(SPIF))) { } // wait
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::safeMode()
|
||||||
|
{
|
||||||
|
if (buttonsState() == UP_BUTTON)
|
||||||
|
{
|
||||||
|
digitalWriteRGB(RED_LED, RGB_ON);
|
||||||
|
|
||||||
|
// prevent the bootloader magic number from being overwritten by timer 0
|
||||||
|
// when a timer variable overlaps the magic number location
|
||||||
|
power_timer0_disable();
|
||||||
|
|
||||||
|
while (true) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Power Management */
|
||||||
|
|
||||||
|
void Arduboy2Core::idle()
|
||||||
|
{
|
||||||
|
set_sleep_mode(SLEEP_MODE_IDLE);
|
||||||
|
sleep_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::bootPowerSaving()
|
||||||
|
{
|
||||||
|
// disable Two Wire Interface (I2C) and the ADC
|
||||||
|
PRR0 = _BV(PRTWI) | _BV(PRADC);
|
||||||
|
// disable USART1
|
||||||
|
PRR1 = _BV(PRUSART1);
|
||||||
|
// All other bits will be written with 0 so will be enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shut down the display
|
||||||
|
void Arduboy2Core::displayOff()
|
||||||
|
{
|
||||||
|
LCDCommandMode();
|
||||||
|
SPItransfer(0xAE); // display off
|
||||||
|
SPItransfer(0x8D); // charge pump:
|
||||||
|
SPItransfer(0x10); // disable
|
||||||
|
delayShort(250);
|
||||||
|
bitClear(RST_PORT, RST_BIT); // set display reset pin low (reset state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart the display after a displayOff()
|
||||||
|
void Arduboy2Core::displayOn()
|
||||||
|
{
|
||||||
|
bootOLED();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Arduboy2Core::width() { return WIDTH; }
|
||||||
|
|
||||||
|
uint8_t Arduboy2Core::height() { return HEIGHT; }
|
||||||
|
|
||||||
|
|
||||||
|
/* Drawing */
|
||||||
|
|
||||||
|
void Arduboy2Core::paint8Pixels(uint8_t pixels)
|
||||||
|
{
|
||||||
|
SPItransfer(pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::paintScreen(const uint8_t *image)
|
||||||
|
{
|
||||||
|
#ifdef OLED_SH1106
|
||||||
|
for (uint8_t i = 0; i < HEIGHT / 8; i++)
|
||||||
|
{
|
||||||
|
LCDCommandMode();
|
||||||
|
SPDR = (OLED_SET_PAGE_ADDRESS + i);
|
||||||
|
while (!(SPSR & _BV(SPIF)));
|
||||||
|
SPDR = (OLED_SET_COLUMN_ADDRESS_HI); // only reset hi nibble to zero
|
||||||
|
while (!(SPSR & _BV(SPIF)));
|
||||||
|
LCDDataMode();
|
||||||
|
for (uint8_t j = WIDTH; j > 0; j--)
|
||||||
|
{
|
||||||
|
SPDR = pgm_read_byte(image++);
|
||||||
|
while (!(SPSR & _BV(SPIF)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif defined(OLED_96X96)
|
||||||
|
uint16_t i = 0;
|
||||||
|
for (uint8_t col = 0; col < WIDTH / 2; col++)
|
||||||
|
{
|
||||||
|
for (uint8_t row = 0; row < HEIGHT / 8; row++)
|
||||||
|
{
|
||||||
|
uint8_t b1 = pgm_read_byte(image + i);
|
||||||
|
uint8_t b2 = pgm_read_byte(image + i + 1);
|
||||||
|
for (uint8_t shift = 0; shift < 8; shift++)
|
||||||
|
{
|
||||||
|
uint8_t c = 0xFF;
|
||||||
|
if ((b1 & 1) == 0) c &= 0x0F;
|
||||||
|
if ((b2 & 1) == 0) c &= 0xF0;
|
||||||
|
SPDR = c;
|
||||||
|
b1 = b1 >> 1;
|
||||||
|
b2 = b2 >> 1;
|
||||||
|
while (!(SPSR & _BV(SPIF)));
|
||||||
|
}
|
||||||
|
i += WIDTH;
|
||||||
|
}
|
||||||
|
i -= HEIGHT / 8 * WIDTH - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
//OLED SSD1306 and compatibles
|
||||||
|
for (int i = 0; i < (HEIGHT*WIDTH)/8; i++)
|
||||||
|
{
|
||||||
|
SPItransfer(pgm_read_byte(image + i));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint from a memory buffer, this should be FAST as it's likely what
|
||||||
|
// will be used by any buffer based subclass
|
||||||
|
void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
|
||||||
|
{
|
||||||
|
#ifdef OLED_SH1106
|
||||||
|
//Assembly optimized page mode display code with clear support.
|
||||||
|
//Each byte transfer takes 18 cycles
|
||||||
|
asm volatile (
|
||||||
|
" ldi r25, %[page_cmd] \n\t"
|
||||||
|
".l1: \n\t"
|
||||||
|
" ldi r24, %[width] ;1 \n\t"
|
||||||
|
" ldi r20,5 ;1 \n\t"
|
||||||
|
" cbi %[dc_port], %[dc_bit] ;2 cmd mode \n\t"
|
||||||
|
" out %[spdr], r25 ;1 \n\t"
|
||||||
|
".l2: subi r20,1 ;r20*3-1 : 14 \n\t"
|
||||||
|
" brne .l2 \n\t"
|
||||||
|
" rjmp .+0 ;2 \n\t"
|
||||||
|
" ldi r20,%[col_cmd] ;1 \n\t"
|
||||||
|
" out %[spdr], r20 ;1 \n\t"
|
||||||
|
".l3: rjmp .l7 ;2 \n\t"
|
||||||
|
".l4: ld r20, Z ;2 \n\t"
|
||||||
|
" cp %[clear], __zero_reg__ ;1 \n\t"
|
||||||
|
" brne .l5 ;1/2 \n\t"
|
||||||
|
" nop ;1 \n\t"
|
||||||
|
" rjmp .l6 ;2 \n\t"
|
||||||
|
".l5: st Z, __zero_reg__ ;2 : 7 \n\t"
|
||||||
|
".l6: sbi %[dc_port], %[dc_bit] ;2 data mode \n\t"
|
||||||
|
" out %[spdr], r20 ;1 \n\t"
|
||||||
|
" adiw r30, 1 ;2 \n\t"
|
||||||
|
".l7: rjmp .+0 ;2 \n\t"
|
||||||
|
" nop ;1 \n\t"
|
||||||
|
" subi r24, 1 ;1 \n\t"
|
||||||
|
" brne .l4 ;1/2 : 5/6 \n\t"
|
||||||
|
" rjmp .+0 ;2 \n\t"
|
||||||
|
" subi r25, -1 ;1 \n\t"
|
||||||
|
" cpi r25,%[page_end] ;1 \n\t"
|
||||||
|
" brne .l1 ;1/2 : 5/6 \n\t"
|
||||||
|
:
|
||||||
|
: [ptr] "z" (image),
|
||||||
|
[page_cmd] "M" (OLED_SET_PAGE_ADDRESS),
|
||||||
|
[page_end] "M" (OLED_SET_PAGE_ADDRESS + (HEIGHT / 8)),
|
||||||
|
[dc_port] "I" (_SFR_IO_ADDR(DC_PORT)),
|
||||||
|
[dc_bit] "I" (DC_BIT),
|
||||||
|
[spdr] "I" (_SFR_IO_ADDR(SPDR)),
|
||||||
|
[col_cmd] "M" (OLED_SET_COLUMN_ADDRESS_HI),
|
||||||
|
[width] "M" (WIDTH + 1),
|
||||||
|
[clear] "a" (clear)
|
||||||
|
: "r20", "r24", "r25"
|
||||||
|
);
|
||||||
|
#elif defined(OLED_96X96)
|
||||||
|
// 1 bit to 4-bit display code with clear support.
|
||||||
|
// Each transfer takes 18 cycles with additional 4 cycles for a column change.
|
||||||
|
asm volatile(
|
||||||
|
" ldi r25, %[col] \n\t"
|
||||||
|
".lcolumn: \n\t"
|
||||||
|
" ldi r24, %[row] ;1 \n\t"
|
||||||
|
".lrow: \n\t"
|
||||||
|
" ldi r21, 7 ;1 \n\t"
|
||||||
|
" ld r22, z ;2 \n\t"
|
||||||
|
" ldd r23, z+1 ;2 \n\t"
|
||||||
|
".lshiftstart: \n\t"
|
||||||
|
" ldi r20, 0xFF ;1 \n\t"
|
||||||
|
" sbrs r22, 0 ;1 \n\t"
|
||||||
|
" andi r20, 0x0f ;1 \n\t"
|
||||||
|
" sbrs r23, 0 ;1 \n\t"
|
||||||
|
" andi r20,0xf0 ;1 \n\t"
|
||||||
|
" out %[spdr], r20 ;1 \n\t"
|
||||||
|
" \n\t"
|
||||||
|
" cp %[clear], __zero_reg__ ;1 \n\t"
|
||||||
|
" brne .lclear1 ;1/2 \n\t"
|
||||||
|
".lshiftothers: \n\t"
|
||||||
|
" movw r18, %A[ptr] ;1 \n\t"
|
||||||
|
" rjmp .+0 ;2 \n\t"
|
||||||
|
" rjmp .lshiftnext ;2 \n\t"
|
||||||
|
".lclear1: \n\t"
|
||||||
|
" st z, __zero_reg__ ;2 \n\t"
|
||||||
|
" std z+1, __zero_reg__ ;2 \n\t"
|
||||||
|
".lshiftnext: \n\t"
|
||||||
|
" \n\t"
|
||||||
|
" lsr r22 ;1 \n\t"
|
||||||
|
" lsr r23 ;1 \n\t"
|
||||||
|
" \n\t"
|
||||||
|
" ldi r20, 0xFF ;1 \n\t"
|
||||||
|
" sbrs r22, 0 ;1/2 \n\t"
|
||||||
|
" andi r20, 0x0f ;1 \n\t"
|
||||||
|
" sbrs r23, 0 ;1/2 \n\t"
|
||||||
|
" andi r20,0xf0 ;1 \n\t"
|
||||||
|
" \n\t"
|
||||||
|
" subi r18, %[top_lsb] ;1 \n\t" //image - (HEIGHT / 8) * ((WIDTH / 8) - 1) + 2
|
||||||
|
" sbci r19, %[top_msb] ;1 \n\t"
|
||||||
|
" subi r21, 1 ;1 \n\t"
|
||||||
|
" out %[spdr], r20 ;1 \n\t"
|
||||||
|
" brne .lshiftothers ;1/2 \n\t"
|
||||||
|
" \n\t"
|
||||||
|
" nop ;1 \n\t"
|
||||||
|
" subi %A[ptr], %[width] ;1 \n\t" //image + width (negated addition)
|
||||||
|
" sbci %B[ptr], -1 ;1 \n\t"
|
||||||
|
" subi r24, 1 ;1 \n\t"
|
||||||
|
" brne .lrow ;1/2 \n\t"
|
||||||
|
" \n\t"
|
||||||
|
" movw %A[ptr], r18 ;1 \n\t"
|
||||||
|
" subi r25, 1 ;1 \n\t"
|
||||||
|
" brne .lcolumn ;1/2 \n\t"
|
||||||
|
:
|
||||||
|
: [ptr] "z" (image),
|
||||||
|
[spdr] "I" (_SFR_IO_ADDR(SPDR)),
|
||||||
|
[row] "M" (HEIGHT / 8),
|
||||||
|
[col] "M" (WIDTH / 2),
|
||||||
|
[width] "M" (256 - WIDTH),
|
||||||
|
[top_lsb] "M" ((WIDTH * ((HEIGHT / 8) - 1) - 2) & 0xFF),
|
||||||
|
[top_msb] "M" ((WIDTH * ((HEIGHT / 8) - 1) - 2) >> 8),
|
||||||
|
[clear] "a" (clear)
|
||||||
|
: "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25"
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
//OLED SSD1306 and compatibles
|
||||||
|
//data only transfer with clear support at 18 cycles per transfer
|
||||||
|
asm volatile (
|
||||||
|
" ldi r24,%[len_lsb] \n\t"
|
||||||
|
" ldi r25,%[len_msb] \n\t"
|
||||||
|
".l1: ld r20, Z ;2 \n\t"
|
||||||
|
" out %[spdr], r20 ;1 \n\t"
|
||||||
|
" cp %[clear], __zero_reg__ ;1 \n\t" //if (clear) *(image++) = 0
|
||||||
|
" breq .l2 ;1/2 : 5/6 \n\t"
|
||||||
|
" st Z+, __zero_reg__ ;2 \n\t"
|
||||||
|
" rjmp .l3 ;2 \n\t"
|
||||||
|
".l2: \n\t"
|
||||||
|
" adiw r30, 1 ;2 \n\t" // else *(image++)
|
||||||
|
" nop ;1 \n\t"
|
||||||
|
".l3: \n\t"
|
||||||
|
" rjmp .+0 ;2 \n\t"
|
||||||
|
" rjmp .+0 ;2 \n\t"
|
||||||
|
" rjmp .+0 ;2 \n\t"
|
||||||
|
" sbiw r24, 1 ;1 \n\t"
|
||||||
|
" brne .l1 ;1/2 : 18 \n\t"
|
||||||
|
:
|
||||||
|
: [ptr] "z" (image),
|
||||||
|
[spdr] "I" (_SFR_IO_ADDR(SPDR)),
|
||||||
|
[len_msb] "M" (WIDTH * (HEIGHT / 8) >> 8),
|
||||||
|
[len_lsb] "M" (WIDTH * (HEIGHT / 8) & 0xFF),
|
||||||
|
[clear] "a" (clear)
|
||||||
|
: "r20", "r24", "r25"
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
while (!(SPSR & _BV(SPIF))); // wait for the last transfer to finish
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::blank()
|
||||||
|
{
|
||||||
|
#ifdef OLED_SH1106
|
||||||
|
for (int i = 0; i < (HEIGHT*132)/8; i++)
|
||||||
|
SPItransfer(0x00);
|
||||||
|
#elif defined(OLED_96X96)
|
||||||
|
for (int i = 0; i < (HEIGHT*WIDTH)/2; i++)
|
||||||
|
SPItransfer(0x00);
|
||||||
|
#else //OLED SSD1306 and compatibles
|
||||||
|
for (int i = 0; i < (HEIGHT*WIDTH)/8; i++)
|
||||||
|
SPItransfer(0x00);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::sendLCDCommand(uint8_t command)
|
||||||
|
{
|
||||||
|
LCDCommandMode();
|
||||||
|
SPItransfer(command);
|
||||||
|
LCDDataMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// invert the display or set to normal
|
||||||
|
// when inverted, a pixel set to 0 will be on
|
||||||
|
void Arduboy2Core::invert(bool inverse)
|
||||||
|
{
|
||||||
|
sendLCDCommand(inverse ? OLED_PIXELS_INVERTED : OLED_PIXELS_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn all display pixels on, ignoring buffer contents
|
||||||
|
// or set to normal buffer display
|
||||||
|
void Arduboy2Core::allPixelsOn(bool on)
|
||||||
|
{
|
||||||
|
sendLCDCommand(on ? OLED_ALL_PIXELS_ON : OLED_PIXELS_FROM_RAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// flip the display vertically or set to normal
|
||||||
|
void Arduboy2Core::flipVertical(bool flipped)
|
||||||
|
{
|
||||||
|
sendLCDCommand(flipped ? OLED_VERTICAL_FLIPPED : OLED_VERTICAL_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// flip the display horizontally or set to normal
|
||||||
|
void Arduboy2Core::flipHorizontal(bool flipped)
|
||||||
|
{
|
||||||
|
sendLCDCommand(flipped ? OLED_HORIZ_FLIPPED : OLED_HORIZ_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RGB LED */
|
||||||
|
|
||||||
|
void Arduboy2Core::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, which is not PWM capable
|
||||||
|
(void)red; // parameter unused
|
||||||
|
(void)green; // parameter unused
|
||||||
|
bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, blue ? RGB_ON : RGB_OFF);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue)
|
||||||
|
{
|
||||||
|
#ifdef ARDUBOY_10
|
||||||
|
bitWrite(RED_LED_PORT, RED_LED_BIT, red);
|
||||||
|
bitWrite(GREEN_LED_PORT, GREEN_LED_BIT, green);
|
||||||
|
bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, blue);
|
||||||
|
#elif defined(AB_DEVKIT)
|
||||||
|
// only blue on DevKit
|
||||||
|
(void)red; // parameter unused
|
||||||
|
(void)green; // parameter unused
|
||||||
|
bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, blue);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arduboy2Core::digitalWriteRGB(uint8_t color, uint8_t val)
|
||||||
|
{
|
||||||
|
#ifdef ARDUBOY_10
|
||||||
|
if (color == RED_LED)
|
||||||
|
{
|
||||||
|
bitWrite(RED_LED_PORT, RED_LED_BIT, val);
|
||||||
|
}
|
||||||
|
else if (color == GREEN_LED)
|
||||||
|
{
|
||||||
|
bitWrite(GREEN_LED_PORT, GREEN_LED_BIT, val);
|
||||||
|
}
|
||||||
|
else if (color == BLUE_LED)
|
||||||
|
{
|
||||||
|
bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, val);
|
||||||
|
}
|
||||||
|
#elif defined(AB_DEVKIT)
|
||||||
|
// only blue on DevKit
|
||||||
|
if (color == BLUE_LED)
|
||||||
|
{
|
||||||
|
bitWrite(BLUE_LED_PORT, BLUE_LED_BIT, val);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
|
||||||
|
uint8_t Arduboy2Core::buttonsState()
|
||||||
|
{
|
||||||
|
uint8_t buttons;
|
||||||
|
|
||||||
|
// using ports here is ~100 bytes smaller than digitalRead()
|
||||||
|
#ifdef AB_DEVKIT
|
||||||
|
// down, left, up
|
||||||
|
buttons = ((~PINB) & B01110000);
|
||||||
|
// right button
|
||||||
|
if ((PINC & _BV(6)) == 0) buttons |= RIGHT_BUTTON; //compiles to shorter and faster code
|
||||||
|
|
||||||
|
// A and B
|
||||||
|
if ((PINF & _BV(7)) == 0) buttons |= A_BUTTON;
|
||||||
|
if ((PINF & _BV(6)) == 0) buttons |= B_BUTTON;
|
||||||
|
#elif defined(ARDUBOY_10)
|
||||||
|
// down, up, left right
|
||||||
|
buttons = ((~PINF) & B11110000);
|
||||||
|
// A (left)
|
||||||
|
if ((PINE & _BV(6)) == 0) {buttons |= A_BUTTON;}
|
||||||
|
// B (right)
|
||||||
|
if ((PINB & _BV(4)) == 0) {buttons |= B_BUTTON;}
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_BOOTLOADER_KEYS
|
||||||
|
//bootloader button combo
|
||||||
|
if (buttons == (LEFT_BUTTON | UP_BUTTON | A_BUTTON | B_BUTTON))
|
||||||
|
{ cli();
|
||||||
|
//set magic boot key
|
||||||
|
*(uint8_t *)0x0800 = 0x77;//using uint8_t saves an instruction
|
||||||
|
*(uint8_t *)0x0801 = 0x77;
|
||||||
|
//enable and trigger watchdog by timeout
|
||||||
|
wdt_enable(WDTO_15MS);
|
||||||
|
while (true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delay in ms with 16 bit duration
|
||||||
|
void Arduboy2Core::delayShort(uint16_t ms)
|
||||||
|
{
|
||||||
|
delay((unsigned long) ms);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,735 @@
|
||||||
|
/**
|
||||||
|
* @file Arduboy2Core.h
|
||||||
|
* \brief
|
||||||
|
* The Arduboy2Core class for Arduboy hardware initilization and control.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARDUBOY2_CORE_H
|
||||||
|
#define ARDUBOY2_CORE_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <avr/power.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
|
||||||
|
|
||||||
|
#define RGB_ON LOW /**< For digitially setting an RGB LED on using digitalWriteRGB() */
|
||||||
|
#define RGB_OFF HIGH /**< For digitially setting an RGB LED off using digitalWriteRGB() */
|
||||||
|
|
||||||
|
// ----- Arduboy pins -----
|
||||||
|
#ifdef ARDUBOY_10
|
||||||
|
|
||||||
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
|
#define PIN_CS 3 // Pro Micro alternative display CS pin
|
||||||
|
#define CS_PORT PORTD
|
||||||
|
#define CS_BIT PORTD3
|
||||||
|
#else
|
||||||
|
#define PIN_CS 12 // Display CS Arduino pin number
|
||||||
|
#define CS_PORT PORTD // Display CS port
|
||||||
|
#define CS_BIT PORTD6 // Display CS physical bit number
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PIN_DC 4 // Display D/C Arduino pin number
|
||||||
|
#define DC_PORT PORTD // Display D/C port
|
||||||
|
#define DC_BIT PORTD4 // Display D/C physical bit number
|
||||||
|
|
||||||
|
#define PIN_RST 6 // Display reset Arduino pin number
|
||||||
|
#define RST_PORT PORTD // Display reset port
|
||||||
|
#define RST_BIT PORTD7 // Display reset physical bit number
|
||||||
|
|
||||||
|
#define SPI_MOSI_PORT PORTB
|
||||||
|
#define SPI_MOSI_BIT PORTB2
|
||||||
|
|
||||||
|
#define SPI_SCK_PORT PORTB
|
||||||
|
#define SPI_SCK_BIT PORTB1
|
||||||
|
|
||||||
|
#define RED_LED 10 /**< The pin number for the red color in the RGB LED. */
|
||||||
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
|
#define GREEN_LED 3 // Pro Micro alternative green LED pin
|
||||||
|
#else
|
||||||
|
#define GREEN_LED 11 /**< The pin number for the green color in the RGB LED. */
|
||||||
|
#endif
|
||||||
|
#define BLUE_LED 9 /**< The pin number for the blue color in the RGB LED. */
|
||||||
|
|
||||||
|
#define RED_LED_PORT PORTB
|
||||||
|
#define RED_LED_BIT PORTB6
|
||||||
|
|
||||||
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
|
#define GREEN_LED_PORT PORTD // Pro Micro alternative green LED port
|
||||||
|
#define GREEN_LED_BIT PORTD0
|
||||||
|
#else
|
||||||
|
#define GREEN_LED_PORT PORTB
|
||||||
|
#define GREEN_LED_BIT PORTB7
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BLUE_LED_PORT PORTB
|
||||||
|
#define BLUE_LED_BIT PORTB5
|
||||||
|
|
||||||
|
// bit values for button states
|
||||||
|
// these are determined by the buttonsState() function
|
||||||
|
#define LEFT_BUTTON _BV(5) /**< The Left button value for functions requiring a bitmask */
|
||||||
|
#define RIGHT_BUTTON _BV(6) /**< The Right button value for functions requiring a bitmask */
|
||||||
|
#define UP_BUTTON _BV(7) /**< The Up button value for functions requiring a bitmask */
|
||||||
|
#define DOWN_BUTTON _BV(4) /**< The Down button value for functions requiring a bitmask */
|
||||||
|
#define A_BUTTON _BV(3) /**< The A button value for functions requiring a bitmask */
|
||||||
|
#define B_BUTTON _BV(2) /**< The B button value for functions requiring a bitmask */
|
||||||
|
|
||||||
|
#define PIN_LEFT_BUTTON A2
|
||||||
|
#define LEFT_BUTTON_PORT PORTF
|
||||||
|
#define LEFT_BUTTON_BIT PORTF5
|
||||||
|
|
||||||
|
#define PIN_RIGHT_BUTTON A1
|
||||||
|
#define RIGHT_BUTTON_PORT PORTF
|
||||||
|
#define RIGHT_BUTTON_BIT PORTF6
|
||||||
|
|
||||||
|
#define PIN_UP_BUTTON A0
|
||||||
|
#define UP_BUTTON_PORT PORTF
|
||||||
|
#define UP_BUTTON_BIT PORTF7
|
||||||
|
|
||||||
|
#define PIN_DOWN_BUTTON A3
|
||||||
|
#define DOWN_BUTTON_PORT PORTF
|
||||||
|
#define DOWN_BUTTON_BIT PORTF4
|
||||||
|
|
||||||
|
#define PIN_A_BUTTON 7
|
||||||
|
#define A_BUTTON_PORT PORTE
|
||||||
|
#define A_BUTTON_BIT PORTE6
|
||||||
|
|
||||||
|
#define PIN_B_BUTTON 8
|
||||||
|
#define B_BUTTON_PORT PORTB
|
||||||
|
#define B_BUTTON_BIT PORTB4
|
||||||
|
|
||||||
|
#define PIN_SPEAKER_1 5 /**< The pin number of the first lead of the speaker */
|
||||||
|
|
||||||
|
#define SPEAKER_1_PORT PORTC
|
||||||
|
#define SPEAKER_1_DDR DDRC
|
||||||
|
#define SPEAKER_1_BIT PORTC6
|
||||||
|
|
||||||
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
|
#define PIN_SPEAKER_2 2 //Pro Micro alternative for 2nd speaker pin
|
||||||
|
#define SPEAKER_2_PORT PORTD
|
||||||
|
#define SPEAKER_2_DDR DDRD
|
||||||
|
#define SPEAKER_2_BIT PORTD1
|
||||||
|
#else
|
||||||
|
#define PIN_SPEAKER_2 13 /**< The pin number of the second lead of the speaker */
|
||||||
|
#define SPEAKER_2_PORT PORTC
|
||||||
|
#define SPEAKER_2_DDR DDRC
|
||||||
|
#define SPEAKER_2_BIT PORTC7
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RAND_SEED_IN A4 // Open analog input used for noise by initRandomSeed()
|
||||||
|
#define RAND_SEED_IN_PORTF
|
||||||
|
#define RAND_SEED_IN_BIT PORTF1
|
||||||
|
// Value for ADMUX to read the random seed pin: 2.56V reference, ADC1
|
||||||
|
#define RAND_SEED_IN_ADMUX (_BV(REFS0) | _BV(REFS1) | _BV(MUX0))
|
||||||
|
// -----------------------
|
||||||
|
|
||||||
|
// ----- DevKit pins -----
|
||||||
|
#elif defined(AB_DEVKIT)
|
||||||
|
|
||||||
|
#define PIN_CS 6 // Display CS Arduino pin number
|
||||||
|
#define CS_PORT PORTD // Display CS port
|
||||||
|
#define CS_BIT PORTD7 // Display CS physical bit number
|
||||||
|
|
||||||
|
#define PIN_DC 4 // Display D/C Arduino pin number
|
||||||
|
#define DC_PORT PORTD // Display D/C port
|
||||||
|
#define DC_BIT PORTD4 // Display D/C physical bit number
|
||||||
|
|
||||||
|
#define PIN_RST 12 // Display reset Arduino pin number
|
||||||
|
#define RST_PORT PORTD // Display reset port
|
||||||
|
#define RST_BIT PORTD6 // Display reset physical bit number
|
||||||
|
|
||||||
|
#define SPI_MOSI_PORT PORTB
|
||||||
|
#define SPI_MOSI_BIT PORTB2
|
||||||
|
|
||||||
|
#define SPI_SCK_PORT PORTB
|
||||||
|
#define SPI_SCK_BIT PORTB1
|
||||||
|
|
||||||
|
// map all LEDs to the single TX LED on DEVKIT
|
||||||
|
#define RED_LED 17
|
||||||
|
#define GREEN_LED 17
|
||||||
|
#define BLUE_LED 17
|
||||||
|
|
||||||
|
#define BLUE_LED_PORT PORTB
|
||||||
|
#define BLUE_LED_BIT PORTB0
|
||||||
|
|
||||||
|
// bit values for button states
|
||||||
|
// these are determined by the buttonsState() function
|
||||||
|
#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)
|
||||||
|
|
||||||
|
// pin values for buttons, probably shouldn't use these
|
||||||
|
#define PIN_LEFT_BUTTON 9
|
||||||
|
#define LEFT_BUTTON_PORT PORTB
|
||||||
|
#define LEFT_BUTTON_BIT PORTB5
|
||||||
|
|
||||||
|
#define PIN_RIGHT_BUTTON 5
|
||||||
|
#define RIGHT_BUTTON_PORT PORTC
|
||||||
|
#define RIGHT_BUTTON_BIT PORTC6
|
||||||
|
|
||||||
|
#define PIN_UP_BUTTON 8
|
||||||
|
#define UP_BUTTON_PORT PORTB
|
||||||
|
#define UP_BUTTON_BIT PORTB4
|
||||||
|
|
||||||
|
#define PIN_DOWN_BUTTON 10
|
||||||
|
#define DOWN_BUTTON_PORT PORTB
|
||||||
|
#define DOWN_BUTTON_BIT PORTB6
|
||||||
|
|
||||||
|
#define PIN_A_BUTTON A0
|
||||||
|
#define A_BUTTON_PORT PORTF
|
||||||
|
#define A_BUTTON_BIT PORTF7
|
||||||
|
|
||||||
|
#define PIN_B_BUTTON A1
|
||||||
|
#define B_BUTTON_PORT PORTF
|
||||||
|
#define B_BUTTON_BIT PORTF6
|
||||||
|
|
||||||
|
#define PIN_SPEAKER_1 A2
|
||||||
|
#define SPEAKER_1_PORT PORTF
|
||||||
|
#define SPEAKER_1_DDR DDRF
|
||||||
|
#define SPEAKER_1_BIT PORTF5
|
||||||
|
// SPEAKER_2 is purposely not defined for DEVKIT as it could potentially
|
||||||
|
// be dangerous and fry your hardware (because of the devkit wiring).
|
||||||
|
//
|
||||||
|
// Reference: https://github.com/Arduboy/Arduboy/issues/108
|
||||||
|
|
||||||
|
#define RAND_SEED_IN A4 // Open analog input used for noise by initRandomSeed()
|
||||||
|
#define RAND_SEED_IN_PORTF
|
||||||
|
#define RAND_SEED_IN_BIT PORTF1
|
||||||
|
// Value for ADMUX to read the random seed pin: 2.56V reference, ADC1
|
||||||
|
#define RAND_SEED_IN_ADMUX (_BV(REFS0) | _BV(REFS1) | _BV(MUX0))
|
||||||
|
|
||||||
|
#endif
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
// OLED hardware (SSD1306,SSD1309,SH1106,OLED_64X64)
|
||||||
|
|
||||||
|
#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 OLED_HORIZ_FLIPPED 0xA0 // reversed segment re-map
|
||||||
|
#define OLED_HORIZ_NORMAL 0xA1 // normal segment re-map
|
||||||
|
|
||||||
|
#define OLED_SET_PAGE_ADDRESS 0xB0
|
||||||
|
#ifdef OLED_SH1106
|
||||||
|
#define OLED_SET_COLUMN_ADDRESS_LO 0x02 //SH1106 only: 1st pixel starts on column 2
|
||||||
|
#else
|
||||||
|
#define OLED_SET_COLUMN_ADDRESS_LO 0x00
|
||||||
|
#endif
|
||||||
|
#define OLED_SET_COLUMN_ADDRESS_HI 0x10
|
||||||
|
// -----
|
||||||
|
#ifdef OLED_96X96
|
||||||
|
#define WIDTH 96
|
||||||
|
#define HEIGHT 96
|
||||||
|
#elif defined(OLED_128X64)
|
||||||
|
#define WIDTH 128
|
||||||
|
#define HEIGHT 96
|
||||||
|
#else
|
||||||
|
#define WIDTH 128 /**< The width of the display in pixels */
|
||||||
|
#define HEIGHT 64 /**< The height of the display in pixels */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define COLUMN_ADDRESS_END (WIDTH - 1) & 127 // 128 pixels wide
|
||||||
|
#define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 7 // 8 pages high
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Lower level functions generally dealing directly with the hardware.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* This class is inherited by Arduboy2Base and thus also Arduboy2, so wouldn't
|
||||||
|
* normally be used directly by a sketch.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* A friend class named _Arduboy2Ex_ is declared by this class. The intention
|
||||||
|
* is to allow a sketch to create an _Arduboy2Ex_ class which would have access
|
||||||
|
* to the private and protected members of the Arduboy2Core class. It is hoped
|
||||||
|
* that this may eliminate the need to create an entire local copy of the
|
||||||
|
* library, in order to extend the functionality, in most circumstances.
|
||||||
|
*/
|
||||||
|
class Arduboy2Core
|
||||||
|
{
|
||||||
|
friend class Arduboy2Ex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Arduboy2Core();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Idle the CPU to save power.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
void static idle();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Put the display into data mode.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* When placed in data mode, data that is sent to the display will be
|
||||||
|
* considered as data to be displayed.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* This is a low level function that is not intended for general use in a
|
||||||
|
* sketch. It has been made public and documented for use by derived
|
||||||
|
* classes.
|
||||||
|
*
|
||||||
|
* \see LCDCommandMode() SPItransfer()
|
||||||
|
*/
|
||||||
|
inline void static LCDDataMode() __attribute__((always_inline));
|
||||||
|
/** \brief
|
||||||
|
* Put the display into command mode.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* When placed in command mode, data that is sent to the display will be
|
||||||
|
* treated as commands.
|
||||||
|
*
|
||||||
|
* See the SSD1306 controller and OLED display documents for available
|
||||||
|
* commands and command sequences.
|
||||||
|
*
|
||||||
|
* Links:
|
||||||
|
*
|
||||||
|
* - https://www.adafruit.com/datasheets/SSD1306.pdf
|
||||||
|
* - http://www.buydisplay.com/download/manual/ER-OLED013-1_Series_Datasheet.pdf
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* This is a low level function that is not intended for general use in a
|
||||||
|
* sketch. It has been made public and documented for use by derived
|
||||||
|
* classes.
|
||||||
|
*
|
||||||
|
* \see LCDDataMode() sendLCDCommand() SPItransfer()
|
||||||
|
*/
|
||||||
|
inline void static LCDCommandMode() __attribute__((always_inline));
|
||||||
|
/** \brief
|
||||||
|
* Transfer a byte to the display.
|
||||||
|
*
|
||||||
|
* \param data The byte to be sent to the display.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* Transfer one byte to the display over the SPI port and wait for the
|
||||||
|
* transfer to complete. The byte will either be interpreted as a command
|
||||||
|
* or as data to be placed on the screen, depending on the command/data
|
||||||
|
* mode.
|
||||||
|
*
|
||||||
|
* \see LCDDataMode() LCDCommandMode() sendLCDCommand()
|
||||||
|
*/
|
||||||
|
void static SPItransfer(uint8_t data);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Turn the display off.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* The display will clear and be put into a low power mode. This can be
|
||||||
|
* used to extend battery life when a game is paused or when a sketch
|
||||||
|
* doesn't require anything to be displayed for a relatively long period
|
||||||
|
* of time.
|
||||||
|
*
|
||||||
|
* \see displayOn()
|
||||||
|
*/
|
||||||
|
void static displayOff();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Turn the display on.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* Used to power up and reinitialize the display after calling
|
||||||
|
* `displayOff()`.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* The previous call to `displayOff()` will have caused the display's
|
||||||
|
* buffer contents to be lost. The display will have to be re-painted,
|
||||||
|
* which is usually done by calling `display()`.
|
||||||
|
*
|
||||||
|
* \see displayOff()
|
||||||
|
*/
|
||||||
|
void static displayOn();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Get the width of the display in pixels.
|
||||||
|
*
|
||||||
|
* \return The width of the display in pixels.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* In most cases, the defined value `WIDTH` would be better to use instead
|
||||||
|
* of this function.
|
||||||
|
*/
|
||||||
|
uint8_t static width();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Get the height of the display in pixels.
|
||||||
|
*
|
||||||
|
* \return The height of the display in pixels.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* In most cases, the defined value `HEIGHT` would be better to use instead
|
||||||
|
* of this function.
|
||||||
|
*/
|
||||||
|
uint8_t static height();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Get the current state of all buttons as a bitmask.
|
||||||
|
*
|
||||||
|
* \return A bitmask of the state of all the buttons.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* The returned mask contains a bit for each button. For any pressed button,
|
||||||
|
* its bit will be 1. For released buttons their associated bits will be 0.
|
||||||
|
*
|
||||||
|
* The following defined mask values should be used for the buttons:
|
||||||
|
*
|
||||||
|
* LEFT_BUTTON, RIGHT_BUTTON, UP_BUTTON, DOWN_BUTTON, A_BUTTON, B_BUTTON
|
||||||
|
*/
|
||||||
|
uint8_t static buttonsState();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Paint 8 pixels vertically to the display.
|
||||||
|
*
|
||||||
|
* \param pixels A byte whose bits specify a vertical column of 8 pixels.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* A byte representing a vertical column of 8 pixels is written to the
|
||||||
|
* display at the current page and column address. The address is then
|
||||||
|
* incremented. The page/column address will wrap to the start of the
|
||||||
|
* display (the top left) when it increments past the end (lower right).
|
||||||
|
*
|
||||||
|
* The least significant bit represents the top pixel in the column.
|
||||||
|
* A bit set to 1 is lit, 0 is unlit.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* X = lit pixels, . = unlit pixels
|
||||||
|
*
|
||||||
|
* blank() paint8Pixels() 0xFF, 0, 0xF0, 0, 0x0F
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Paints an entire image directly to the display from program memory.
|
||||||
|
*
|
||||||
|
* \param image A byte array in program memory representing the entire
|
||||||
|
* contents of the display.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* The contents of the specified array in program memory is written to the
|
||||||
|
* display. Each byte in the array represents a vertical column of 8 pixels
|
||||||
|
* with the least significant bit at the top. The bytes are written starting
|
||||||
|
* at the top left, progressing horizontally and wrapping at the end of each
|
||||||
|
* row, to the bottom right. The size of the array must exactly match the
|
||||||
|
* number of pixels in the entire display.
|
||||||
|
*
|
||||||
|
* \see paint8Pixels()
|
||||||
|
*/
|
||||||
|
void static paintScreen(const uint8_t *image);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Paints an entire image directly to the display from an array in RAM.
|
||||||
|
*
|
||||||
|
* \param image A byte array in RAM representing the entire contents of
|
||||||
|
* the display.
|
||||||
|
* \param clear If `true` the array in RAM will be cleared to zeros upon
|
||||||
|
* return from this function. If `false` the RAM buffer will remain
|
||||||
|
* unchanged. (optional; defaults to `false`)
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* The contents of the specified array in RAM is written to the display.
|
||||||
|
* Each byte in the array represents a vertical column of 8 pixels with
|
||||||
|
* the least significant bit at the top. The bytes are written starting
|
||||||
|
* at the top left, progressing horizontally and wrapping at the end of
|
||||||
|
* each row, to the bottom right. The size of the array must exactly
|
||||||
|
* match the number of pixels in the entire display.
|
||||||
|
*
|
||||||
|
* If parameter `clear` is set to `true` the RAM array will be cleared to
|
||||||
|
* zeros after its contents are written to the display.
|
||||||
|
*
|
||||||
|
* \see paint8Pixels()
|
||||||
|
*/
|
||||||
|
void static paintScreen(uint8_t image[], bool clear = false);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Blank the display screen by setting all pixels off.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* All pixels on the screen will be written with a value of 0 to turn
|
||||||
|
* them off.
|
||||||
|
*/
|
||||||
|
void static blank();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Invert the entire display or set it back to normal.
|
||||||
|
*
|
||||||
|
* \param inverse `true` will invert the display. `false` will set the
|
||||||
|
* display to no-inverted.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* Calling this function with a value of `true` will set the display to
|
||||||
|
* inverted mode. A pixel with a value of 0 will be on and a pixel set to 1
|
||||||
|
* will be off.
|
||||||
|
*
|
||||||
|
* Once in inverted mode, the display will remain this way
|
||||||
|
* until it is set back to non-inverted mode by calling this function with
|
||||||
|
* `false`.
|
||||||
|
*/
|
||||||
|
void static invert(bool inverse);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Turn all display pixels on or display the buffer contents.
|
||||||
|
*
|
||||||
|
* \param on `true` turns all pixels on. `false` displays the contents
|
||||||
|
* of the hardware display buffer.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* Calling this function with a value of `true` will override the contents
|
||||||
|
* of the hardware display buffer and turn all pixels on. The contents of
|
||||||
|
* the hardware buffer will remain unchanged.
|
||||||
|
*
|
||||||
|
* Calling this function with a value of `false` will set the normal state
|
||||||
|
* of displaying the contents of the hardware display buffer.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* All pixels will be lit even if the display is in inverted mode.
|
||||||
|
*
|
||||||
|
* \see invert()
|
||||||
|
*/
|
||||||
|
void static allPixelsOn(bool on);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Flip the display vertically or set it back to normal.
|
||||||
|
*
|
||||||
|
* \param flipped `true` will set vertical flip mode. `false` will set
|
||||||
|
* normal vertical orientation.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* Calling this function with a value of `true` will cause the Y coordinate
|
||||||
|
* to start at the bottom edge of the display instead of the top,
|
||||||
|
* effectively flipping the display vertically.
|
||||||
|
*
|
||||||
|
* Once in vertical flip mode, it will remain this way until normal
|
||||||
|
* vertical mode is set by calling this function with a value of `false`.
|
||||||
|
*
|
||||||
|
* \see flipHorizontal()
|
||||||
|
*/
|
||||||
|
void static flipVertical(bool flipped);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Flip the display horizontally or set it back to normal.
|
||||||
|
*
|
||||||
|
* \param flipped `true` will set horizontal flip mode. `false` will set
|
||||||
|
* normal horizontal orientation.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* Calling this function with a value of `true` will cause the X coordinate
|
||||||
|
* to start at the left edge of the display instead of the right,
|
||||||
|
* effectively flipping the display horizontally.
|
||||||
|
*
|
||||||
|
* Once in horizontal flip mode, it will remain this way until normal
|
||||||
|
* horizontal mode is set by calling this function with a value of `false`.
|
||||||
|
*
|
||||||
|
* \see flipVertical()
|
||||||
|
*/
|
||||||
|
void static flipHorizontal(bool flipped);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Send a single command byte to the display.
|
||||||
|
*
|
||||||
|
* \param command The command byte to send to the display.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* The display will be set to command mode then the specified command
|
||||||
|
* byte will be sent. The display will then be set to data mode.
|
||||||
|
* Multi-byte commands can be sent by calling this function multiple times.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* Sending improper commands to the display can place it into invalid or
|
||||||
|
* unexpected states, possibly even causing physical damage.
|
||||||
|
*/
|
||||||
|
void static sendLCDCommand(uint8_t command);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Set the light output of the RGB LED.
|
||||||
|
*
|
||||||
|
* \param red,green,blue The brightness value for each LED.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* The RGB LED is actually individual red, green and blue LEDs placed
|
||||||
|
* very close together in a single package. By setting the brightness of
|
||||||
|
* each LED, the RGB LED can show various colors and intensities.
|
||||||
|
* The brightness of each LED can be set to a value from 0 (fully off)
|
||||||
|
* to 255 (fully on).
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* \parblock
|
||||||
|
* Certain libraries that take control of the hardware timers may interfere
|
||||||
|
* with the ability of this function to properly control the RGB LED.
|
||||||
|
*_ArduboyPlaytune_ is one such library known to do this.
|
||||||
|
* The digitalWriteRGB() function will still work properly in this case.
|
||||||
|
* \endparblock
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* \parblock
|
||||||
|
* Many of the Kickstarter Arduboys were accidentally shipped with the
|
||||||
|
* RGB LED installed incorrectly. For these units, the green LED cannot be
|
||||||
|
* lit. As long as the green led is set to off, setting the red LED will
|
||||||
|
* actually control the blue LED and setting the blue LED will actually
|
||||||
|
* control the red LED. If the green LED is turned fully on, none of the
|
||||||
|
* LEDs will light.
|
||||||
|
* \endparblock
|
||||||
|
*
|
||||||
|
* \see digitalWriteRGB()
|
||||||
|
*/
|
||||||
|
void static setRGBled(uint8_t red, uint8_t green, uint8_t blue);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Set the RGB LEDs digitally, to either fully on or fully off.
|
||||||
|
*
|
||||||
|
* \param red,green,blue Use value RGB_ON or RGB_OFF to set each LED.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* The RGB LED is actually individual red, green and blue LEDs placed
|
||||||
|
* very close together in a single package. This 3 parameter version of the
|
||||||
|
* function will set each LED either on or off, to set the RGB LED to
|
||||||
|
* 7 different colors at their highest brightness or turn it off.
|
||||||
|
*
|
||||||
|
* The colors are as follows:
|
||||||
|
*
|
||||||
|
* RED LED GREEN_LED BLUE_LED COLOR
|
||||||
|
* ------- --------- -------- -----
|
||||||
|
* RGB_OFF RGB_OFF RGB_OFF OFF
|
||||||
|
* RGB_OFF RGB_OFF RGB_ON Blue
|
||||||
|
* RGB_OFF RGB_ON RGB_OFF Green
|
||||||
|
* RGB_OFF RGB_ON RGB_ON Cyan
|
||||||
|
* RGB_ON RGB_OFF RGB_OFF Red
|
||||||
|
* RGB_ON RGB_OFF RGB_ON Magenta
|
||||||
|
* RGB_ON RGB_ON RGB_OFF Yellow
|
||||||
|
* RGB_ON RGB_ON RGB_ON White
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* Many of the Kickstarter Arduboys were accidentally shipped with the
|
||||||
|
* RGB LED installed incorrectly. For these units, the green LED cannot be
|
||||||
|
* lit. As long as the green led is set to off, turning on the red LED will
|
||||||
|
* actually light the blue LED and turning on the blue LED will actually
|
||||||
|
* light the red LED. If the green LED is turned on, none of the LEDs
|
||||||
|
* will light.
|
||||||
|
*
|
||||||
|
* \see digitalWriteRGB(uint8_t, uint8_t) setRGBled()
|
||||||
|
*/
|
||||||
|
void static digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Set one of the RGB LEDs digitally, to either fully on or fully off.
|
||||||
|
*
|
||||||
|
* \param color The name of the LED to set. The value given should be one
|
||||||
|
* of RED_LED, GREEN_LED or BLUE_LED.
|
||||||
|
*
|
||||||
|
* \param val Indicates whether to turn the specified LED on or off.
|
||||||
|
* The value given should be RGB_ON or RGB_OFF.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* This 2 parameter version of the function will set a single LED within
|
||||||
|
* the RGB LED either fully on or fully off. See the description of the
|
||||||
|
* 3 parameter version of this function for more details on the RGB LED.
|
||||||
|
*
|
||||||
|
* \see digitalWriteRGB(uint8_t, uint8_t, uint8_t) setRGBled()
|
||||||
|
*/
|
||||||
|
void static digitalWriteRGB(uint8_t color, uint8_t val);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Initialize the Arduboy's hardware.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* This function initializes the display, buttons, etc.
|
||||||
|
*
|
||||||
|
* This function is called by begin() so isn't normally called within a
|
||||||
|
* sketch. However, in order to free up some code space, by eliminating
|
||||||
|
* some of the start up features, it can be called in place of begin().
|
||||||
|
* The functions that begin() would call after boot() can then be called
|
||||||
|
* to add back in some of the start up features, if desired.
|
||||||
|
* See the README file or documentation on the main page for more details.
|
||||||
|
*
|
||||||
|
* \see Arduboy2Base::begin()
|
||||||
|
*/
|
||||||
|
void static boot();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Allow upload when the bootloader "magic number" could be corrupted.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* If the UP button is held when this function is entered, the RGB LED
|
||||||
|
* will be lit and timer 0 will be disabled, then the sketch will remain
|
||||||
|
* in a tight loop. This is to address a problem with uploading a new
|
||||||
|
* sketch, for sketches that interfere with the bootloader "magic number".
|
||||||
|
* The problem occurs with certain sketches that use large amounts of RAM.
|
||||||
|
*
|
||||||
|
* This function should be called after `boot()` in sketches that
|
||||||
|
* potentially could cause the problem.
|
||||||
|
*
|
||||||
|
* It is intended to replace the `flashlight()` function when more
|
||||||
|
* program space is required. If possible, it is more desirable to use
|
||||||
|
* `flashlight()`, so that the actual flashlight feature isn't lost.
|
||||||
|
*
|
||||||
|
* \see Arduboy2Base::flashlight() boot()
|
||||||
|
*/
|
||||||
|
void static safeMode();
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Delay for the number of milliseconds, specified as a 16 bit value.
|
||||||
|
*
|
||||||
|
* \param ms The delay in milliseconds.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* This function works the same as the Arduino `delay()` function except
|
||||||
|
* the provided value is 16 bits long, so the maximum delay allowed is
|
||||||
|
* 65535 milliseconds (about 65.5 seconds). Using this function instead
|
||||||
|
* of Arduino `delay()` will save a few bytes of code.
|
||||||
|
*/
|
||||||
|
void static delayShort(uint16_t ms) __attribute__ ((noinline));
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// internals
|
||||||
|
void static setCPUSpeed8MHz();
|
||||||
|
void static bootSPI();
|
||||||
|
void static bootOLED();
|
||||||
|
void static bootPins();
|
||||||
|
void static bootPowerSaving();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,362 @@
|
||||||
|
/**
|
||||||
|
* @file Sprites.cpp
|
||||||
|
* \brief
|
||||||
|
* A class for drawing animated sprites from image and mask bitmaps.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Sprites.h"
|
||||||
|
|
||||||
|
void Sprites::drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
|
||||||
|
const uint8_t *mask, uint8_t frame, uint8_t mask_frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, mask, mask_frame, SPRITE_MASKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, NULL, 0, SPRITE_OVERWRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK_ERASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, NULL, 0, SPRITE_IS_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
|
||||||
|
{
|
||||||
|
draw(x, y, bitmap, frame, NULL, 0, SPRITE_PLUS_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//common functions
|
||||||
|
void Sprites::draw(int16_t x, int16_t y,
|
||||||
|
const uint8_t *bitmap, uint8_t frame,
|
||||||
|
const uint8_t *mask, uint8_t sprite_frame,
|
||||||
|
uint8_t drawMode)
|
||||||
|
{
|
||||||
|
unsigned int frame_offset;
|
||||||
|
|
||||||
|
if (bitmap == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t width = pgm_read_byte(bitmap);
|
||||||
|
uint8_t height = pgm_read_byte(++bitmap);
|
||||||
|
bitmap++;
|
||||||
|
if (frame > 0 || sprite_frame > 0) {
|
||||||
|
frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));
|
||||||
|
// sprite plus mask uses twice as much space for each frame
|
||||||
|
if (drawMode == SPRITE_PLUS_MASK) {
|
||||||
|
frame_offset *= 2;
|
||||||
|
} else if (mask != NULL) {
|
||||||
|
mask += sprite_frame * frame_offset;
|
||||||
|
}
|
||||||
|
bitmap += frame * frame_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're detecting the draw mode then base it on whether a mask
|
||||||
|
// was passed as a separate object
|
||||||
|
if (drawMode == SPRITE_AUTO_MODE) {
|
||||||
|
drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawBitmap(x, y, bitmap, mask, width, height, drawMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sprites::drawBitmap(int16_t x, int16_t y,
|
||||||
|
const uint8_t *bitmap, const uint8_t *mask,
|
||||||
|
uint8_t w, uint8_t h, uint8_t draw_mode)
|
||||||
|
{
|
||||||
|
// no need to draw at all of we're offscreen
|
||||||
|
if (x + w <= 0 || x > WIDTH - 1 || y + h <= 0 || y > HEIGHT - 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (bitmap == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// xOffset technically doesn't need to be 16 bit but the math operations
|
||||||
|
// are measurably faster if it is
|
||||||
|
uint16_t xOffset, ofs;
|
||||||
|
int8_t yOffset = abs(y) % 8;
|
||||||
|
int8_t sRow = y / 8;
|
||||||
|
uint8_t loop_h, start_h, rendered_width;
|
||||||
|
|
||||||
|
if (y < 0 && yOffset > 0) {
|
||||||
|
sRow--;
|
||||||
|
yOffset = 8 - yOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the left side of the render is offscreen skip those loops
|
||||||
|
if (x < 0) {
|
||||||
|
xOffset = abs(x);
|
||||||
|
} else {
|
||||||
|
xOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the right side of the render is offscreen skip those loops
|
||||||
|
if (x + w > WIDTH - 1) {
|
||||||
|
rendered_width = ((WIDTH - x) - xOffset);
|
||||||
|
} else {
|
||||||
|
rendered_width = (w - xOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the top side of the render is offscreen skip those loops
|
||||||
|
if (sRow < -1) {
|
||||||
|
start_h = abs(sRow) - 1;
|
||||||
|
} else {
|
||||||
|
start_h = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop_h = h / 8 + (h % 8 > 0 ? 1 : 0); // divide, then round up
|
||||||
|
|
||||||
|
// if (sRow + loop_h - 1 > (HEIGHT/8)-1)
|
||||||
|
if (sRow + loop_h > (HEIGHT / 8)) {
|
||||||
|
loop_h = (HEIGHT / 8) - sRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare variables for loops later so we can compare with 0
|
||||||
|
// instead of comparing two variables
|
||||||
|
loop_h -= start_h;
|
||||||
|
|
||||||
|
sRow += start_h;
|
||||||
|
ofs = (sRow * WIDTH) + x + xOffset;
|
||||||
|
uint8_t *bofs = (uint8_t *)bitmap + (start_h * w) + xOffset;
|
||||||
|
uint8_t data;
|
||||||
|
|
||||||
|
uint8_t mul_amt = 1 << yOffset;
|
||||||
|
uint16_t mask_data;
|
||||||
|
uint16_t bitmap_data;
|
||||||
|
|
||||||
|
switch (draw_mode) {
|
||||||
|
case SPRITE_UNMASKED:
|
||||||
|
// we only want to mask the 8 bits of our own sprite, so we can
|
||||||
|
// calculate the mask before the start of the loop
|
||||||
|
mask_data = ~(0xFF * mul_amt);
|
||||||
|
// really if yOffset = 0 you have a faster case here that could be
|
||||||
|
// optimized
|
||||||
|
for (uint8_t a = 0; a < loop_h; a++) {
|
||||||
|
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
|
||||||
|
bitmap_data = pgm_read_byte(bofs) * mul_amt;
|
||||||
|
|
||||||
|
if (sRow >= 0) {
|
||||||
|
data = Arduboy2Base::sBuffer[ofs];
|
||||||
|
data &= (uint8_t)(mask_data);
|
||||||
|
data |= (uint8_t)(bitmap_data);
|
||||||
|
Arduboy2Base::sBuffer[ofs] = data;
|
||||||
|
}
|
||||||
|
if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) {
|
||||||
|
data = Arduboy2Base::sBuffer[ofs + WIDTH];
|
||||||
|
data &= (*((unsigned char *) (&mask_data) + 1));
|
||||||
|
data |= (*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
Arduboy2Base::sBuffer[ofs + WIDTH] = data;
|
||||||
|
}
|
||||||
|
ofs++;
|
||||||
|
bofs++;
|
||||||
|
}
|
||||||
|
sRow++;
|
||||||
|
bofs += w - rendered_width;
|
||||||
|
ofs += WIDTH - rendered_width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPRITE_IS_MASK:
|
||||||
|
for (uint8_t a = 0; a < loop_h; a++) {
|
||||||
|
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
|
||||||
|
bitmap_data = pgm_read_byte(bofs) * mul_amt;
|
||||||
|
if (sRow >= 0) {
|
||||||
|
Arduboy2Base::sBuffer[ofs] |= (uint8_t)(bitmap_data);
|
||||||
|
}
|
||||||
|
if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) {
|
||||||
|
Arduboy2Base::sBuffer[ofs + WIDTH] |= (*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
}
|
||||||
|
ofs++;
|
||||||
|
bofs++;
|
||||||
|
}
|
||||||
|
sRow++;
|
||||||
|
bofs += w - rendered_width;
|
||||||
|
ofs += WIDTH - rendered_width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPRITE_IS_MASK_ERASE:
|
||||||
|
for (uint8_t a = 0; a < loop_h; a++) {
|
||||||
|
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
|
||||||
|
bitmap_data = pgm_read_byte(bofs) * mul_amt;
|
||||||
|
if (sRow >= 0) {
|
||||||
|
Arduboy2Base::sBuffer[ofs] &= ~(uint8_t)(bitmap_data);
|
||||||
|
}
|
||||||
|
if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) {
|
||||||
|
Arduboy2Base::sBuffer[ofs + WIDTH] &= ~(*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
}
|
||||||
|
ofs++;
|
||||||
|
bofs++;
|
||||||
|
}
|
||||||
|
sRow++;
|
||||||
|
bofs += w - rendered_width;
|
||||||
|
ofs += WIDTH - rendered_width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPRITE_MASKED:
|
||||||
|
uint8_t *mask_ofs;
|
||||||
|
mask_ofs = (uint8_t *)mask + (start_h * w) + xOffset;
|
||||||
|
for (uint8_t a = 0; a < loop_h; a++) {
|
||||||
|
for (uint8_t iCol = 0; iCol < rendered_width; iCol++) {
|
||||||
|
// NOTE: you might think in the yOffset==0 case that this results
|
||||||
|
// in more effort, but in all my testing the compiler was forcing
|
||||||
|
// 16-bit math to happen here anyways, so this isn't actually
|
||||||
|
// compiling to more code than it otherwise would. If the offset
|
||||||
|
// is 0 the high part of the word will just never be used.
|
||||||
|
|
||||||
|
// load data and bit shift
|
||||||
|
// mask needs to be bit flipped
|
||||||
|
mask_data = ~(pgm_read_byte(mask_ofs) * mul_amt);
|
||||||
|
bitmap_data = pgm_read_byte(bofs) * mul_amt;
|
||||||
|
|
||||||
|
if (sRow >= 0) {
|
||||||
|
data = Arduboy2Base::sBuffer[ofs];
|
||||||
|
data &= (uint8_t)(mask_data);
|
||||||
|
data |= (uint8_t)(bitmap_data);
|
||||||
|
Arduboy2Base::sBuffer[ofs] = data;
|
||||||
|
}
|
||||||
|
if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) {
|
||||||
|
data = Arduboy2Base::sBuffer[ofs + WIDTH];
|
||||||
|
data &= (*((unsigned char *) (&mask_data) + 1));
|
||||||
|
data |= (*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
Arduboy2Base::sBuffer[ofs + WIDTH] = data;
|
||||||
|
}
|
||||||
|
ofs++;
|
||||||
|
mask_ofs++;
|
||||||
|
bofs++;
|
||||||
|
}
|
||||||
|
sRow++;
|
||||||
|
bofs += w - rendered_width;
|
||||||
|
mask_ofs += w - rendered_width;
|
||||||
|
ofs += WIDTH - rendered_width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case SPRITE_PLUS_MASK:
|
||||||
|
// *2 because we use double the bits (mask + bitmap)
|
||||||
|
bofs = (uint8_t *)(bitmap + ((start_h * w) + xOffset) * 2);
|
||||||
|
|
||||||
|
uint8_t xi = rendered_width; // counter for x loop below
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
"push r28\n" // save Y
|
||||||
|
"push r29\n"
|
||||||
|
"movw r28, %[buffer_ofs]\n" // Y = buffer_ofs_2
|
||||||
|
"subi r28, %[neg_width]\n" // buffer_ofs_2 = buffer_ofs + WIDTH
|
||||||
|
"sbci r29, -1\n"
|
||||||
|
"loop_y:\n"
|
||||||
|
"loop_x:\n"
|
||||||
|
// load bitmap and mask data
|
||||||
|
"lpm %A[bitmap_data], Z+\n"
|
||||||
|
"lpm %A[mask_data], Z+\n"
|
||||||
|
|
||||||
|
// shift mask and buffer data
|
||||||
|
"tst %[yOffset]\n"
|
||||||
|
"breq skip_shifting\n"
|
||||||
|
"mul %A[bitmap_data], %[mul_amt]\n"
|
||||||
|
"movw %[bitmap_data], r0\n"
|
||||||
|
"mul %A[mask_data], %[mul_amt]\n"
|
||||||
|
"movw %[mask_data], r0\n"
|
||||||
|
|
||||||
|
// SECOND PAGE
|
||||||
|
// if yOffset != 0 && sRow < ((HEIGHT / 8) - 1)
|
||||||
|
"cpi %[sRow], %[row_height]\n"
|
||||||
|
"brge end_second_page\n"
|
||||||
|
// then
|
||||||
|
"ld %[data], Y\n"
|
||||||
|
"com %B[mask_data]\n" // invert high byte of mask
|
||||||
|
"and %[data], %B[mask_data]\n"
|
||||||
|
"or %[data], %B[bitmap_data]\n"
|
||||||
|
// update buffer, increment
|
||||||
|
"st Y+, %[data]\n"
|
||||||
|
|
||||||
|
"end_second_page:\n"
|
||||||
|
"skip_shifting:\n"
|
||||||
|
|
||||||
|
// FIRST PAGE
|
||||||
|
// if sRow >= 0
|
||||||
|
"tst %[sRow]\n"
|
||||||
|
"brmi skip_first_page\n"
|
||||||
|
"ld %[data], %a[buffer_ofs]\n"
|
||||||
|
// then
|
||||||
|
"com %A[mask_data]\n"
|
||||||
|
"and %[data], %A[mask_data]\n"
|
||||||
|
"or %[data], %A[bitmap_data]\n"
|
||||||
|
// update buffer, increment
|
||||||
|
"st %a[buffer_ofs]+, %[data]\n"
|
||||||
|
"jmp end_first_page\n"
|
||||||
|
|
||||||
|
"skip_first_page:\n"
|
||||||
|
// since no ST Z+ when skipped we need to do this manually
|
||||||
|
"adiw %[buffer_ofs], 1\n"
|
||||||
|
|
||||||
|
"end_first_page:\n"
|
||||||
|
|
||||||
|
// "x_loop_next:\n"
|
||||||
|
"dec %[xi]\n"
|
||||||
|
"brne loop_x\n"
|
||||||
|
|
||||||
|
// increment y
|
||||||
|
"next_loop_y:\n"
|
||||||
|
"dec %[yi]\n"
|
||||||
|
"breq finished\n"
|
||||||
|
"mov %[xi], %[x_count]\n" // reset x counter
|
||||||
|
// sRow++;
|
||||||
|
"inc %[sRow]\n"
|
||||||
|
"clr __zero_reg__\n"
|
||||||
|
// sprite_ofs += (w - rendered_width) * 2;
|
||||||
|
"add %A[sprite_ofs], %A[sprite_ofs_jump]\n"
|
||||||
|
"adc %B[sprite_ofs], __zero_reg__\n"
|
||||||
|
// buffer_ofs += WIDTH - rendered_width;
|
||||||
|
"add %A[buffer_ofs], %A[buffer_ofs_jump]\n"
|
||||||
|
"adc %B[buffer_ofs], __zero_reg__\n"
|
||||||
|
// buffer_ofs_page_2 += WIDTH - rendered_width;
|
||||||
|
"add r28, %A[buffer_ofs_jump]\n"
|
||||||
|
"adc r29, __zero_reg__\n"
|
||||||
|
|
||||||
|
"rjmp loop_y\n"
|
||||||
|
"finished:\n"
|
||||||
|
// put the Y register back in place
|
||||||
|
"pop r29\n"
|
||||||
|
"pop r28\n"
|
||||||
|
"clr __zero_reg__\n" // just in case
|
||||||
|
: [xi] "+&a" (xi),
|
||||||
|
[yi] "+&a" (loop_h),
|
||||||
|
[sRow] "+&a" (sRow), // CPI requires an upper register (r16-r23)
|
||||||
|
[data] "=&l" (data),
|
||||||
|
[mask_data] "=&l" (mask_data),
|
||||||
|
[bitmap_data] "=&l" (bitmap_data)
|
||||||
|
:
|
||||||
|
[screen_width] "M" (WIDTH),
|
||||||
|
[x_count] "l" (rendered_width), // lower register
|
||||||
|
[sprite_ofs] "z" (bofs),
|
||||||
|
[buffer_ofs] "x" (Arduboy2Base::sBuffer+ofs),
|
||||||
|
[buffer_ofs_jump] "a" (WIDTH-rendered_width), // upper reg (r16-r23)
|
||||||
|
[sprite_ofs_jump] "a" ((w-rendered_width)*2), // upper reg (r16-r23)
|
||||||
|
|
||||||
|
// [sprite_ofs_jump] "r" (0),
|
||||||
|
[yOffset] "l" (yOffset), // lower register
|
||||||
|
[mul_amt] "l" (mul_amt), // lower register
|
||||||
|
[neg_width] "M" (256 - WIDTH),
|
||||||
|
[row_height] "M" ((HEIGHT / 8) - 1)
|
||||||
|
// NOTE: We also clobber r28 and r29 (y) but sometimes the compiler
|
||||||
|
// won't allow us, so in order to make this work we don't tell it
|
||||||
|
// that we clobber them. Instead, we push/pop to preserve them.
|
||||||
|
// Then we need to guarantee that the the compiler doesn't put one of
|
||||||
|
// our own variables into r28/r29.
|
||||||
|
// We do that by specifying all the inputs and outputs use either
|
||||||
|
// lower registers (l) or simple (r16-r23) upper registers (a).
|
||||||
|
: // pushes/clobbers/pops r28 and r29 (y)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
/**
|
||||||
|
* @file Sprites.h
|
||||||
|
* \brief
|
||||||
|
* A class for drawing animated sprites from image and mask bitmaps.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Sprites_h
|
||||||
|
#define Sprites_h
|
||||||
|
|
||||||
|
#include "Arduboy2.h"
|
||||||
|
|
||||||
|
#define SPRITE_MASKED 1
|
||||||
|
#define SPRITE_UNMASKED 2
|
||||||
|
#define SPRITE_OVERWRITE 2
|
||||||
|
#define SPRITE_PLUS_MASK 3
|
||||||
|
#define SPRITE_IS_MASK 250
|
||||||
|
#define SPRITE_IS_MASK_ERASE 251
|
||||||
|
#define SPRITE_AUTO_MODE 255
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* A class for drawing animated sprites from image and mask bitmaps.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* The functions in this class will draw to the screen buffer an image
|
||||||
|
* contained in an array located in program memory. A mask can also be
|
||||||
|
* specified or implied, which dictates how existing pixels in the buffer,
|
||||||
|
* within the image boundaries, will be affected.
|
||||||
|
*
|
||||||
|
* A sprite or mask array contains one or more "frames". Each frame is intended
|
||||||
|
* to show whatever the sprite represents in a different position, such as the
|
||||||
|
* various poses for a running or jumping character. By specifying a different
|
||||||
|
* frame each time the sprite is drawn, it can be animated.
|
||||||
|
*
|
||||||
|
* Each image array begins with values for the width and height of the sprite,
|
||||||
|
* in pixels. The width can be any value. The height must be a multiple of
|
||||||
|
* 8 pixels, but with proper masking, a sprite of any height can be created.
|
||||||
|
*
|
||||||
|
* For a separate mask array, as is used with `drawExternalMask()`, the width
|
||||||
|
* and height are not included but must contain data of the same dimensions
|
||||||
|
* as the corresponding image array.
|
||||||
|
*
|
||||||
|
* Following the width and height values for an image array, or the from the
|
||||||
|
* beginning of a separate mask array, the array contains the image and/or
|
||||||
|
* mask data for each frame. Each byte represents a vertical column of 8 pixels
|
||||||
|
* with the least significant bit (bit 0) at the top. The bytes are drawn as
|
||||||
|
* 8 pixel high rows from left to right, top to bottom. When the end of a row
|
||||||
|
* is reached, as specified by the width value, the next byte in the array will
|
||||||
|
* be the start of the next row.
|
||||||
|
*
|
||||||
|
* Data for each frame after the first one immediately follows the previous
|
||||||
|
* frame. Frame numbers start at 0.
|
||||||
|
*/
|
||||||
|
class Sprites
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** \brief
|
||||||
|
* Draw a sprite using a separate image and mask array.
|
||||||
|
*
|
||||||
|
* \param x,y The coordinates of the top left pixel location.
|
||||||
|
* \param bitmap A pointer to the array containing the image frames.
|
||||||
|
* \param mask A pointer to the array containing the mask frames.
|
||||||
|
* \param frame The frame number of the image to draw.
|
||||||
|
* \param mask_frame The frame number for the mask to use (can be different
|
||||||
|
* from the image frame number).
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* An array containing the image frames, and another array containing
|
||||||
|
* corresponding mask frames, are used to draw a sprite.
|
||||||
|
*
|
||||||
|
* Bits set to 1 in the mask indicate that the pixel will be set to the
|
||||||
|
* value of the corresponding image bit. Bits set to 0 in the mask will be
|
||||||
|
* left unchanged.
|
||||||
|
*
|
||||||
|
* image mask before after
|
||||||
|
*
|
||||||
|
* ..... .OOO. ..... .....
|
||||||
|
* ..O.. OOOOO ..... ..O..
|
||||||
|
* OO.OO OO.OO ..... OO.OO
|
||||||
|
* ..O.. OOOOO ..... ..O..
|
||||||
|
* ..... .OOO. ..... .....
|
||||||
|
*
|
||||||
|
* image mask before after
|
||||||
|
*
|
||||||
|
* ..... .OOO. OOOOO O...O
|
||||||
|
* ..O.. OOOOO OOOOO ..O..
|
||||||
|
* OO.OO OOOOO OOOOO OO.OO
|
||||||
|
* ..O.. OOOOO OOOOO ..O..
|
||||||
|
* ..... .OOO. OOOOO O...O
|
||||||
|
*/
|
||||||
|
static void drawExternalMask(int16_t x, int16_t y, const uint8_t *bitmap,
|
||||||
|
const uint8_t *mask, uint8_t frame, uint8_t mask_frame);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Draw a sprite using an array containing both image and mask values.
|
||||||
|
*
|
||||||
|
* \param x,y The coordinates of the top left pixel location.
|
||||||
|
* \param bitmap A pointer to the array containing the image/mask frames.
|
||||||
|
* \param frame The frame number of the image to draw.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* An array containing combined image and mask data is used to draw a
|
||||||
|
* sprite. Bytes are given in pairs with the first byte representing the
|
||||||
|
* image pixels and the second byte specifying the corresponding mask.
|
||||||
|
* The width given in the array still specifies the image width, so each
|
||||||
|
* row of image and mask bytes will be twice the width value.
|
||||||
|
*
|
||||||
|
* Bits set to 1 in the mask indicate that the pixel will be set to the
|
||||||
|
* value of the corresponding image bit. Bits set to 0 in the mask will be
|
||||||
|
* left unchanged.
|
||||||
|
*
|
||||||
|
* image mask before after
|
||||||
|
*
|
||||||
|
* ..... .OOO. ..... .....
|
||||||
|
* ..O.. OOOOO ..... ..O..
|
||||||
|
* OO.OO OO.OO ..... OO.OO
|
||||||
|
* ..O.. OOOOO ..... ..O..
|
||||||
|
* ..... .OOO. ..... .....
|
||||||
|
*
|
||||||
|
* image mask before after
|
||||||
|
*
|
||||||
|
* ..... .OOO. OOOOO O...O
|
||||||
|
* ..O.. OOOOO OOOOO ..O..
|
||||||
|
* OO.OO OOOOO OOOOO OO.OO
|
||||||
|
* ..O.. OOOOO OOOOO ..O..
|
||||||
|
* ..... .OOO. OOOOO O...O
|
||||||
|
*/
|
||||||
|
static void drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Draw a sprite by replacing the existing content completely.
|
||||||
|
*
|
||||||
|
* \param x,y The coordinates of the top left pixel location.
|
||||||
|
* \param bitmap A pointer to the array containing the image frames.
|
||||||
|
* \param frame The frame number of the image to draw.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* A sprite is drawn by overwriting the pixels in the buffer with the data
|
||||||
|
* from the specified frame in the array. No masking is done. A bit set
|
||||||
|
* to 1 in the frame will set the pixel to 1 in the buffer, and a 0 in the
|
||||||
|
* array will set a 0 in the buffer.
|
||||||
|
*
|
||||||
|
* image before after
|
||||||
|
*
|
||||||
|
* ..... ..... .....
|
||||||
|
* ..O.. ..... ..O..
|
||||||
|
* OO.OO ..... OO.OO
|
||||||
|
* ..O.. ..... ..O..
|
||||||
|
* ..... ..... .....
|
||||||
|
*
|
||||||
|
* image before after
|
||||||
|
*
|
||||||
|
* ..... OOOOO .....
|
||||||
|
* ..O.. OOOOO ..O..
|
||||||
|
* OO.OO OOOOO OO.OO
|
||||||
|
* ..O.. OOOOO ..O..
|
||||||
|
* ..... OOOOO .....
|
||||||
|
*/
|
||||||
|
static void drawOverwrite(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* "Erase" a sprite.
|
||||||
|
*
|
||||||
|
* \param x,y The coordinates of the top left pixel location.
|
||||||
|
* \param bitmap A pointer to the array containing the image frames.
|
||||||
|
* \param frame The frame number of the image to erase.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* The data from the specified frame in the array is used to erase a
|
||||||
|
* sprite. To "erase" a sprite, bits set to 1 in the frame will set the
|
||||||
|
* corresponding pixel in the buffer to 0. Frame bits set to 0 will remain
|
||||||
|
* unchanged in the buffer.
|
||||||
|
*
|
||||||
|
* image before after
|
||||||
|
*
|
||||||
|
* ..... ..... .....
|
||||||
|
* ..O.. ..... .....
|
||||||
|
* OO.OO ..... .....
|
||||||
|
* ..O.. ..... .....
|
||||||
|
* ..... ..... .....
|
||||||
|
*
|
||||||
|
* image before after
|
||||||
|
*
|
||||||
|
* ..... OOOOO OOOOO
|
||||||
|
* ..O.. OOOOO OO.OO
|
||||||
|
* OO.OO OOOOO ..O..
|
||||||
|
* ..O.. OOOOO OO.OO
|
||||||
|
* ..... OOOOO OOOOO
|
||||||
|
*/
|
||||||
|
static void drawErase(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
|
||||||
|
|
||||||
|
/** \brief
|
||||||
|
* Draw a sprite using only the bits set to 1.
|
||||||
|
*
|
||||||
|
* \param x,y The coordinates of the top left pixel location.
|
||||||
|
* \param bitmap A pointer to the array containing the image frames.
|
||||||
|
* \param frame The frame number of the image to draw.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* Bits set to 1 in the frame will be used to draw the sprite by setting
|
||||||
|
* the corresponding pixel in the buffer to 1. Bits set to 0 in the frame
|
||||||
|
* will remain unchanged in the buffer.
|
||||||
|
*
|
||||||
|
* image before after
|
||||||
|
*
|
||||||
|
* ..... ..... .....
|
||||||
|
* ..O.. ..... ..O..
|
||||||
|
* OO.OO ..... OO.OO
|
||||||
|
* ..O.. ..... ..O..
|
||||||
|
* ..... ..... .....
|
||||||
|
*
|
||||||
|
* image before after
|
||||||
|
*
|
||||||
|
* ..... OOOOO OOOOO (no change because all pixels were
|
||||||
|
* ..O.. OOOOO OOOOO already white)
|
||||||
|
* OO.OO OOOOO OOOOO
|
||||||
|
* ..O.. OOOOO OOOOO
|
||||||
|
* ..... OOOOO OOOOO
|
||||||
|
*/
|
||||||
|
static void drawSelfMasked(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame);
|
||||||
|
|
||||||
|
// Master function. Needs to be abstracted into separate function for
|
||||||
|
// every render type.
|
||||||
|
// (Not officially part of the API)
|
||||||
|
static void draw(int16_t x, int16_t y,
|
||||||
|
const uint8_t *bitmap, uint8_t frame,
|
||||||
|
const uint8_t *mask, uint8_t sprite_frame,
|
||||||
|
uint8_t drawMode);
|
||||||
|
|
||||||
|
// (Not officially part of the API)
|
||||||
|
static void drawBitmap(int16_t x, int16_t y,
|
||||||
|
const uint8_t *bitmap, const uint8_t *mask,
|
||||||
|
uint8_t w, uint8_t h, uint8_t draw_mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue