update Arduboy and Arduboy2 libs

Arduboy lib:
added support for Noritake GU128X64-800B VFD display

Arduboy2 lib:
added support for Noritake GU128X64-800B VFD display
removed experimental batterycheck code and keywords
temporary reverted assembly code in sprites.cpp
fixed one off bug in drawBitmap
buttonsState no longer uses timer ISR code
exitToBootloader function uses timer ISR code
This commit is contained in:
Mr.Blinky 2020-06-27 01:22:31 +02:00 committed by GitHub
parent 7b4faf6a85
commit 162a70371e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 372 additions and 285 deletions

View File

@ -2,7 +2,7 @@
// need to redeclare these here since we declare them static in .h // need to redeclare these here since we declare them static in .h
volatile uint8_t *ArduboyCore::mosiport, volatile uint8_t *ArduboyCore::mosiport,
/* *ArduboyCore::csport, */ *ArduboyCore::dcport; *ArduboyCore::csport, *ArduboyCore::dcport;
uint8_t ArduboyCore::mosipinmask, uint8_t ArduboyCore::mosipinmask,
ArduboyCore::cspinmask, ArduboyCore::dcpinmask; ArduboyCore::cspinmask, ArduboyCore::dcpinmask;
@ -35,7 +35,12 @@ const uint8_t PROGMEM lcdBootProgram[] = {
// //
// Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf // Further reading: https://www.adafruit.com/datasheets/SSD1306.pdf
#ifdef OLED_SH1106 #if defined(GU12864_800B)
0x24, 0x40, // enable Layer 0, graphic display area on
0x47, // set brightness
0x64, 0x00, // set x position 0
0x84, // address mode set: X increment
#elif OLED_SH1106
0x8D, 0x14, // Charge Pump Setting v = enable (0x14) 0x8D, 0x14, // Charge Pump Setting v = enable (0x14)
0xA1, // Set Segment Re-map 0xA1, // Set Segment Re-map
0xC8, // Set COM Output Scan Direction 0xC8, // Set COM Output Scan Direction
@ -217,6 +222,27 @@ void ArduboyCore::bootPins()
#endif #endif
} }
#if defined(GU12864_800B)
void ArduboyCore::displayEnable()
{
*csport |= cspinmask;
SPCR = _BV(SPE) | _BV(MSTR) | _BV(CPOL) | _BV(CPHA);
LCDCommandMode();
}
void ArduboyCore::displayDisable()
{
SPCR = _BV(SPE) | _BV(MSTR);
}
void ArduboyCore::displayWrite(uint8_t data)
{
*csport &= ~cspinmask;
SPI.transfer(data);
*csport |= cspinmask;
}
#endif
void ArduboyCore::bootLCD() void ArduboyCore::bootLCD()
{ {
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX) #if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
@ -230,13 +256,32 @@ void ArduboyCore::bootLCD()
i2c_stop(); i2c_stop();
#else #else
// setup the ports we need to talk to the OLED // setup the ports we need to talk to the OLED
//csport = portOutputRegister(digitalPinToPort(CS)); csport = portOutputRegister(digitalPinToPort(CS));
*portOutputRegister(digitalPinToPort(CS)) &= ~cspinmask;
cspinmask = digitalPinToBitMask(CS); cspinmask = digitalPinToBitMask(CS);
*portOutputRegister(digitalPinToPort(CS)) &= ~cspinmask;
dcport = portOutputRegister(digitalPinToPort(DC)); dcport = portOutputRegister(digitalPinToPort(DC));
dcpinmask = digitalPinToBitMask(DC); dcpinmask = digitalPinToBitMask(DC);
SPI.setClockDivider(SPI_CLOCK_DIV2); SPI.setClockDivider(SPI_CLOCK_DIV2);
#if defined(OLED_128X64_ON_96X96) || defined(OLED_128X64_ON_128X96) || defined(OLED_128X64_ON_128X128)|| defined(OLED_128X96_ON_128X128) || defined(OLED_96X96_ON_128X128) || defined(OLED_64X128_ON_128X128) #if defined(GU12864_800B)
delayShort(1);
digitalWrite(RST, HIGH);
delayShort(10);
displayEnable();
for (uint8_t i = 0; i < sizeof(lcdBootProgram) + 8; i++)
{
if (i < 8)
{
displayWrite(0x62); // set display area
displayWrite(i); // display area address
LCDDataMode();
displayWrite(0xFF); // Graphic display
LCDCommandMode();
}
else
displayWrite(pgm_read_byte(lcdBootProgram + i - 8));
}
displayDisable();
#elif defined(OLED_128X64_ON_96X96) || defined(OLED_128X64_ON_128X96) || defined(OLED_128X64_ON_128X128)|| defined(OLED_128X96_ON_128X128) || defined(OLED_96X96_ON_128X128) || defined(OLED_64X128_ON_128X128)
LCDDataMode(); LCDDataMode();
for (uint16_t i = 0; i < 8192; i++) SPI.transfer(0); //Clear all display ram for (uint16_t i = 0; i < 8192; i++) SPI.transfer(0); //Clear all display ram
#endif #endif
@ -253,14 +298,22 @@ void ArduboyCore::bootLCD()
void ArduboyCore::LCDDataMode() void ArduboyCore::LCDDataMode()
{ {
#if defined(GU12864_800B)
*dcport &= ~dcpinmask;
#else
*dcport |= dcpinmask; *dcport |= dcpinmask;
#endif
// *csport &= ~cspinmask; // *csport &= ~cspinmask;
} }
void ArduboyCore::LCDCommandMode() void ArduboyCore::LCDCommandMode()
{ {
// *csport |= cspinmask; // *csport |= cspinmask;
#if defined(GU12864_800B)
*dcport |= dcpinmask;
#else
*dcport &= ~dcpinmask; *dcport &= ~dcpinmask;
#endif
// *csport &= ~cspinmask; CS set once at bootLCD // *csport &= ~cspinmask; CS set once at bootLCD
} }
@ -359,7 +412,24 @@ void ArduboyCore::paint8Pixels(uint8_t pixels)
void ArduboyCore::paintScreen(const unsigned char *image) void ArduboyCore::paintScreen(const unsigned char *image)
{ {
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX) #if defined(GU12864_800B)
displayEnable();
for (uint8_t r = 0; r < (HEIGHT/8); r++)
{
LCDCommandMode();
displayWrite(0x60);
displayWrite(r);
LCDDataMode();
for (uint8_t c = 0; c < (WIDTH); c++)
{
*csport &= ~cspinmask;
SPDR = pgm_read_byte(image++);
while (!(SPSR & _BV(SPIF)));
*csport |= cspinmask;
}
}
displayDisable();
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
i2c_start(SSD1306_I2C_DATA); i2c_start(SSD1306_I2C_DATA);
for (int i = 0; i < (HEIGHT * WIDTH) / 8; i++) for (int i = 0; i < (HEIGHT * WIDTH) / 8; i++)
i2c_sendByte(pgm_read_byte(image+i)); i2c_sendByte(pgm_read_byte(image+i));
@ -439,7 +509,24 @@ void ArduboyCore::paintScreen(const unsigned char *image)
// will be used by any buffer based subclass // will be used by any buffer based subclass
void ArduboyCore::paintScreen(unsigned char image[]) void ArduboyCore::paintScreen(unsigned char image[])
{ {
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX) #if defined(GU12864_800B)
displayEnable();
for (uint8_t r = 0; r < (HEIGHT/8); r++)
{
LCDCommandMode();
displayWrite(0x60);
displayWrite(r);
LCDDataMode();
for (uint8_t c = 0; c < (WIDTH); c++)
{
*csport &= ~cspinmask;
SPDR = *(image++);
while (!(SPSR & _BV(SPIF)));
*csport |= cspinmask;
}
}
displayDisable();
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
uint16_t length = WIDTH * HEIGHT / 8; uint16_t length = WIDTH * HEIGHT / 8;
uint8_t sda_clr = I2C_PORT & ~((1 << I2C_SDA) | (1 << I2C_SCL)); uint8_t sda_clr = I2C_PORT & ~((1 << I2C_SDA) | (1 << I2C_SCL));
uint8_t scl = 1 << I2C_SCL; uint8_t scl = 1 << I2C_SCL;
@ -719,14 +806,37 @@ void ArduboyCore::sendLCDCommand(uint8_t command)
// when inverted, a pixel set to 0 will be on // when inverted, a pixel set to 0 will be on
void ArduboyCore::invert(boolean inverse) void ArduboyCore::invert(boolean inverse)
{ {
#if defined(GU12864_800B)
displayEnable();
displayWrite(0x24);
if (inverse) displayWrite(0x50);
else displayWrite(0x40);
LCDDataMode();
displayDisable();
#else
sendLCDCommand(inverse ? OLED_PIXELS_INVERTED : OLED_PIXELS_NORMAL); sendLCDCommand(inverse ? OLED_PIXELS_INVERTED : OLED_PIXELS_NORMAL);
#endif
} }
// turn all display pixels on, ignoring buffer contents // turn all display pixels on, ignoring buffer contents
// or set to normal buffer display // or set to normal buffer display
void ArduboyCore::allPixelsOn(boolean on) void ArduboyCore::allPixelsOn(boolean on)
{ {
#if defined(GU12864_800B)
displayEnable();
if (on)
{
displayWrite(0x20);
displayWrite(0x50);
}
else
displayWrite(0x24);
displayWrite(0x40);
LCDDataMode();
displayDisable();
#else
sendLCDCommand(on ? OLED_ALL_PIXELS_ON : OLED_PIXELS_FROM_RAM); sendLCDCommand(on ? OLED_ALL_PIXELS_ON : OLED_PIXELS_FROM_RAM);
#endif
} }
// flip the display vertically or set to normal // flip the display vertically or set to normal

View File

@ -212,7 +212,13 @@ public:
* in a frame loop. * in a frame loop.
*/ */
void static idle(); void static idle();
#if defined(GU12864_800B)
void static displayEnable();
void static displayDisable();
void static displayWrite(uint8_t data);
#endif
void static LCDDataMode(); //< put the display in data mode void static LCDDataMode(); //< put the display in data mode
/// put the display in command mode /// put the display in command mode
@ -365,7 +371,7 @@ protected:
private: private:
volatile static uint8_t *mosiport, /* *csport, */ *dcport; volatile static uint8_t *mosiport, *csport, *dcport;
uint8_t static mosipinmask, cspinmask, dcpinmask; uint8_t static mosipinmask, cspinmask, dcpinmask;
}; };

View File

@ -32,8 +32,6 @@ bootLogoSpritesOverwrite KEYWORD2
bootLogoSpritesSelfMasked KEYWORD2 bootLogoSpritesSelfMasked KEYWORD2
bootLogoText KEYWORD2 bootLogoText KEYWORD2
buttonsState KEYWORD2 buttonsState KEYWORD2
checkBatteryState KEYWORD2
checkBatteryStateLED KEYWORD2
clear KEYWORD2 clear KEYWORD2
collide KEYWORD2 collide KEYWORD2
cpuLoad KEYWORD2 cpuLoad KEYWORD2
@ -167,7 +165,3 @@ RGB_ON LITERAL1
ARDUBOY_NO_USB LITERAL1 ARDUBOY_NO_USB LITERAL1
BATTERY_STATE_LOW LITERAL1
BATTERY_STATE_NORMAL LITERAL1
BATTERY_STATE_INVALID LITERAL1
FLASH_LED LITERAL1

View File

@ -8,32 +8,12 @@
#include "ab_logo.c" #include "ab_logo.c"
#include "glcdfont.c" #include "glcdfont.c"
//================================
//========== class Rect ==========
//================================
Rect::Rect(int16_t x, int16_t y, uint8_t width, uint8_t height)
: x(x), y(y), width(width), height(height)
{
}
//=================================
//========== class Point ==========
//=================================
Point::Point(int16_t x, int16_t y)
: x(x), y(y)
{
}
//======================================== //========================================
//========== class Arduboy2Base ========== //========== class Arduboy2Base ==========
//======================================== //========================================
uint8_t Arduboy2Base::sBuffer[]; uint8_t Arduboy2Base::sBuffer[];
uint8_t Arduboy2Base::batteryLow = EEPROM.read(EEPROM_BATTERY_LOW); //Low battery bandgap value - 192
Arduboy2Base::Arduboy2Base() Arduboy2Base::Arduboy2Base()
{ {
currentButtonState = 0; currentButtonState = 0;
@ -78,16 +58,17 @@ void Arduboy2Base::flashlight()
if (!pressed(UP_BUTTON)) { if (!pressed(UP_BUTTON)) {
return; return;
} }
#ifdef GU12864_800B
allPixelsOn(true);
#else
sendLCDCommand(OLED_ALL_PIXELS_ON); // smaller than allPixelsOn() sendLCDCommand(OLED_ALL_PIXELS_ON); // smaller than allPixelsOn()
#endif
digitalWriteRGB(RGB_ON, RGB_ON, RGB_ON); digitalWriteRGB(RGB_ON, RGB_ON, RGB_ON);
#ifndef ARDUBOY_CORE // for Arduboy core timer 0 should remain enabled
// prevent the bootloader magic number from being overwritten by timer 0 // prevent the bootloader magic number from being overwritten by timer 0
// when a timer variable overlaps the magic number location, for when // when a timer variable overlaps the magic number location, for when
// flashlight mode is used for upload problem recovery // flashlight mode is used for upload problem recovery
power_timer0_disable(); power_timer0_disable();
#endif
while (true) { while (true) {
idle(); idle();
@ -267,11 +248,7 @@ bool Arduboy2Base::everyXFrames(uint8_t frames)
bool Arduboy2Base::nextFrame() bool Arduboy2Base::nextFrame()
{ {
#ifdef ARDUBOY_CORE uint8_t now = (uint8_t) timer0_millis;
uint8_t now = millisChar();
#else
uint8_t now = (uint8_t) millis();
#endif
uint8_t frameDurationMs = now - thisFrameStart; uint8_t frameDurationMs = now - thisFrameStart;
if (justRendered) { if (justRendered) {
@ -867,7 +844,7 @@ void Arduboy2Base::drawBitmap
uint8_t color) uint8_t color)
{ {
// no need to draw at all if we're offscreen // no need to draw at all if we're offscreen
if (x+w < 0 || x > WIDTH-1 || y+h < 0 || y > HEIGHT-1) if (x+w <= 0 || x > WIDTH-1 || y+h <= 0 || y > HEIGHT-1)
return; return;
int8_t yOffset = y & 7; int8_t yOffset = y & 7;
@ -1200,88 +1177,6 @@ void Arduboy2Base::swap(int16_t& a, int16_t& b)
b = temp; b = temp;
} }
uint8_t Arduboy2Base::checkBatteryState()
{
uint8_t state = BATTERY_STATE_INVALID;
asm volatile (
" ldi r30, lo8(%[prr0]) \n" // if (bit_is_set(PRR0,PRADC)) //ADC power off
" ldi r31, hi8(%[prr0]) \n" // {
" ld r24, z \n"
" lds r25, %[adcsra] \n"
" sbrs r24, %[pradc] \n"
" rjmp 1f \n"
" \n"
" andi r24, ~(1<<%[pradc]) \n" // PRR0 &= ~_BV(PRADC); // ADC power on
" st z, r24 \n"
" ldi r24, %[admuxval] \n" // ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)
" sts %[admux], r24 \n"
" ori r25, 1<<%[adsc] \n" // ADCSRA |= _BV(ADSC) //start conversion
" sts %[adcsra], r25 \n" // }
" ;rjmp 2f \n" // bit is set so continue below to jump to 2f
"1: \n"
" sbrc r25, %[adsc] \n" // else if (!(ADCSRA & _BV(ADSC)) //ADC conversion ready
" rjmp 2f \n" // {
" \n"
" ori r24, 1<<%[pradc] \n" // PRR0 |= _BV(PRADC); // ADC power off
" st z, r24 \n"
" ldi r30, %[adcl] \n" // uint16_t bandgap = ADCL | (ADCH << 8);
" ld r24, z+ \n"
" ld r25, z \n"
" subi r24, 192 \n" // bandgap -= 192;
" sbci r25, 0 \n"
" and r25, r25 \n" // if (bandgap < 256)
" brne 2f \n" // {
" \n"
" ldi %[state],%[normal] \n" // state = BATTERY_STATE_NORMAL;
" lds r25, %[battlow] \n"
" cp r25, r24 \n"
" brcc 2f \n" // if (batteryLow < bandgap) state = BATTERY_STATE_LOW;
" ldi %[state],%[low] \n" // }
"2: \n" // }
:[state] "+d"(state)
:[prr0] "M" (_SFR_MEM_ADDR(PRR0)),
[adcsra] "M" (_SFR_MEM_ADDR(ADCSRA)),
[pradc] "I" (PRADC),
[admuxval] "M" (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)),
[admux] "M" (_SFR_MEM_ADDR(ADMUX)),
[adsc] "I" (ADSC),
[adcl] "M" (_SFR_MEM_ADDR(ADCL)),
[battlow] "" (&batteryLow),
[normal] "I" (BATTERY_STATE_NORMAL),
[low] "I" (BATTERY_STATE_LOW)
: "r24", "r25", "r30", "r31"
);
#if 0
// For reference, this is the C++ equivalent
uint8_t state = BATTERY_STATE_UNDEFINED;
if (bit_is_set(PRR0,PRADC)) //only enable when ADC power is disabled
{
PRR0 &= ~_BV(PRADC); // ADC power on
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); //meassure 1.1V bandgap against AVcc
ADCSRA |= _BV(ADSC); //start conversion
}
else if (!(ADCSRA & _BV(ADSC)))
{
PRR0 |= _BV(PRADC); // ADC power off
uint16_t bandgap = ADCL | (ADCH << 8);
bandgap -= 192;
if (bandgap < 256)
{
state = BATTERY_STATE_NORMAL;
if (batteryLow < (uint8_t)bandgap) state = BATTERY_STATE_LOW;
}
}
#endif
return state;
}
uint8_t Arduboy2Base::checkBatteryStateLED(bool flash)
{
uint8_t state = checkBatteryState();
if (state == BATTERY_STATE_NORMAL | flash) TXLED0;
if (state == BATTERY_STATE_LOW) TXLED1;
return state;
}
//==================================== //====================================
//========== class Arduboy2 ========== //========== class Arduboy2 ==========

View File

@ -41,8 +41,6 @@
#define EEPROM_VERSION 0 #define EEPROM_VERSION 0
#define EEPROM_SYS_FLAGS 1 #define EEPROM_SYS_FLAGS 1
#define EEPROM_AUDIO_ON_OFF 2 #define EEPROM_AUDIO_ON_OFF 2
#define EEPROM_BANDGAP_CAL 6 //Bandgap calibration value
#define EEPROM_BATTERY_LOW 7 //Battery low threshold
#define EEPROM_UNIT_ID 8 // A uint16_t binary unit ID #define EEPROM_UNIT_ID 8 // A uint16_t binary unit ID
#define EEPROM_UNIT_NAME 10 // An up to 6 character unit name. Cannot contain #define EEPROM_UNIT_NAME 10 // An up to 6 character unit name. Cannot contain
// 0x00 or 0xFF. Lengths less than 6 are padded // 0x00 or 0xFF. Lengths less than 6 are padded
@ -89,10 +87,6 @@
#define CLEAR_BUFFER true /**< Value to be passed to `display()` to clear the screen buffer. */ #define CLEAR_BUFFER true /**< Value to be passed to `display()` to clear the screen buffer. */
#define BATTERY_STATE_LOW 0
#define BATTERY_STATE_NORMAL 1
#define BATTERY_STATE_INVALID 0xFF
#define FLASH_LED true
//============================================= //=============================================
//========== Rect (rectangle) object ========== //========== Rect (rectangle) object ==========
@ -128,7 +122,10 @@ struct Rect
* \param width The width of the rectangle. Copied to variable `width`. * \param width The width of the rectangle. Copied to variable `width`.
* \param height The height of the rectangle. Copied to variable `height`. * \param height The height of the rectangle. Copied to variable `height`.
*/ */
Rect(int16_t x, int16_t y, uint8_t width, uint8_t height); constexpr Rect(int16_t x, int16_t y, uint8_t width, uint8_t height)
: x(x), y(y), width(width), height(height)
{
}
}; };
//================================== //==================================
@ -159,7 +156,10 @@ struct Point
* \param x The X coordinate of the point. Copied to variable `x`. * \param x The X coordinate of the point. Copied to variable `x`.
* \param y The Y coordinate of the point. Copied to variable `y`. * \param y The Y coordinate of the point. Copied to variable `y`.
*/ */
Point(int16_t x, int16_t y); constexpr Point(int16_t x, int16_t y)
: x(x), y(y)
{
}
}; };
//================================== //==================================
@ -1266,60 +1266,6 @@ class Arduboy2Base : public Arduboy2Core
*/ */
void writeShowBootLogoLEDsFlag(bool val); void writeShowBootLogoLEDsFlag(bool val);
/** \brief
* Returns the battery state.
* \details
* This function is intended as a method to determine a low battery state
*
* Returns the following states:
* - BATTERY_STATE_LOW The battery low threshold has been reached.
* - BATTERY_STATE_NORMAL The battery is considered normal.
* - BATTERY_STATE_INVALID The ADC conversion is not ready yet or the
* result is out of range.
*
* This fucntion depends on the EEPROM_BATTERY_LOW value been set to the
* low batterly bandgap voltage value. The default value (0xFF) will
* disable the EEPROM_BATTERY_LOW state.
*
* example:
* \code{.cpp}
* void loop() {
* if (!arduboy.nextFrame()) return;
* if (arduboy.everyXFrames(FRAMERATE) && arduboy.checkBatteryState() == BATTERY_STATE_LOW)
* {
* batteryLowWarning = true;
* }
* \endcode
*
* \see everyXFrames()
*/
uint8_t checkBatteryState();
/** \brief
* Returns battery state and sets TXLED as a low battery indicator
* \param flash defaults to 'false' for no flashing. use 'FLASH_LED' or
* `true` for flashing.
* \details
* This function is intended as a method to determine a low battery state.
* The TXLED is used as a battery low indicator. The TXLED will light up
* continiously by default when the battery state is low. The optional
* FLASH_LED parameter can be passed to make the LED toggle on or off on low
* battery state.
*
* This function is a quick way of adding a low battery indicator to a sketch.
*
* example:
* \code{.cpp}
* void loop() {
* if (!arduboy.nextFrame()) return;
* //turn TXLED alternately on 1 second and off 1 second when battery is low
* if (arduboy.everyXFrames(FRAMERATE)) checkBatteryStateLED(FLASH_LED);
* \endcode
*
* \see checkBatteryState() everyXFrames()
*/
uint8_t checkBatteryStateLED(bool flash = false);
/** \brief /** \brief
* A counter which is incremented once per frame. * A counter which is incremented once per frame.
* *
@ -1369,8 +1315,6 @@ class Arduboy2Base : public Arduboy2Core
*/ */
static uint8_t sBuffer[(HEIGHT*WIDTH)/8]; static uint8_t sBuffer[(HEIGHT*WIDTH)/8];
static uint8_t batteryLow;
protected: protected:
// helper function for sound enable/disable system control // helper function for sound enable/disable system control
void sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal); void sysCtrlSound(uint8_t buttons, uint8_t led, uint8_t eeVal);
@ -1712,5 +1656,7 @@ class Arduboy2 : public Print, public Arduboy2Base
bool textWrap; bool textWrap;
}; };
extern volatile unsigned long timer0_millis;
#endif #endif

View File

@ -13,8 +13,13 @@ const uint8_t PROGMEM lcdBootProgram[] = {
// 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
#if defined(OLED_SH1106) #if defined(GU12864_800B)
0x24, 0x40, // enable Layer 0, graphic display area on
0x47, // set brightness
0x64, 0x00, // set x position 0
0x84, // address mode set: X increment
#elif defined(OLED_SH1106)
0x8D, 0x14, // Charge Pump Setting v = enable (0x14) 0x8D, 0x14, // Charge Pump Setting v = enable (0x14)
0xA1, // Set Segment Re-map 0xA1, // Set Segment Re-map
0xC8, // Set COM Output Scan Direction 0xC8, // Set COM Output Scan Direction
@ -321,7 +326,25 @@ void Arduboy2Core::bootPins()
void Arduboy2Core::bootOLED() void Arduboy2Core::bootOLED()
{ {
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX) #if defined(GU12864_800B)
bitSet(RST_PORT,RST_BIT);
delayShort(10);
displayEnable();
for (uint8_t i = 0; i < sizeof(lcdBootProgram) + 8; i++)
{
if (i < 8)
{
displayWrite(0x62); // set display area
displayWrite(i); // display area address
LCDDataMode();
displayWrite(0xFF); // Graphic display
LCDCommandMode();
}
else
displayWrite(pgm_read_byte(lcdBootProgram + i - 8));
}
displayDisable();
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
i2c_start(SSD1306_I2C_CMD); i2c_start(SSD1306_I2C_CMD);
for (uint8_t i = 0; i < sizeof(lcdBootProgram); i++) for (uint8_t i = 0; i < sizeof(lcdBootProgram); i++)
i2c_sendByte(pgm_read_byte(lcdBootProgram + i)); i2c_sendByte(pgm_read_byte(lcdBootProgram + i));
@ -476,10 +499,38 @@ void Arduboy2Core::bootPowerSaving()
PRR1 = _BV(PRUSART1); PRR1 = _BV(PRUSART1);
} }
#if defined(GU12864_800B)
void Arduboy2Core::displayEnable()
{
bitSet(CS_PORT,CS_BIT);
SPCR = _BV(SPE) | _BV(MSTR) | _BV(CPOL) | _BV(CPHA);
//bitClear(CS_PORT,CS_BIT);
LCDCommandMode();
}
void Arduboy2Core::displayDisable()
{
//bitSet(CS_PORT,CS_BIT);
SPCR = _BV(SPE) | _BV(MSTR);
}
void Arduboy2Core::displayWrite(uint8_t data)
{
bitClear(CS_PORT,CS_BIT);
SPItransfer(data);
bitSet(CS_PORT,CS_BIT);
}
#endif
// Shut down the display // Shut down the display
void Arduboy2Core::displayOff() void Arduboy2Core::displayOff()
{ {
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX) #if defined(GU12864_800B)
displayEnable();
displayWrite(0x20);
displayWrite(0x00);
displayDisable();
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
i2c_start(SSD1306_I2C_CMD); i2c_start(SSD1306_I2C_CMD);
i2c_sendByte(0xAE); // display off i2c_sendByte(0xAE); // display off
i2c_sendByte(0x8D); // charge pump: i2c_sendByte(0x8D); // charge pump:
@ -519,7 +570,24 @@ void Arduboy2Core::paint8Pixels(uint8_t pixels)
void Arduboy2Core::paintScreen(const uint8_t *image) void Arduboy2Core::paintScreen(const uint8_t *image)
{ {
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX) #if defined(GU12864_800B)
displayEnable();
for (uint8_t r = 0; r < (HEIGHT/8); r++)
{
LCDCommandMode();
displayWrite(0x60);
displayWrite(r);
LCDDataMode();
for (uint8_t c = 0; c < (WIDTH); c++)
{
bitClear(CS_PORT,CS_BIT);
SPDR = pgm_read_byte(image++);
while (!(SPSR & _BV(SPIF)));
bitSet(CS_PORT,CS_BIT);
}
}
displayDisable();
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
i2c_start(SSD1306_I2C_DATA); i2c_start(SSD1306_I2C_DATA);
for (int i = 0; i < (HEIGHT * WIDTH) / 8; i++) for (int i = 0; i < (HEIGHT * WIDTH) / 8; i++)
i2c_sendByte(pgm_read_byte(image+i)); i2c_sendByte(pgm_read_byte(image+i));
@ -599,7 +667,30 @@ void Arduboy2Core::paintScreen(const uint8_t *image)
// 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)
{ {
#if defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX) #if defined(GU12864_800B)
displayEnable();
for (uint8_t r = 0; r < (HEIGHT/8); r++)
{
LCDCommandMode();
displayWrite(0x60);
displayWrite(r);
LCDDataMode();
for (uint8_t c = 0; c < (WIDTH); c++)
{
bitClear(CS_PORT,CS_BIT);
if (clear)
{
SPDR = *image; // set the first SPI data byte to get things started
*(image++) = 0; // clear the first image byte
}
else
SPDR = *(image++);
while (!(SPSR & _BV(SPIF)));
bitSet(CS_PORT,CS_BIT);
}
}
displayDisable();
#elif defined(OLED_SSD1306_I2C) || (OLED_SSD1306_I2CX)
uint16_t length = WIDTH * HEIGHT / 8; uint16_t length = WIDTH * HEIGHT / 8;
uint8_t sda_clr = I2C_PORT & ~((1 << I2C_SDA) | (1 << I2C_SCL)); uint8_t sda_clr = I2C_PORT & ~((1 << I2C_SDA) | (1 << I2C_SCL));
uint8_t scl = 1 << I2C_SCL; uint8_t scl = 1 << I2C_SCL;
@ -957,7 +1048,7 @@ void Arduboy2Core::sendLCDCommand(uint8_t command)
i2c_start(SSD1306_I2C_CMD); i2c_start(SSD1306_I2C_CMD);
i2c_sendByte(command); i2c_sendByte(command);
i2c_stop(); i2c_stop();
#else #else if !defined GU12864_800B
LCDCommandMode(); LCDCommandMode();
SPItransfer(command); SPItransfer(command);
LCDDataMode(); LCDDataMode();
@ -968,26 +1059,57 @@ void Arduboy2Core::sendLCDCommand(uint8_t command)
// when inverted, a pixel set to 0 will be on // when inverted, a pixel set to 0 will be on
void Arduboy2Core::invert(bool inverse) void Arduboy2Core::invert(bool inverse)
{ {
#if defined(GU12864_800B)
displayEnable();
displayWrite(0x24);
if (inverse) displayWrite(0x50);
else displayWrite(0x40);
LCDDataMode();
displayDisable();
#else
sendLCDCommand(inverse ? OLED_PIXELS_INVERTED : OLED_PIXELS_NORMAL); sendLCDCommand(inverse ? OLED_PIXELS_INVERTED : OLED_PIXELS_NORMAL);
#endif
} }
// turn all display pixels on, ignoring buffer contents // turn all display pixels on, ignoring buffer contents
// or set to normal buffer display // or set to normal buffer display
void Arduboy2Core::allPixelsOn(bool on) void Arduboy2Core::allPixelsOn(bool on)
{ {
#if defined(GU12864_800B)
displayEnable();
if (on)
{
displayWrite(0x20);
displayWrite(0x50);
}
else
displayWrite(0x24);
displayWrite(0x40);
LCDDataMode();
displayDisable();
#else
sendLCDCommand(on ? OLED_ALL_PIXELS_ON : OLED_PIXELS_FROM_RAM); sendLCDCommand(on ? OLED_ALL_PIXELS_ON : OLED_PIXELS_FROM_RAM);
#endif
} }
// flip the display vertically or set to normal // flip the display vertically or set to normal
void Arduboy2Core::flipVertical(bool flipped) void Arduboy2Core::flipVertical(bool flipped)
{ {
#ifdef GU12864_800B
//not available
#else
sendLCDCommand(flipped ? OLED_VERTICAL_FLIPPED : OLED_VERTICAL_NORMAL); sendLCDCommand(flipped ? OLED_VERTICAL_FLIPPED : OLED_VERTICAL_NORMAL);
#endif
} }
// flip the display horizontally or set to normal // flip the display horizontally or set to normal
void Arduboy2Core::flipHorizontal(bool flipped) void Arduboy2Core::flipHorizontal(bool flipped)
{ {
#ifdef GU12864_800B
//not available
#else
sendLCDCommand(flipped ? OLED_HORIZ_FLIPPED : OLED_HORIZ_NORMAL); sendLCDCommand(flipped ? OLED_HORIZ_FLIPPED : OLED_HORIZ_NORMAL);
#endif
} }
/* RGB LED */ /* RGB LED */
@ -1201,9 +1323,9 @@ void Arduboy2Core::digitalWriteRGB(uint8_t color, uint8_t val)
uint8_t Arduboy2Core::buttonsState() uint8_t Arduboy2Core::buttonsState()
{ {
#ifndef ARDUBOY_CORE
uint8_t buttons; uint8_t buttons;
#ifdef ARDUBOY_10
#ifdef ARDUBOY_10
// up, right, left, down // up, right, left, down
buttons = ((~PINF) & buttons = ((~PINF) &
(_BV(UP_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) | (_BV(UP_BUTTON_BIT) | _BV(RIGHT_BUTTON_BIT) |
@ -1222,11 +1344,8 @@ uint8_t Arduboy2Core::buttonsState()
if (bitRead(A_BUTTON_PORTIN, A_BUTTON_BIT) == 0) { buttons |= A_BUTTON; } if (bitRead(A_BUTTON_PORTIN, A_BUTTON_BIT) == 0) { buttons |= A_BUTTON; }
// B // B
if (bitRead(B_BUTTON_PORTIN, B_BUTTON_BIT) == 0) { buttons |= B_BUTTON; } if (bitRead(B_BUTTON_PORTIN, B_BUTTON_BIT) == 0) { buttons |= B_BUTTON; }
#endif #endif
#else
register uint8_t buttons asm("r24");
asm volatile("call scan_buttons\n\t" : "=d" (buttons));
#endif
return buttons; return buttons;
} }
@ -1242,8 +1361,10 @@ void Arduboy2Core::delayShort(uint16_t ms)
void Arduboy2Core::exitToBootloader() void Arduboy2Core::exitToBootloader()
{ {
#ifndef ARDUBOY_CORE
cli(); cli();
#ifdef ARDUBOY_CORE
asm volatile ("jmp exit_to_bootloader");
#else
// set bootloader magic key // set bootloader magic key
// storing two uint8_t instead of one uint16_t saves an instruction // storing two uint8_t instead of one uint16_t saves an instruction
// when high and low bytes of the magic key are the same // when high and low bytes of the magic key are the same
@ -1254,10 +1375,7 @@ void Arduboy2Core::exitToBootloader()
WDTCSR = (_BV(WDCE) | _BV(WDE)); WDTCSR = (_BV(WDCE) | _BV(WDE));
WDTCSR = _BV(WDE); WDTCSR = _BV(WDE);
while (true) { } while (true) { }
#else #endif
bootloader_timer = 120; //ms
while (true) { }
#endif
} }
// Replacement main() that eliminates the USB stack code. // Replacement main() that eliminates the USB stack code.

View File

@ -61,6 +61,9 @@ extern volatile unsigned char bootloader_timer;
#define DC_BIT PORTD4 // Display D/C physical bit number #define DC_BIT PORTD4 // Display D/C physical bit number
#ifdef CART_CS_SDA #ifdef CART_CS_SDA
#ifdef AB_ALTERNATE_WIRING
#error SDA can not be used as flash chip select when using Pro Micro alternate wiring. Use RX instead.
#endif
#define PIN_CART 2 // SDA as alternative flash cart chip select #define PIN_CART 2 // SDA as alternative flash cart chip select
#define CART_BIT PORTD1 #define CART_BIT PORTD1
#else #else
@ -462,7 +465,11 @@ class Arduboy2Core
*/ */
void static inline LCDDataMode() __attribute__((always_inline)) void static inline LCDDataMode() __attribute__((always_inline))
{ {
#if defined(GU12864_800B)
bitClear(DC_PORT, DC_BIT);
#else
bitSet(DC_PORT, DC_BIT); bitSet(DC_PORT, DC_BIT);
#endif
} }
/** \brief /** \brief
* Put the display into command mode. * Put the display into command mode.
@ -488,7 +495,11 @@ class Arduboy2Core
*/ */
void static inline LCDCommandMode() __attribute__((always_inline)) void static inline LCDCommandMode() __attribute__((always_inline))
{ {
#ifdef GU12864_800B
bitSet(DC_PORT, DC_BIT);
#else
bitClear(DC_PORT, DC_BIT); bitClear(DC_PORT, DC_BIT);
#endif
} }
/** \brief /** \brief
* Transfer a byte to the display. * Transfer a byte to the display.
@ -972,6 +983,12 @@ class Arduboy2Core
void static bootOLED(); void static bootOLED();
void static bootPins(); void static bootPins();
void static bootPowerSaving(); void static bootPowerSaving();
#if defined(GU12864_800B)
void static displayWrite(uint8_t d);
void static displayEnable();
void static displayDisable();
#endif
}; };
#endif #endif

View File

@ -44,83 +44,84 @@ void Sprites::draw(int16_t x, int16_t y,
if (bitmap == NULL) if (bitmap == NULL)
return; return;
// uint8_t width = pgm_read_byte(bitmap); uint8_t width = pgm_read_byte(bitmap);
// uint8_t height = pgm_read_byte(++bitmap); uint8_t height = pgm_read_byte(++bitmap);
// bitmap++; bitmap++;
// if (frame > 0 || sprite_frame > 0) { if (frame > 0 || sprite_frame > 0) {
// frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1))); frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));
// // sprite plus mask uses twice as much space for each frame // sprite plus mask uses twice as much space for each frame
// if (drawMode == SPRITE_PLUS_MASK) { if (drawMode == SPRITE_PLUS_MASK) {
// frame_offset *= 2; frame_offset *= 2;
// } else if (mask != NULL) { } else if (mask != NULL) {
// mask += sprite_frame * frame_offset; mask += sprite_frame * frame_offset;
// } }
// bitmap += 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 we're detecting the draw mode then base it on whether a mask
// if (drawMode == SPRITE_AUTO_MODE) { // was passed as a separate object
// drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED; if (drawMode == SPRITE_AUTO_MODE) {
// } drawMode = mask == NULL ? SPRITE_UNMASKED : SPRITE_MASKED;
}
// assembly optimisation of above code saving 20(+) bytes // assembly optimisation of above code saving 20(+) bytes
uint8_t width; // uint8_t width;
uint8_t height; // uint8_t height;
asm volatile( // asm volatile(
" lpm %[width], Z+ \n\t" // width = pgm_read_byte(bitmap++); // " lpm %[width], Z+ \n\t" // width = pgm_read_byte(bitmap++);
" lpm %[height], Z+ \n\t" // height = pgm_read_byte(bitmap++); // " lpm %[height], Z+ \n\t" // height = pgm_read_byte(bitmap++);
" cp %[frame], __zero_reg__ \n\t" // if (frame > 0 || sprite_frame > 0) // " cp %[frame], __zero_reg__ \n\t" // if (frame > 0 || sprite_frame > 0)
" brne 1f \n\t" // " brne 1f \n\t"
" cp %[spr_frame], __zero_reg__ \n\t" // " cp %[spr_frame], __zero_reg__ \n\t"
" breq 3f \n\t" // " breq 3f \n\t"
"1: \n\t" // "1: \n\t"
" ldi r20, 7 \n\t" //rows = ((height+7) // " ldi r20, 7 \n\t" //rows = ((height+7)
" add r20, %[height] \n\t" // " add r20, %[height] \n\t"
" ror r20 \n\t" //include carry for heights > 248 // " ror r20 \n\t" //include carry for heights > 248
" lsr r20 \n\t" //rows = (height+7) / 8 // " lsr r20 \n\t" //rows = (height+7) / 8
" lsr r20 \n\t" // " lsr r20 \n\t"
" cpi %[mode], %[sprite_plus_mask] \n\t" //if (drawMode == SPRITE_PLUS_MASK) rows *= 2 // " cpi %[mode], %[sprite_plus_mask] \n\t" //if (drawMode == SPRITE_PLUS_MASK) rows *= 2
" brne 2f \n\t" // " brne 2f \n\t"
" lsl r20 \n\t" // " lsl r20 \n\t"
"2: \n\t" // "2: \n\t"
" mul r20, %[width] \n\t" //frame offset = rows * width // " mul r20, %[width] \n\t" //frame offset = rows * width
" movw r20, r0 \n\t" // " movw r20, r0 \n\t"
" mul %[frame] , r20 \n\t" // " mul %[frame] , r20 \n\t"
" add %A[bitmap], r0 \n\t" //bitmap += frame * (frame offset & 0xFF) // " add %A[bitmap], r0 \n\t" //bitmap += frame * (frame offset & 0xFF)
" adc %B[bitmap], r1 \n\t" // " adc %B[bitmap], r1 \n\t"
" mul %[frame] , r21 \n\t" //bitmap += frame * (frame_offset >> 8 )) << 8 // " mul %[frame] , r21 \n\t" //bitmap += frame * (frame_offset >> 8 )) << 8
" add %B[bitmap], r0 \n\t" // " add %B[bitmap], r0 \n\t"
" \n\t" // " \n\t"
" adiw %A[mask], 0 \n\t" //if (mask != NULL) // " adiw %A[mask], 0 \n\t" //if (mask != NULL)
" breq 3f \n\t" // " breq 3f \n\t"
" \n\t" // " \n\t"
" mul %[spr_frame] , r20 \n\t" // " mul %[spr_frame] , r20 \n\t"
" add %A[mask], r0 \n\t" //mask += sprite_frame * (frame offset & 0xFF) // " add %A[mask], r0 \n\t" //mask += sprite_frame * (frame offset & 0xFF)
" adc %B[mask], r1 \n\t" // " adc %B[mask], r1 \n\t"
" mul %[spr_frame] , r21 \n\t" //mask += (sprite_frame * (frame_offset >> 8 )) << 8 // " mul %[spr_frame] , r21 \n\t" //mask += (sprite_frame * (frame_offset >> 8 )) << 8
" add %B[mask], r0 \n\t" // " add %B[mask], r0 \n\t"
"3: \n\t" // "3: \n\t"
" clr __zero_reg__ \n\t" // " clr __zero_reg__ \n\t"
"4: \n\t" // "4: \n\t"
" cpi %[mode], %[sprite_auto_mode] \n\t" //if (drawMode == SPRITE_AUTO_MODE) // " cpi %[mode], %[sprite_auto_mode] \n\t" //if (drawMode == SPRITE_AUTO_MODE)
" brne 5f \n\t" // " brne 5f \n\t"
" adiw %A[mask], 0 \n\t" //if (mask = NULL) drawMode = SPRITE_UNMASKED // " adiw %A[mask], 0 \n\t" //if (mask = NULL) drawMode = SPRITE_UNMASKED
" ldi %[mode], %[sprite_unmasked] \n\t" // " ldi %[mode], %[sprite_unmasked] \n\t"
" breq 5f \n\t" // " breq 5f \n\t"
" ldi %[mode], %[sprite_masked] \n\t" //else drawMode = SPRITE_PLUS_MASK // " ldi %[mode], %[sprite_masked] \n\t" //else drawMode = SPRITE_PLUS_MASK
"5: \n\t" // "5: \n\t"
: [width] "=&r" (width), // : [width] "=&r" (width),
[height] "=&r" (height), // [height] "=&r" (height),
[mask] "+x" (mask), // [mask] "+x" (mask),
[bitmap] "+z" (bitmap), // [bitmap] "+z" (bitmap),
[mode] "+d" (drawMode) // [mode] "+d" (drawMode)
: [frame] "r" (frame), // : [frame] "r" (frame),
[spr_frame] "r" (sprite_frame), // [spr_frame] "r" (sprite_frame),
[sprite_plus_mask] "M" (SPRITE_PLUS_MASK), // [sprite_plus_mask] "M" (SPRITE_PLUS_MASK),
[sprite_auto_mode] "M" (SPRITE_AUTO_MODE), // [sprite_auto_mode] "M" (SPRITE_AUTO_MODE),
[sprite_unmasked] "M" (SPRITE_UNMASKED), // [sprite_unmasked] "M" (SPRITE_UNMASKED),
[sprite_masked] "M" (SPRITE_MASKED) // [sprite_masked] "M" (SPRITE_MASKED)
: "r20", "r21" // : "r20", "r21"
); // );
drawBitmap(x, y, bitmap, mask, width, height, drawMode); drawBitmap(x, y, bitmap, mask, width, height, drawMode);
} }