mirror of https://github.com/MLXXXp/Arduboy2.git
Multiple OLED display support
added multiple OLED display support added flexible width and height support optimized buttonsState with optional bootlkey support
This commit is contained in:
parent
d42c0d77c0
commit
86185a5688
|
@ -109,7 +109,7 @@ void Arduboy2Base::bootLogo()
|
||||||
|
|
||||||
void Arduboy2Base::drawLogoBitmap(int16_t y)
|
void Arduboy2Base::drawLogoBitmap(int16_t y)
|
||||||
{
|
{
|
||||||
drawBitmap(20, y, arduboy_logo, 88, 16);
|
drawBitmap(20 - (64 - WIDTH / 2), y, arduboy_logo, 88, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Arduboy2Base::bootLogoCompressed()
|
void Arduboy2Base::bootLogoCompressed()
|
||||||
|
@ -119,7 +119,7 @@ void Arduboy2Base::bootLogoCompressed()
|
||||||
|
|
||||||
void Arduboy2Base::drawLogoCompressed(int16_t y)
|
void Arduboy2Base::drawLogoCompressed(int16_t y)
|
||||||
{
|
{
|
||||||
drawCompressed(20, y, arduboy_logo_compressed);
|
drawCompressed(20 - (64 - WIDTH / 2), y, arduboy_logo_compressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Arduboy2Base::bootLogoSpritesSelfMasked()
|
void Arduboy2Base::bootLogoSpritesSelfMasked()
|
||||||
|
@ -129,7 +129,7 @@ void Arduboy2Base::bootLogoSpritesSelfMasked()
|
||||||
|
|
||||||
void Arduboy2Base::drawLogoSpritesSelfMasked(int16_t y)
|
void Arduboy2Base::drawLogoSpritesSelfMasked(int16_t y)
|
||||||
{
|
{
|
||||||
Sprites::drawSelfMasked(20, y, arduboy_logo_sprite, 0);
|
Sprites::drawSelfMasked(20 - (64 - WIDTH / 2), y, arduboy_logo_sprite, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Arduboy2Base::bootLogoSpritesOverwrite()
|
void Arduboy2Base::bootLogoSpritesOverwrite()
|
||||||
|
@ -139,7 +139,7 @@ void Arduboy2Base::bootLogoSpritesOverwrite()
|
||||||
|
|
||||||
void Arduboy2Base::drawLogoSpritesOverwrite(int16_t y)
|
void Arduboy2Base::drawLogoSpritesOverwrite(int16_t y)
|
||||||
{
|
{
|
||||||
Sprites::drawOverwrite(20, y, arduboy_logo_sprite, 0);
|
Sprites::drawOverwrite(20 - (64 - WIDTH / 2), y, arduboy_logo_sprite, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// bootLogoText() should be kept in sync with bootLogoShell()
|
// bootLogoText() should be kept in sync with bootLogoShell()
|
||||||
|
@ -306,25 +306,27 @@ void Arduboy2Base::drawPixel(int16_t x, int16_t y, uint8_t color)
|
||||||
|
|
||||||
asm volatile
|
asm volatile
|
||||||
(
|
(
|
||||||
"mul %[width_offset], %A[y]\n"
|
"mov r16,%A[y] \n"
|
||||||
"movw %[row_offset], r0\n"
|
"andi r16, 0xF8 \n"
|
||||||
"andi %A[row_offset], 0x80\n" // row_offset &= (~0b01111111);
|
"mul %[width_offset], r16 \n"
|
||||||
"clr __zero_reg__\n"
|
"movw %[row_offset], r0 \n"
|
||||||
"add %A[row_offset], %[x]\n"
|
"clr __zero_reg__ \n"
|
||||||
// mask for only 0-7
|
"add %A[row_offset], %[x] \n"
|
||||||
"andi %A[y], 0x07\n"
|
"adc %B[row_offset], __zero_reg__ \n"
|
||||||
// Z += y
|
// mask for only 0-7
|
||||||
"add r30, %A[y]\n"
|
"andi %A[y], 0x07 \n"
|
||||||
"adc r31, __zero_reg__\n"
|
// Z += y
|
||||||
|
"add r30, %A[y] \n"
|
||||||
|
"adc r31, __zero_reg__ \n"
|
||||||
// load correct bitshift from program RAM
|
// load correct bitshift from program RAM
|
||||||
"lpm %[bit], Z\n"
|
"lpm %[bit], Z \n"
|
||||||
: [row_offset] "=&x" (row_offset), // upper register (ANDI)
|
: [row_offset] "=&x" (row_offset), // upper register (ANDI)
|
||||||
[bit] "=r" (bit),
|
[bit] "=r" (bit),
|
||||||
[y] "+d" (y), // upper register (ANDI), must be writable
|
[y] "+d" (y), // upper register (ANDI), must be writable
|
||||||
"+z" (bsl) // is modified to point to the proper shift array element
|
"+z" (bsl) // is modified to point to the proper shift array element
|
||||||
: [width_offset] "r" ((uint8_t)(WIDTH/8)),
|
: [width_offset] "r" ((uint8_t)(WIDTH/8)),
|
||||||
[x] "r" ((uint8_t)x)
|
[x] "r" ((uint8_t)x)
|
||||||
:
|
: "r16"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (color) {
|
if (color) {
|
||||||
|
@ -624,24 +626,27 @@ void Arduboy2Base::fillScreen(uint8_t color)
|
||||||
// if value is zero, skip assigning to 0xff
|
// if value is zero, skip assigning to 0xff
|
||||||
"cpse %[color], __zero_reg__\n"
|
"cpse %[color], __zero_reg__\n"
|
||||||
"ldi %[color], 0xFF\n"
|
"ldi %[color], 0xFF\n"
|
||||||
// counter = 0
|
// counter = WIDTH * HEIGHT / 8 / 8
|
||||||
"clr __tmp_reg__\n"
|
"ldi r24, %[cnt]\n"
|
||||||
"loopto:\n"
|
"loopto:\n"
|
||||||
// (4x) push zero into screen buffer,
|
// (8x) push zero into screen buffer,
|
||||||
// then increment buffer position
|
// then increment buffer position
|
||||||
"st Z+, %[color]\n"
|
"st Z+, %[color]\n"
|
||||||
"st Z+, %[color]\n"
|
"st Z+, %[color]\n"
|
||||||
"st Z+, %[color]\n"
|
"st Z+, %[color]\n"
|
||||||
"st Z+, %[color]\n"
|
"st Z+, %[color]\n"
|
||||||
// increase counter
|
"st Z+, %[color]\n"
|
||||||
"inc __tmp_reg__\n"
|
"st Z+, %[color]\n"
|
||||||
// repeat for 256 loops
|
"st Z+, %[color]\n"
|
||||||
// (until counter rolls over back to 0)
|
"st Z+, %[color]\n"
|
||||||
|
// decrease counter
|
||||||
|
"subi r24, 1\n"
|
||||||
|
// repeat for 128, 144 or 192 loops depending on screen resolution
|
||||||
"brne loopto\n"
|
"brne loopto\n"
|
||||||
: [color] "+d" (color),
|
: [color] "+d" (color),
|
||||||
"+z" (bPtr)
|
"+z" (bPtr)
|
||||||
:
|
: [cnt] "M" (WIDTH * HEIGHT / 8 / 8)
|
||||||
:
|
: "r24"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1140,9 +1145,9 @@ void Arduboy2::bootLogoText()
|
||||||
}
|
}
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
cursor_x = 23;
|
cursor_x = 23 - (64 - WIDTH / 2);
|
||||||
cursor_y = y;
|
cursor_y = y;
|
||||||
print("ARDUBOY");
|
print(F("ARDUBOY"));
|
||||||
display();
|
display();
|
||||||
delayShort(27);
|
delayShort(27);
|
||||||
// longer delay post boot, we put it inside the loop to
|
// longer delay post boot, we put it inside the loop to
|
||||||
|
@ -1173,7 +1178,7 @@ void Arduboy2::bootLogoExtra()
|
||||||
if (c != 0xFF && c != 0x00)
|
if (c != 0xFF && c != 0x00)
|
||||||
{
|
{
|
||||||
uint8_t i = EEPROM_UNIT_NAME;
|
uint8_t i = EEPROM_UNIT_NAME;
|
||||||
cursor_x = 50;
|
cursor_x = 50 - (64 - WIDTH / 2);
|
||||||
cursor_y = 56;
|
cursor_y = 56;
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|
|
@ -5,12 +5,41 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Arduboy2Core.h"
|
#include "Arduboy2Core.h"
|
||||||
|
#include <avr/wdt.h>
|
||||||
|
|
||||||
const uint8_t PROGMEM lcdBootProgram[] = {
|
const uint8_t PROGMEM lcdBootProgram[] = {
|
||||||
// boot defaults are commented out but left here in case they
|
// boot defaults are commented out but left here in case they
|
||||||
// might prove useful for reference
|
// might prove useful for reference
|
||||||
//
|
|
||||||
// Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf
|
// 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
|
// Display Off
|
||||||
// 0xAE,
|
// 0xAE,
|
||||||
|
@ -68,8 +97,12 @@ const uint8_t PROGMEM lcdBootProgram[] = {
|
||||||
|
|
||||||
// set page address range
|
// set page address range
|
||||||
// 0x22, 0x00, PAGE_ADDRESS_END
|
// 0x22, 0x00, PAGE_ADDRESS_END
|
||||||
};
|
#endif
|
||||||
|
|
||||||
|
#if defined OLED_SSD1309 //required additionally for SSD1309
|
||||||
|
0x21, 0x00, COLUMN_ADDRESS_END
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
Arduboy2Core::Arduboy2Core() { }
|
Arduboy2Core::Arduboy2Core() { }
|
||||||
|
|
||||||
|
@ -113,25 +146,39 @@ void Arduboy2Core::bootPins()
|
||||||
#ifdef ARDUBOY_10
|
#ifdef ARDUBOY_10
|
||||||
|
|
||||||
// Port B INPUT_PULLUP or HIGH
|
// Port B INPUT_PULLUP or HIGH
|
||||||
PORTB |= _BV(RED_LED_BIT) | _BV(GREEN_LED_BIT) | _BV(BLUE_LED_BIT) |
|
PORTB |= _BV(RED_LED_BIT) |
|
||||||
_BV(B_BUTTON_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 INPUT or LOW (none)
|
||||||
// Port B inputs
|
// Port B inputs
|
||||||
DDRB &= ~(_BV(B_BUTTON_BIT));
|
DDRB &= ~(_BV(B_BUTTON_BIT));
|
||||||
// Port B outputs
|
// Port B outputs
|
||||||
DDRB |= _BV(RED_LED_BIT) | _BV(GREEN_LED_BIT) | _BV(BLUE_LED_BIT) |
|
DDRB |= _BV(RED_LED_BIT) |
|
||||||
_BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT);
|
#ifndef ARDUINO_AVR_PROMICRO
|
||||||
|
_BV(GREEN_LED_BIT) |
|
||||||
|
#endif
|
||||||
|
_BV(BLUE_LED_BIT) | _BV(SPI_MOSI_BIT) | _BV(SPI_SCK_BIT);
|
||||||
|
|
||||||
// Port C
|
// Port C
|
||||||
// Speaker: Not set here. Controlled by audio class
|
// Speaker: Not set here. Controlled by audio class
|
||||||
|
|
||||||
// Port D INPUT_PULLUP or HIGH
|
// Port D INPUT_PULLUP or HIGH
|
||||||
PORTD |= _BV(CS_BIT);
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
|
PORTD |= _BV(CS_BIT) | _BV(GREEN_LED_BIT);
|
||||||
|
#else
|
||||||
|
PORTD |= _BV(CS_BIT);
|
||||||
|
#endif
|
||||||
// Port D INPUT or LOW
|
// Port D INPUT or LOW
|
||||||
PORTD &= ~(_BV(RST_BIT));
|
PORTD &= ~(_BV(RST_BIT));
|
||||||
// Port D inputs (none)
|
// Port D inputs (none)
|
||||||
// Port D outputs
|
// Port D outputs
|
||||||
DDRD |= _BV(RST_BIT) | _BV(CS_BIT) | _BV(DC_BIT);
|
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
|
// Port E INPUT_PULLUP or HIGH
|
||||||
PORTE |= _BV(A_BUTTON_BIT);
|
PORTE |= _BV(A_BUTTON_BIT);
|
||||||
|
@ -305,55 +352,212 @@ void Arduboy2Core::paint8Pixels(uint8_t pixels)
|
||||||
|
|
||||||
void Arduboy2Core::paintScreen(const uint8_t *image)
|
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++)
|
for (int i = 0; i < (HEIGHT*WIDTH)/8; i++)
|
||||||
{
|
{
|
||||||
SPItransfer(pgm_read_byte(image + i));
|
SPItransfer(pgm_read_byte(image + i));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint from a memory buffer, this should be FAST as it's likely what
|
// paint from a memory buffer, this should be FAST as it's likely what
|
||||||
// will be used by any buffer based subclass
|
// will be used by any buffer based subclass
|
||||||
void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
|
void Arduboy2Core::paintScreen(uint8_t image[], bool clear)
|
||||||
{
|
{
|
||||||
uint8_t c;
|
#ifdef OLED_SH1106
|
||||||
int i = 0;
|
//Assembly optimized page mode display code with clear support.
|
||||||
|
//Each byte transfer takes 18 cycles
|
||||||
if (clear)
|
asm volatile (
|
||||||
{
|
" ldi r25, %[page_cmd] \n\t"
|
||||||
SPDR = image[i]; // set the first SPI data byte to get things started
|
".l1: \n\t"
|
||||||
image[i++] = 0; // clear the first image byte
|
" ldi r24, %[width] ;1 \n\t"
|
||||||
}
|
" ldi r20,5 ;1 \n\t"
|
||||||
else
|
" cbi %[dc_port], %[dc_bit] ;2 cmd mode \n\t"
|
||||||
SPDR = image[i++];
|
" out %[spdr], r25 ;1 \n\t"
|
||||||
|
".l2: subi r20,1 ;r20*3-1 : 14 \n\t"
|
||||||
// the code to iterate the loop and get the next byte from the buffer is
|
" brne .l2 \n\t"
|
||||||
// executed while the previous byte is being sent out by the SPI controller
|
" rjmp .+0 ;2 \n\t"
|
||||||
while (i < (HEIGHT * WIDTH) / 8)
|
" ldi r20,%[col_cmd] ;1 \n\t"
|
||||||
{
|
" out %[spdr], r20 ;1 \n\t"
|
||||||
// get the next byte. It's put in a local variable so it can be sent as
|
".l3: rjmp .l7 ;2 \n\t"
|
||||||
// as soon as possible after the sending of the previous byte has completed
|
".l4: ld r20, Z ;2 \n\t"
|
||||||
if (clear)
|
" cp %[clear], __zero_reg__ ;1 \n\t"
|
||||||
{
|
" brne .l5 ;1/2 \n\t"
|
||||||
c = image[i];
|
" nop ;1 \n\t"
|
||||||
// clear the byte in the image buffer
|
" rjmp .l6 ;2 \n\t"
|
||||||
image[i++] = 0;
|
".l5: st Z, __zero_reg__ ;2 : 7 \n\t"
|
||||||
}
|
".l6: sbi %[dc_port], %[dc_bit] ;2 data mode \n\t"
|
||||||
else
|
" out %[spdr], r20 ;1 \n\t"
|
||||||
c = image[i++];
|
" adiw r30, 1 ;2 \n\t"
|
||||||
|
".l7: rjmp .+0 ;2 \n\t"
|
||||||
while (!(SPSR & _BV(SPIF))) { } // wait for the previous byte to be sent
|
" nop ;1 \n\t"
|
||||||
|
" subi r24, 1 ;1 \n\t"
|
||||||
// put the next byte in the SPI data register. The SPI controller will
|
" brne .l4 ;1/2 : 5/6 \n\t"
|
||||||
// clock it out while the loop continues and gets the next byte ready
|
" rjmp .+0 ;2 \n\t"
|
||||||
SPDR = c;
|
" subi r25, -1 ;1 \n\t"
|
||||||
}
|
" cpi r25,%[page_end] ;1 \n\t"
|
||||||
while (!(SPSR & _BV(SPIF))) { } // wait for the last byte to be sent
|
" 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()
|
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++)
|
for (int i = 0; i < (HEIGHT*WIDTH)/8; i++)
|
||||||
SPItransfer(0x00);
|
SPItransfer(0x00);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Arduboy2Core::sendLCDCommand(uint8_t command)
|
void Arduboy2Core::sendLCDCommand(uint8_t command)
|
||||||
|
@ -455,16 +659,30 @@ uint8_t Arduboy2Core::buttonsState()
|
||||||
// down, left, up
|
// down, left, up
|
||||||
buttons = ((~PINB) & B01110000);
|
buttons = ((~PINB) & B01110000);
|
||||||
// right button
|
// right button
|
||||||
buttons = buttons | (((~PINC) & B01000000) >> 4);
|
if ((PINC & _BV(6)) == 0) buttons |= RIGHT_BUTTON; //compiles to shorter and faster code
|
||||||
|
|
||||||
// A and B
|
// A and B
|
||||||
buttons = buttons | (((~PINF) & B11000000) >> 6);
|
if ((PINF & _BV(7)) == 0) buttons |= A_BUTTON;
|
||||||
|
if ((PINF & _BV(6)) == 0) buttons |= B_BUTTON;
|
||||||
#elif defined(ARDUBOY_10)
|
#elif defined(ARDUBOY_10)
|
||||||
// down, up, left right
|
// down, up, left right
|
||||||
buttons = ((~PINF) & B11110000);
|
buttons = ((~PINF) & B11110000);
|
||||||
// A (left)
|
// A (left)
|
||||||
buttons = buttons | (((~PINE) & B01000000) >> 3);
|
if ((PINE & _BV(6)) == 0) {buttons |= A_BUTTON;}
|
||||||
// B (right)
|
// B (right)
|
||||||
buttons = buttons | (((~PINB) & B00010000) >> 2);
|
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
|
#endif
|
||||||
|
|
||||||
return buttons;
|
return buttons;
|
||||||
|
|
|
@ -38,9 +38,15 @@
|
||||||
// ----- Arduboy pins -----
|
// ----- Arduboy pins -----
|
||||||
#ifdef ARDUBOY_10
|
#ifdef ARDUBOY_10
|
||||||
|
|
||||||
#define PIN_CS 12 // Display CS Arduino pin number
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
#define CS_PORT PORTD // Display CS port
|
#define PIN_CS 3 // Pro Micro alternative display CS pin
|
||||||
#define CS_BIT PORTD6 // Display CS physical bit number
|
#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 PIN_DC 4 // Display D/C Arduino pin number
|
||||||
#define DC_PORT PORTD // Display D/C port
|
#define DC_PORT PORTD // Display D/C port
|
||||||
|
@ -57,14 +63,23 @@
|
||||||
#define SPI_SCK_BIT PORTB1
|
#define SPI_SCK_BIT PORTB1
|
||||||
|
|
||||||
#define RED_LED 10 /**< The pin number for the red color in the RGB LED. */
|
#define RED_LED 10 /**< The pin number for the red color in the RGB LED. */
|
||||||
#define GREEN_LED 11 /**< The pin number for the greem color in the RGB LED. */
|
#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 BLUE_LED 9 /**< The pin number for the blue color in the RGB LED. */
|
||||||
|
|
||||||
#define RED_LED_PORT PORTB
|
#define RED_LED_PORT PORTB
|
||||||
#define RED_LED_BIT PORTB6
|
#define RED_LED_BIT PORTB6
|
||||||
|
|
||||||
#define GREEN_LED_PORT PORTB
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
#define GREEN_LED_BIT PORTB7
|
#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_PORT PORTB
|
||||||
#define BLUE_LED_BIT PORTB5
|
#define BLUE_LED_BIT PORTB5
|
||||||
|
@ -103,15 +118,22 @@
|
||||||
#define B_BUTTON_BIT PORTB4
|
#define B_BUTTON_BIT PORTB4
|
||||||
|
|
||||||
#define PIN_SPEAKER_1 5 /**< The pin number of the first lead of the speaker */
|
#define PIN_SPEAKER_1 5 /**< The pin number of the first lead of the speaker */
|
||||||
#define PIN_SPEAKER_2 13 /**< The pin number of the second lead of the speaker */
|
|
||||||
|
|
||||||
#define SPEAKER_1_PORT PORTC
|
#define SPEAKER_1_PORT PORTC
|
||||||
#define SPEAKER_1_DDR DDRC
|
#define SPEAKER_1_DDR DDRC
|
||||||
#define SPEAKER_1_BIT PORTC6
|
#define SPEAKER_1_BIT PORTC6
|
||||||
|
|
||||||
#define SPEAKER_2_PORT PORTC
|
#ifdef ARDUINO_AVR_PROMICRO
|
||||||
#define SPEAKER_2_DDR DDRC
|
#define PIN_SPEAKER_2 2 //Pro Micro alternative for 2nd speaker pin
|
||||||
#define SPEAKER_2_BIT PORTC7
|
#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 A4 // Open analog input used for noise by initRandomSeed()
|
||||||
#define RAND_SEED_IN_PORTF
|
#define RAND_SEED_IN_PORTF
|
||||||
|
@ -201,7 +223,7 @@
|
||||||
#endif
|
#endif
|
||||||
// --------------------
|
// --------------------
|
||||||
|
|
||||||
// OLED hardware (SSD1306)
|
// OLED hardware (SSD1306,SSD1309,SH1106,OLED_64X64)
|
||||||
|
|
||||||
#define OLED_PIXELS_INVERTED 0xA7 // All pixels inverted
|
#define OLED_PIXELS_INVERTED 0xA7 // All pixels inverted
|
||||||
#define OLED_PIXELS_NORMAL 0xA6 // All pixels normal
|
#define OLED_PIXELS_NORMAL 0xA6 // All pixels normal
|
||||||
|
@ -215,10 +237,24 @@
|
||||||
#define OLED_HORIZ_FLIPPED 0xA0 // reversed segment re-map
|
#define OLED_HORIZ_FLIPPED 0xA0 // reversed segment re-map
|
||||||
#define OLED_HORIZ_NORMAL 0xA1 // normal 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 128 /**< The width of the display in pixels */
|
#define WIDTH 96
|
||||||
#define HEIGHT 64 /**< The height of the display in pixels */
|
#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 COLUMN_ADDRESS_END (WIDTH - 1) & 127 // 128 pixels wide
|
||||||
#define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 7 // 8 pages high
|
#define PAGE_ADDRESS_END ((HEIGHT/8)-1) & 7 // 8 pages high
|
||||||
|
@ -270,8 +306,7 @@ class Arduboy2Core
|
||||||
*
|
*
|
||||||
* \see LCDCommandMode() SPItransfer()
|
* \see LCDCommandMode() SPItransfer()
|
||||||
*/
|
*/
|
||||||
void static LCDDataMode();
|
inline void static LCDDataMode() __attribute__((always_inline));
|
||||||
|
|
||||||
/** \brief
|
/** \brief
|
||||||
* Put the display into command mode.
|
* Put the display into command mode.
|
||||||
*
|
*
|
||||||
|
@ -294,8 +329,7 @@ class Arduboy2Core
|
||||||
*
|
*
|
||||||
* \see LCDDataMode() sendLCDCommand() SPItransfer()
|
* \see LCDDataMode() sendLCDCommand() SPItransfer()
|
||||||
*/
|
*/
|
||||||
void static LCDCommandMode();
|
inline void static LCDCommandMode() __attribute__((always_inline));
|
||||||
|
|
||||||
/** \brief
|
/** \brief
|
||||||
* Transfer a byte to the display.
|
* Transfer a byte to the display.
|
||||||
*
|
*
|
||||||
|
|
|
@ -148,7 +148,7 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
|
||||||
data |= (uint8_t)(bitmap_data);
|
data |= (uint8_t)(bitmap_data);
|
||||||
Arduboy2Base::sBuffer[ofs] = data;
|
Arduboy2Base::sBuffer[ofs] = data;
|
||||||
}
|
}
|
||||||
if (yOffset != 0 && sRow < 7) {
|
if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) {
|
||||||
data = Arduboy2Base::sBuffer[ofs + WIDTH];
|
data = Arduboy2Base::sBuffer[ofs + WIDTH];
|
||||||
data &= (*((unsigned char *) (&mask_data) + 1));
|
data &= (*((unsigned char *) (&mask_data) + 1));
|
||||||
data |= (*((unsigned char *) (&bitmap_data) + 1));
|
data |= (*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
@ -170,7 +170,7 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
|
||||||
if (sRow >= 0) {
|
if (sRow >= 0) {
|
||||||
Arduboy2Base::sBuffer[ofs] |= (uint8_t)(bitmap_data);
|
Arduboy2Base::sBuffer[ofs] |= (uint8_t)(bitmap_data);
|
||||||
}
|
}
|
||||||
if (yOffset != 0 && sRow < 7) {
|
if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) {
|
||||||
Arduboy2Base::sBuffer[ofs + WIDTH] |= (*((unsigned char *) (&bitmap_data) + 1));
|
Arduboy2Base::sBuffer[ofs + WIDTH] |= (*((unsigned char *) (&bitmap_data) + 1));
|
||||||
}
|
}
|
||||||
ofs++;
|
ofs++;
|
||||||
|
@ -189,7 +189,7 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
|
||||||
if (sRow >= 0) {
|
if (sRow >= 0) {
|
||||||
Arduboy2Base::sBuffer[ofs] &= ~(uint8_t)(bitmap_data);
|
Arduboy2Base::sBuffer[ofs] &= ~(uint8_t)(bitmap_data);
|
||||||
}
|
}
|
||||||
if (yOffset != 0 && sRow < 7) {
|
if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) {
|
||||||
Arduboy2Base::sBuffer[ofs + WIDTH] &= ~(*((unsigned char *) (&bitmap_data) + 1));
|
Arduboy2Base::sBuffer[ofs + WIDTH] &= ~(*((unsigned char *) (&bitmap_data) + 1));
|
||||||
}
|
}
|
||||||
ofs++;
|
ofs++;
|
||||||
|
@ -223,7 +223,7 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
|
||||||
data |= (uint8_t)(bitmap_data);
|
data |= (uint8_t)(bitmap_data);
|
||||||
Arduboy2Base::sBuffer[ofs] = data;
|
Arduboy2Base::sBuffer[ofs] = data;
|
||||||
}
|
}
|
||||||
if (yOffset != 0 && sRow < 7) {
|
if (yOffset != 0 && sRow < ((HEIGHT / 8) - 1)) {
|
||||||
data = Arduboy2Base::sBuffer[ofs + WIDTH];
|
data = Arduboy2Base::sBuffer[ofs + WIDTH];
|
||||||
data &= (*((unsigned char *) (&mask_data) + 1));
|
data &= (*((unsigned char *) (&mask_data) + 1));
|
||||||
data |= (*((unsigned char *) (&bitmap_data) + 1));
|
data |= (*((unsigned char *) (&bitmap_data) + 1));
|
||||||
|
@ -251,9 +251,8 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
|
||||||
"push r28\n" // save Y
|
"push r28\n" // save Y
|
||||||
"push r29\n"
|
"push r29\n"
|
||||||
"movw r28, %[buffer_ofs]\n" // Y = buffer_ofs_2
|
"movw r28, %[buffer_ofs]\n" // Y = buffer_ofs_2
|
||||||
"adiw r28, 63\n" // buffer_ofs_2 = buffer_ofs + 128
|
"subi r28, %[neg_width]\n" // buffer_ofs_2 = buffer_ofs + WIDTH
|
||||||
"adiw r28, 63\n"
|
"sbci r29, -1\n"
|
||||||
"adiw r28, 2\n"
|
|
||||||
"loop_y:\n"
|
"loop_y:\n"
|
||||||
"loop_x:\n"
|
"loop_x:\n"
|
||||||
// load bitmap and mask data
|
// load bitmap and mask data
|
||||||
|
@ -269,8 +268,8 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
|
||||||
"movw %[mask_data], r0\n"
|
"movw %[mask_data], r0\n"
|
||||||
|
|
||||||
// SECOND PAGE
|
// SECOND PAGE
|
||||||
// if yOffset != 0 && sRow < 7
|
// if yOffset != 0 && sRow < ((HEIGHT / 8) - 1)
|
||||||
"cpi %[sRow], 7\n"
|
"cpi %[sRow], %[row_height]\n"
|
||||||
"brge end_second_page\n"
|
"brge end_second_page\n"
|
||||||
// then
|
// then
|
||||||
"ld %[data], Y\n"
|
"ld %[data], Y\n"
|
||||||
|
@ -346,7 +345,9 @@ void Sprites::drawBitmap(int16_t x, int16_t y,
|
||||||
|
|
||||||
// [sprite_ofs_jump] "r" (0),
|
// [sprite_ofs_jump] "r" (0),
|
||||||
[yOffset] "l" (yOffset), // lower register
|
[yOffset] "l" (yOffset), // lower register
|
||||||
[mul_amt] "l" (mul_amt) // 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
|
// 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
|
// 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.
|
// that we clobber them. Instead, we push/pop to preserve them.
|
||||||
|
|
Loading…
Reference in New Issue