/** * @file Arduboy2Core.h * \brief * The Arduboy2Core class for Arduboy hardware initilization and control. */ #ifndef ARDUBOY2_CORE_H #define ARDUBOY2_CORE_H #include #include #include #include #include // main hardware compile flags #if !defined(ARDUBOY_10) && !defined(AB_DEVKIT) /* defaults to Arduboy Release 1.0 if not using a boards.txt file * * we default to Arduboy Release 1.0 if a compile flag has not been * passed to us from a boards.txt file * * if you wish to compile for the devkit without using a boards.txt * file simply comment out the ARDUBOY_10 define and uncomment * the AB_DEVKIT define like this: * * // #define ARDUBOY_10 * #define AB_DEVKIT */ #define ARDUBOY_10 //< compile for the production Arduboy v1.0 // #define AB_DEVKIT //< compile for the official dev kit #endif #ifdef AB_DEVKIT #define SAFE_MODE //< include safe mode (44 bytes) #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() */ #ifdef ARDUBOY_10 #define CS 12 #define DC 4 #define RST 6 #define RED_LED 10 /**< The pin number for the red color in the RGB LED. */ #define GREEN_LED 11 /**< The pin number for the greem color in the RGB LED. */ #define BLUE_LED 9 /**< The pin number for the blue color in the RGB LED. */ #define TX_LED 30 /**< The pin number for the transmit indicator LED. */ #define RX_LED 17 /**< The pin number for the receive indicator LED. */ // pin values for buttons, probably shouldn't use these #define PIN_LEFT_BUTTON A2 #define PIN_RIGHT_BUTTON A1 #define PIN_UP_BUTTON A0 #define PIN_DOWN_BUTTON A3 #define PIN_A_BUTTON 7 #define PIN_B_BUTTON 8 // bit values for button states #define LEFT_BUTTON _BV(5) /**< 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_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 PIN_SPEAKER_1_PORT &PORTC #define PIN_SPEAKER_2_PORT &PORTC #define PIN_SPEAKER_1_BITMASK _BV(6) #define PIN_SPEAKER_2_BITMASK _BV(7) #elif defined(AB_DEVKIT) #define CS 6 #define DC 4 #define RST 12 // map all LEDs to the single TX LED on DEVKIT #define RED_LED 17 #define GREEN_LED 17 #define BLUE_LED 17 #define TX_LED 17 #define RX_LED 17 // pin values for buttons, probably shouldn't use these #define PIN_LEFT_BUTTON 9 #define PIN_RIGHT_BUTTON 5 #define PIN_UP_BUTTON 8 #define PIN_DOWN_BUTTON 10 #define PIN_A_BUTTON A0 #define PIN_B_BUTTON A1 // bit values for button states #define LEFT_BUTTON _BV(5) #define RIGHT_BUTTON _BV(2) #define UP_BUTTON _BV(4) #define DOWN_BUTTON _BV(6) #define A_BUTTON _BV(1) #define B_BUTTON _BV(0) #define PIN_SPEAKER_1 A2 #define PIN_SPEAKER_1_PORT &PORTF #define PIN_SPEAKER_1_BITMASK _BV(5) // 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 #endif // OLED hardware (SSD1306) #define OLED_PIXELS_INVERTED 0xA7 // All pixels inverted #define OLED_PIXELS_NORMAL 0xA6 // All pixels normal #define OLED_ALL_PIXELS_ON 0xA5 // all pixels on #define OLED_PIXELS_FROM_RAM 0xA4 // pixels mapped to display RAM contents #define OLED_VERTICAL_FLIPPED 0xC0 // reversed COM scan direction #define OLED_VERTICAL_NORMAL 0xC8 // normal COM scan direction #define OLED_HORIZ_FLIPPED 0xA0 // reversed segment re-map #define OLED_HORIZ_NORMAL 0xA1 // normal segment re-map // ----- #define WIDTH 128 /**< The width of the display in pixels */ #define HEIGHT 64 /**< The height of the display in pixels */ #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. */ void static LCDDataMode(); /** \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 sendLCDCommand() */ void static LCDCommandMode(); /** \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 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 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 setRGBled() */ void static digitalWriteRGB(uint8_t red, uint8_t green, uint8_t blue); /** \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(); protected: /* * Safe Mode is engaged by holding down both the LEFT button and UP button * when plugging the device into USB. It puts your device into a tight * loop and allows it to be reprogrammed even if you have uploaded a very * broken sketch that interferes with the normal USB triggered auto-reboot * functionality of the device. * * This is most useful on Devkits because they lack a built-in reset * button. */ void static inline safeMode() __attribute__((always_inline)); // internals void static inline setCPUSpeed8MHz() __attribute__((always_inline)); void static inline bootOLED() __attribute__((always_inline)); void static inline bootPins() __attribute__((always_inline)); void static inline bootPowerSaving() __attribute__((always_inline)); private: volatile static uint8_t *csport, *dcport; uint8_t static cspinmask, dcpinmask; }; #endif