diff --git a/.gitignore b/.gitignore index e9ca720..264b480 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ -*.lib -*.o -*.dSYM +# Build directory +build/ + +# Temp files +.DS_Store +.Trashes +Thumbs.db +Desktop.ini + +# Vim swap files +*.sw* + +# Backup files +*~ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e665684 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.4) +project(tinysim) + +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +include(${PROJECT_SOURCE_DIR}/cmake/ArduinoToCXX.cmake) + +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIR}) + +include_directories(${PROJECT_SOURCE_DIR}/include) + +add_subdirectory(src) +add_subdirectory(examples/tinytest) diff --git a/README.md b/README.md index 7583c86..6be3a8d 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ # Tiny Arcade Simulator -This is an embryo of a simulator for Tiny Arcade, to simplify development +This is an embryo of a simulator for Tiny Arcade, to simplify development of new and cool games. ## Goal -The goal is to be able to compile and run the same code for both the real -TinyArcade hardware and the simulator. +The goal is to be able to compile and run the same code for both the real +TinyArcade hardware and the simulator. ## Implementation -To allow users on different platforms I chose to implemented it using SDL2. +To allow users on different platforms I chose to implemented it using SDL2. ### Current status @@ -29,5 +29,19 @@ To allow users on different platforms I chose to implemented it using SDL2. ### Long term idea While coding, I realised that I can make this much more dynamic and usefull. -Having a library of modules and a UI, it would be possible to simulate many +Having a library of modules and a UI, it would be possible to simulate many different setups of hardware. + +## Building + +Requires [CMake >=3.4](https://cmake.org/). + +* **Windows** : See https://cmake.org/runningcmake/ +* **Linux**, **BSD**, **OSX** : See below + +```bash +mkdir -p build +cd build +cmake .. +make +``` diff --git a/cmake/ArduinoToCXX.cmake b/cmake/ArduinoToCXX.cmake new file mode 100644 index 0000000..0a8e0bc --- /dev/null +++ b/cmake/ArduinoToCXX.cmake @@ -0,0 +1,30 @@ +# Transform a list of arduino source filenames to C++ filenames +# This macro copies all *.ino files into the binary directory with extension .cpp +# Other files are passed through unmodified +# +# Example: +# set(SRCS foo.ino bar.cpp) +# arduino_to_cxx(SRCS ${SRCS}) +# add_executable(foo ${SRCS}) + +macro(arduino_to_cxx output_var input) + # Add the current list directory for include searching + # This is required when a file is moved to the binary directory + include_directories(${CMAKE_CURRENT_LIST_DIR}) + + foreach(f ${input}) + string(REGEX MATCH "\.ino$" matches ${input}) + if(matches) + # Get the new filename + string(REGEX REPLACE "^(.*)\.ino$" "${CMAKE_CURRENT_BINARY_DIR}/\\1.cpp" newname ${f}) + + # Replace the filename in the output list + list(REMOVE_ITEM ${output_var} ${f}) + list(APPEND ${output_var} ${newname}) + + # Move the file into the binary directory + file(COPY ${f} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + file(RENAME ${CMAKE_CURRENT_BINARY_DIR}/${f} ${newname}) + endif(matches) + endforeach(f) +endmacro(arduino_to_cxx) diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake new file mode 100644 index 0000000..3de27ea --- /dev/null +++ b/cmake/FindSDL2.cmake @@ -0,0 +1,164 @@ + +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDLmain.h and SDLmain.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2DIR is an environment variable that would +# correspond to the ./configure --prefix=$SDL2DIR +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL convention +# is #include "SDL.h", not . This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +SET(SDL2_SEARCH_PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + ${SDL2_PATH} +) + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES include/SDL2 include + PATHS ${SDL2_SEARCH_PATHS} +) + +FIND_LIBRARY(SDL2_LIBRARY_TEMP + NAMES SDL2 + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS ${SDL2_SEARCH_PATHS} +) + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS ${SDL2_SEARCH_PATHS} + ) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) diff --git a/examples/tinytest/CMakeLists.txt b/examples/tinytest/CMakeLists.txt new file mode 100644 index 0000000..7cd24d6 --- /dev/null +++ b/examples/tinytest/CMakeLists.txt @@ -0,0 +1,8 @@ +set(SRCS + tinytest.ino +) +arduino_to_cxx(SRCS ${SRCS}) + +add_executable(tinytest ${SRCS}) + +target_link_libraries(tinytest tinysim ${SDL2_LIBRARY}) diff --git a/examples/tinytest/tinytest.ino b/examples/tinytest/tinytest.ino new file mode 100644 index 0000000..4860703 --- /dev/null +++ b/examples/tinytest/tinytest.ino @@ -0,0 +1,88 @@ +#include "Arduino.h" +#include +#include + +#include + +TinyScreen display = TinyScreen(TinyScreenPlus); + +#define STAR_COUNT 300 + +struct Star +{ + int16_t x; // Fixed point 8.8 + int16_t y; // + int16_t speed; // Fixed point 8.8 +}; + +static struct Star stars[STAR_COUNT]; + + +static uint16_t myFrameBuffer [96*64]; + + +static void DrawStars() +{ + struct Star *star = stars; + for( int i = 0; i < STAR_COUNT; ++i, star++ ) + { + star->x -= star->speed; + star->x &= 0x7fff; + int16_t x = star->x >> 8; + if( (x >= 0) && (x < 96) ) + myFrameBuffer[ x + star->y * 96] = 0xffff; + } +} + +static void SendFrameBufferToDisplay() +{ + display.startData(); + display.writeBuffer((uint8_t*)myFrameBuffer, sizeof(myFrameBuffer)); + display.endTransfer(); +} + + + +static void fill_with_colors() +{ + uint16_t color = 0; + for( int row = 0; row < 64; ++row ) + { + for( int col = 0; col < 96; ++col ) + { + uint16_t bscol = color; + bscol = ((bscol&0xff) << 8) | (bscol >> 8); + myFrameBuffer[row*96+col] = bscol; + + color += 1; + } + } +} + +void loop() +{ + fill_with_colors(); + DrawStars(); + SendFrameBufferToDisplay(); +} + +void setup() +{ + for( int i = 0; i < STAR_COUNT; ++i ) + { + stars[i].x = rand() & 0x7fff; + stars[i].y = rand() & 0x3f; + stars[i].speed = rand() & 0x3ff; + } + + display.begin(); + display.setBrightness(10); // 0-15 + + display.setBitDepth(TSBitDepth16); + + // column start + display.setX(0, 95); + + // row start + display.setY(0, 63); +} diff --git a/include/TinyScreen.h b/include/TinyScreen.h new file mode 100644 index 0000000..4a6346e --- /dev/null +++ b/include/TinyScreen.h @@ -0,0 +1,172 @@ +#ifndef TinyScreen_h +#define TinyScreen_h + +#include +#include + +// Color definitions +// 8b: BBBG GGRR +const uint8_t TS_8b_Black = 0x00; +const uint8_t TS_8b_Gray = 0x6D; +const uint8_t TS_8b_White = 0xFF; +const uint8_t TS_8b_Blue = 0xE0; +const uint8_t TS_8b_DarkBlue = 0x60; +const uint8_t TS_8b_Red = 0x03; +const uint8_t TS_8b_DarkRed = 0x01; +const uint8_t TS_8b_Green = 0x1C; +const uint8_t TS_8b_DarkGreen = 0x0C; +const uint8_t TS_8b_Brown = 0x32; +const uint8_t TS_8b_DarkBrown = 0x22; +const uint8_t TS_8b_Yellow = 0x1F; + +// 16b: BBBB BGGG GGGR RRRR +const uint16_t TS_16b_Black = 0x0000; +const uint16_t TS_16b_Gray = 0x7BEF; +const uint16_t TS_16b_DarkGray = 0x39E7; +const uint16_t TS_16b_White = 0xFFFF; +const uint16_t TS_16b_Blue = 0xF800; +const uint16_t TS_16b_DarkBlue = 0x7800; +const uint16_t TS_16b_Red = 0x001F; +const uint16_t TS_16b_DarkRed = 0x000F; +const uint16_t TS_16b_Green = 0x07E0; +const uint16_t TS_16b_DarkGreen = 0x03E0; +const uint16_t TS_16b_Brown = 0x0C10; +const uint16_t TS_16b_DarkBrown = 0x0810; +const uint16_t TS_16b_Yellow = 0x07FF; + +// TinyScreen types +const uint8_t TinyScreenDefault = 0; +const uint8_t TinyScreenAlternate = 1; +const uint8_t TinyScreenPlus = 2; + +// TinyScreen Rectangle Fills +const uint8_t TSRectangleFilled = 1; +const uint8_t TSRectangleNoFill = 0; + +// TinyScreen bitDepths +const uint8_t TSBitDepth8 = 0; +const uint8_t TSBitDepth16 = 1; + +// TinyScreen Color Modes +const uint8_t TSColorModeBGR = 0; +const uint8_t TSColorModeRGB = 1; + +// TinyScreen button definitions +const uint8_t TSButtonUpperLeft = 1<<1; +const uint8_t TSButtonUpperRight = 1<<2; +const uint8_t TSButtonLowerLeft = 1<<0; +const uint8_t TSButtonLowerRight = 1<<3; + +// TinyScreen+ pin defintions +const uint8_t TSP_PIN_DC = 22; +const uint8_t TSP_PIN_CS = 38; +const uint8_t TSP_PIN_SHDN = 27; +const uint8_t TSP_PIN_RST = 26; + +const uint8_t TSP_PIN_BT1 = 19; +const uint8_t TSP_PIN_BT2 = 25; +const uint8_t TSP_PIN_BT3 = 30; +const uint8_t TSP_PIN_BT4 = 31; + +// GPIO Pins +const uint8_t GPIO_DC = 0x01; +const uint8_t GPIO_CS = 0x02; +const uint8_t GPIO_SHDN = 0x04; +const uint8_t GPIO_RES = 0x08; +const uint8_t GPIO_BTN1 = 0x10; +const uint8_t GPIO_BTN2 = 0x20; +const uint8_t GPIO_BTN3 = 0x40; +const uint8_t GPIO_BTN4 = 0x80; +const uint8_t GPIO_CMD_START = ~(GPIO_CS|GPIO_DC); +const uint8_t GPIO_DATA_START = ~GPIO_CS; +const uint8_t GPIO_TRANSFER_END = GPIO_CS|GPIO_SHDN; + +//GPIO Registers +const uint8_t GPIO_RegData = 0x00; +const uint8_t GPIO_RegDir = 0x01; +const uint8_t GPIO_RegPullUp = 0x02; +const uint8_t GPIO_RegInterruptMask = 0x05; +const uint8_t GPIO_RegSenseHigh = 0x06; +const uint8_t GPIO_RegInterruptSource = 0x08; + +const uint8_t GPIO_ADDR = 0x20; + +typedef struct +{ + const uint8_t width; + const uint16_t offset; + +} FONT_CHAR_INFO; + +typedef struct +{ + const unsigned char height; + const char startCh; + const char endCh; + const FONT_CHAR_INFO* charDesc; + const unsigned char* bitmap; + +} FONT_INFO; + +class TinyScreen { +public: + //init, control + TinyScreen(uint8_t); + void startData(void); + void startCommand(void); + void endTransfer(void); + void begin(void); + void on(void); + void off(void); + void setFlip(uint8_t); + void setMirror(uint8_t); + void setBitDepth(uint8_t); + void setColorMode(uint8_t); + void setBrightness(uint8_t); + void writeRemap(void); + //accelerated drawing commands + void drawPixel(uint8_t, uint8_t, uint16_t); + void drawLine(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); + void drawLine(uint8_t, uint8_t, uint8_t, uint8_t, uint16_t); + void drawRect(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); + void drawRect(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint16_t); + void clearWindow(uint8_t, uint8_t, uint8_t, uint8_t); + void clearScreen(void); + //basic graphics commands + void writePixel(uint16_t); + void writeBuffer(uint8_t *, int); + void setX(uint8_t, uint8_t); + void setY(uint8_t, uint8_t); + void goTo(uint8_t, uint8_t); + //I2C GPIO related + uint8_t getButtons(uint8_t); + uint8_t getButtons(void); + void writeGPIO(uint8_t, uint8_t); + //font + void setFont(const FONT_INFO&); + uint8_t getFontHeight(const FONT_INFO&); + uint8_t getFontHeight(void); + uint8_t getPrintWidth(char *); + void setCursor(uint8_t, uint8_t); + void fontColor(uint16_t, uint16_t); + virtual size_t write(uint8_t); + //DMA for SAMD + void initDMA(void); + uint8_t getReadyStatusDMA(void); + void writeBufferDMA(uint8_t *, int); + + static const uint8_t xMax=95; + static const uint8_t yMax=63; + + +private: + uint8_t _addr, _cursorX, _cursorY, _fontHeight, _fontFirstCh, _fontLastCh, _bitDepth, _flipDisplay, _mirrorDisplay, _colorMode, _externalIO, _type; +/* + uint16_t _fontColor, _fontBGcolor; + const FONT_CHAR_INFO* _fontDescriptor; + const unsigned char* _fontBitmap; + SPIClass *TSSPI; +*/ +}; + +#endif diff --git a/include/avr/pgmspace.h b/include/avr/pgmspace.h index f5f803b..672be61 100644 --- a/include/avr/pgmspace.h +++ b/include/avr/pgmspace.h @@ -4,7 +4,7 @@ #include #define PROGMEM - +#define PSTR(str) (str) extern uint8_t pgm_read_byte( const uint8_t* address ); extern uint16_t pgm_read_word( const uint16_t* address ); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..084020e --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,16 @@ +set(SRCS + simtinyscreen.cpp + simwire.cpp + spisim.cpp + tinysim.cpp + TinyScreen.cpp +) + +set(HEADERS + simtinyscreen.h + tinysim.h +) + +add_library(tinysim STATIC ${SRCS} ${HEADERS}) +set_property(TARGET tinysim PROPERTY CXX_STANDARD 11) +set_property(TARGET tinysim PROPERTY CXX_STANDARD_REQUIRED true) diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 6d00ffa..0000000 --- a/src/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -CC=gcc -CXX=clang++ -#CFLAGS= -O0 -g -std=c++11 -mmacosx-version-min=10.11 -Wextra -Wall -F/Library/Frameworks -I../include -I-../include -CFLAGS= -O0 -g -std=c++11 -mmacosx-version-min=10.9 -F/Library/Frameworks -I../include -I-../include -LIBS=-framework SDL2 -lc++ -lc -LDFLAGS=-macosx_version_min 10.9 - - -CFILES=tinysim.cpp spisim.cpp simtinyscreen.cpp simwire.cpp - -OBJS=$(CFILES:.cpp=.o) - -all: tinysim.lib tinytest - -tinytest: test.o tinysim.lib - $(LD) $(LDFLAGS) -o tinytest $(LIBS) $^ - -tinysim.lib: $(OBJS) - $(AR) rcs tinysim.lib $(OBJS) - -%.o: %.cpp - $(CXX) $(CFLAGS) -c -o $@ $< - -clean: - rm -f tinysim.lib tinytest $(OBJS) *.o - -.PHONY: all diff --git a/src/TinyScreen.cpp b/src/TinyScreen.cpp new file mode 100644 index 0000000..b11464d --- /dev/null +++ b/src/TinyScreen.cpp @@ -0,0 +1,253 @@ +#include "TinyScreen.h" + +#include +#include + +#include + +TinyScreen::TinyScreen(uint8_t type) { + _addr = 0; + _cursorX = 0; + _cursorY = 0; + _fontHeight = 0; + _fontFirstCh = 0; + _fontLastCh = 0; + _bitDepth = 0; + _flipDisplay = 0; + _mirrorDisplay = 0; + _colorMode = 0; + _externalIO = 0; + _type = 0; +} + +void TinyScreen::startData(void) { + writeGPIO(GPIO_RegData, GPIO_DATA_START); +} + +void TinyScreen::startCommand(void) { + writeGPIO(GPIO_RegData, GPIO_CMD_START); +} + +void TinyScreen::endTransfer(void) { + writeGPIO(GPIO_RegData, GPIO_TRANSFER_END); +} + +void TinyScreen::begin(void) { + setBrightness(5); + writeRemap(); + clearWindow(0, 0, 96, 64); + on(); +} + +void TinyScreen::on(void) { + startCommand(); + SPI.transfer(0xAF);//display on + endTransfer(); +} + +void TinyScreen::off(void) { + startCommand(); + SPI.transfer(0xAE);//display off + endTransfer(); + writeGPIO(GPIO_RegData,~GPIO_SHDN);//bost converter off +} + +void TinyScreen::setFlip(uint8_t flip) { + _flipDisplay = flip; + writeRemap(); +} + +void TinyScreen::setMirror(uint8_t mirror) { + _mirrorDisplay = mirror; + writeRemap(); +} + +void TinyScreen::setBitDepth(uint8_t depth) { + _bitDepth = depth; + writeRemap(); +} + +void TinyScreen::setColorMode(uint8_t mode) { + _colorMode = mode; + writeRemap(); +} + +void TinyScreen::setBrightness(unsigned char brightness) { + if (brightness > 15) + brightness = 15; + + startCommand(); + SPI.transfer(0x87);//set master current + SPI.transfer(brightness); + endTransfer(); +} + +void TinyScreen::writeRemap(void) { + uint8_t remap = (1 << 5) | (1 << 2); + + if (_flipDisplay) + remap |= ((1 << 4) | (1 << 1)); + + if (_mirrorDisplay) + remap ^= (1 << 1); + + if (_bitDepth) + remap |= (1 << 6); + + if (_colorMode) + remap ^= (1 << 2); + + startCommand(); + SPI.transfer(0xA0);//set remap + SPI.transfer(remap); + endTransfer(); +} + +void TinyScreen::drawPixel(uint8_t x, uint8_t y, uint16_t color) { + printf("TODO: drawPixel\n"); +} + +void TinyScreen::drawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t r, uint8_t g, uint8_t b) { + printf("TODO: drawLine\n"); +} + +void TinyScreen::drawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint16_t color) { + printf("TODO: drawLine\n"); +} + +void TinyScreen::drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t f, uint8_t r, uint8_t g, uint8_t b) { + printf("TODO: drawRect\n"); +} + +void TinyScreen::drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t f, uint16_t color) { + printf("TODO: drawRect\n"); +} + +void TinyScreen::clearWindow(uint8_t x, uint8_t y, uint8_t w, uint8_t h) { + if (x > xMax || y > yMax) + return; + + uint8_t x2 = x + w - 1; + uint8_t y2 = y + h - 1; + + if (x2 > xMax) + x2 = xMax; + + if (y2 > yMax) + y2 = yMax; + + startCommand(); + SPI.transfer(0x25);//clear window + SPI.transfer(x); + SPI.transfer(y); + SPI.transfer(x2); + SPI.transfer(y2); + endTransfer(); +} + +void TinyScreen::clearScreen(void) { + printf("TODO: clearScreen\n"); +} + +void TinyScreen::writePixel(uint16_t color) { + printf("TODO: writePixel\n"); +} + +void TinyScreen::writeBuffer(uint8_t *buffer, int count) { + for (int i = 0; i < count; i++) { + SPDR = buffer[i]; + } +} + +void TinyScreen::setX(unsigned char x, unsigned char end) { + if (x > xMax) + x = xMax; + + if (end > xMax) + end = xMax; + + startCommand(); + SPI.transfer(0x15);//set column + SPI.transfer(x); + SPI.transfer(end); + endTransfer(); +} + +void TinyScreen::setY(unsigned char y, unsigned char end) { + if (y > yMax) + y = yMax; + + if (end > yMax) + end = yMax; + + startCommand(); + SPI.transfer(0x75);//set row + SPI.transfer(y); + SPI.transfer(end); + endTransfer(); +} + +void TinyScreen::goTo(uint8_t x, uint8_t y) { + setX(x, xMax); + setY(y, yMax); +} + +uint8_t TinyScreen::getButtons(uint8_t buttonMask) { + printf("TODO: getButtons\n"); + return 0; +} + +uint8_t TinyScreen::getButtons(void) { + return getButtons(TSButtonUpperLeft | TSButtonUpperRight | TSButtonLowerLeft | TSButtonLowerRight); +} + +void TinyScreen::writeGPIO(uint8_t regAddr, uint8_t regData) { + Wire.beginTransmission(GPIO_ADDR); + Wire.write(regAddr); + Wire.write(regData); + Wire.endTransmission(); +} + +void TinyScreen::setFont(const FONT_INFO& fontInfo) { + printf("TODO: setFont\n"); +} + +uint8_t TinyScreen::getFontHeight(const FONT_INFO& fontInfo) { + return fontInfo.height; +} + +uint8_t TinyScreen::getFontHeight() { + printf("TODO: getFontHeight\n"); + return 0; +} + +uint8_t TinyScreen::getPrintWidth(char *st) { + printf("TODO: getPrintWidth\n"); + return 0; +} + +void TinyScreen::setCursor(uint8_t x, uint8_t y) { + printf("TODO: setCursor\n"); +} + +void TinyScreen::fontColor(uint16_t fg, uint16_t bg) { + printf("TODO: fontColor\n"); +} + +size_t TinyScreen::write(uint8_t ch) { + printf("TODO: write\n"); + return 1; +} + +void TinyScreen::initDMA(void) { + /* Nothing to do */ +} + +uint8_t TinyScreen::getReadyStatusDMA(void) { + /* Always ready */ + return 1; +} + +void TinyScreen::writeBufferDMA(uint8_t *buffer, int count) { + writeBuffer(buffer, count); +} diff --git a/src/simtinyscreen.cpp b/src/simtinyscreen.cpp index ff7ded9..3b3a712 100644 --- a/src/simtinyscreen.cpp +++ b/src/simtinyscreen.cpp @@ -18,6 +18,8 @@ SimTinyScreen::SimTinyScreen() columnPtr = 0; columnEnd = 0; + + expectedByteCount_ = -1; } void SimTinyScreen::i2cWriteData( uint8_t data ) @@ -35,7 +37,7 @@ uint8_t SimTinyScreen::i2cReadData( uint8_t reg ) { // This is just a guess on how it works. busState = 0; - return regs[reg]; // Upper bits are buttons, + return regs[reg]; // Upper bits are buttons, } uint8_t SimTinyScreen::spiSlaveWrite( uint8_t data ) @@ -55,7 +57,7 @@ static uint16_t GetColorByCombiningBuffer( const uint8_t* buffer, uint8_t mode, switch( mode ) { case 0: if( !reversed ) { - // Normal RGB + // Normal RGB uint8_t data = *buffer; color = (data&0xe0)<<8u; color |= (data&0x1c) << 6u; @@ -84,7 +86,7 @@ static uint16_t GetColorByCombiningBuffer( const uint8_t* buffer, uint8_t mode, } break; - case 2: + case 2: { uint8_t b0 = *buffer; uint8_t b1 = *(buffer+1); @@ -112,7 +114,7 @@ void SimTinyScreen::WriteDataByte( uint8_t data ) uint8_t colorMode = (colorModeRemapReg >> 6) & 3u; bool reverseColors = 0 != (colorModeRemapReg & (1u<<2)); bool writeColor = false; - uint16_t finalColor; + uint16_t finalColor; if( colorMode > 2 ) { @@ -165,7 +167,7 @@ void SimTinyScreen::WriteCommandByte( uint8_t data ) { commandBuffer[bufferIndex++] = data; - if( expectedByteCount_ == 0 ) + if( expectedByteCount_ < 0 ) { switch( data ) { @@ -308,32 +310,31 @@ void SimTinyScreen::WriteCommandByte( uint8_t data ) default: // Unknown command, what to do? + expectedByteCount_ = -1; break; - } + } } - // TODO: Save bytes to buffer. execute command when expected byte count is 0. if( expectedByteCount_ == 0 ) { ExecuteCommandInBuffer( ); bufferIndex = 0; - } else { - --expectedByteCount_; } + --expectedByteCount_; } void SimTinyScreen::ExecuteCommandInBuffer() { auto buffPtr = commandBuffer; - auto command = *buffPtr++; + auto command = *buffPtr++; switch( command ) { case 0x15: { - // Set column address + // Set column address columnStart = *buffPtr++; columnEnd = *buffPtr++; columnPtr = columnStart; @@ -344,7 +345,7 @@ void SimTinyScreen::ExecuteCommandInBuffer() case 0x75: { - // Set row address + // Set row address rowStart = *buffPtr++; rowEnd = *buffPtr++; rowPtr = rowStart; @@ -357,6 +358,7 @@ void SimTinyScreen::ExecuteCommandInBuffer() { // Remap and color depth settings. colorModeRemapReg = *buffPtr; + printf("set color remap: $%02x\n", colorModeRemapReg); colorWriteCounter = 0u; } break; @@ -365,13 +367,12 @@ void SimTinyScreen::ExecuteCommandInBuffer() default: printf("SSD1331 command not implemented: $%02x", command); { - int i = 1; + int i = 1; while( i < bufferIndex ) { printf(", $%02x", commandBuffer[i++] ); } printf("\n" ); - } break; } -} \ No newline at end of file +} diff --git a/src/simtinyscreen.h b/src/simtinyscreen.h index a8e429b..33d9f9f 100644 --- a/src/simtinyscreen.h +++ b/src/simtinyscreen.h @@ -20,7 +20,7 @@ class SimTinyScreen : public ISPIDevice, public II2CSlaveDevice virtual uint8_t spiSlaveWrite( uint8_t ); - // I2C Slave + // I2C Slave virtual void i2cWriteData( uint8_t ); virtual uint8_t i2cReadData( uint8_t ) ; @@ -41,9 +41,9 @@ class SimTinyScreen : public ISPIDevice, public II2CSlaveDevice // Control register - uint8_t expectedByteCount_; + int8_t expectedByteCount_; - uint8_t currentCommand; // + uint8_t currentCommand; // uint8_t colorWriteCounter; uint16_t finalColor; @@ -71,4 +71,3 @@ class SimTinyScreen : public ISPIDevice, public II2CSlaveDevice }; #endif - diff --git a/src/spisim.cpp b/src/spisim.cpp index 32adcf2..1c47509 100644 --- a/src/spisim.cpp +++ b/src/spisim.cpp @@ -22,13 +22,16 @@ SimSPI::SimSPI() { devicesArraySize = 4; devices = new ISPIDevice*[devicesArraySize]; + for (int i = 0; i < devicesArraySize; i++) { + devices[i] = NULL; + } } void SimSPI::AddDevice( ISPIDevice *device ) { int i = 0; - while( (i < devicesArraySize) && (devices[i] == NULL) ) + while( (i < devicesArraySize) && (devices[i] != NULL) ) i++; if( i < devicesArraySize ) @@ -112,5 +115,3 @@ uint16_t millis() auto now = std::chrono::high_resolution_clock::now().time_since_epoch(); return (uint16_t)std::chrono::duration_cast(now).count(); } - - diff --git a/src/test.cpp b/src/test.cpp deleted file mode 100644 index 11a4aa0..0000000 --- a/src/test.cpp +++ /dev/null @@ -1,153 +0,0 @@ -//#include "tinysim.h" -#include -#include - -#include "Arduino.h" -#include -#include - - -#define SCREEN_I2C_ADDR 0x20 -#define GPIO_EXP_REG_DATA 0x0 -#define GPIO_EXP_REG_DIR 0x01 -#define GPIO_EXP_REG_PULLUP 0x02 -#define GPIO_EXP_REG_PULLDOWN 0x03 - - -#define PIN_DC 0x01 -#define PIN_CS 0x02 -#define PIN_SHDN 0x04 -#define PIN_RES 0x08 -#define PIN_BTN1 0x10 -#define PIN_BTN2 0x20 -#define PIN_BTN3 0x40 -#define PIN_BTN4 0x80 - -#define STAR_COUNT 300 - -struct Star -{ - int16_t x; // Fixed point 8.8 - int16_t y; // - int16_t speed; // Fixed point 8.8 -}; - -static struct Star stars[STAR_COUNT]; - - -static uint16_t myFrameBuffer [96*64]; - - -static void DrawStars() -{ - struct Star *star = stars; - for( int i = 0; i < STAR_COUNT; ++i, star++ ) - { - star->x -= star->speed; - star->x &= 0x7fff; - int16_t x = star->x >> 8; - if( (x >= 0) && (x < 96) ) - myFrameBuffer[ x + star->y * 96] = 0xffff; - } -} - -static void SendFrameBufferToDisplay() -{ - Wire.beginTransmission(SCREEN_I2C_ADDR); - Wire.write( 0 ); // First write selects register - Wire.write( 0x1 ); // Bit 0 selects command/data write, bit 1 Chip select, active low - Wire.endTransmission(); - - - uint8_t *buff = (uint8_t*) myFrameBuffer; - for(int i = sizeof(myFrameBuffer); i > 0 ; --i) - { - SPDR = *buff++; - while(!(SPSR & _BV(SPIF))); - } - - Wire.beginTransmission(SCREEN_I2C_ADDR); - Wire.write( 0 ); // First write selects register - Wire.write( 0xff ); // Bit 0 selects command/data write, bit 1 Chip select, active low - Wire.endTransmission(); -} - - - -static void fill_with_colors() -{ - uint16_t color = 0; - for( int row = 0; row < 64; ++row ) - { - for( int col = 0; col < 96; ++col ) - { - uint16_t bscol = color; - bscol = ((bscol&0xff) << 8) | (bscol >> 8); - myFrameBuffer[row*96+col] = bscol; - - color += 1; - } - } -} - -void loop() -{ - fill_with_colors(); - DrawStars(); - SendFrameBufferToDisplay(); -} - -static void setup_gpio_expander() -{ - Wire.beginTransmission(SCREEN_I2C_ADDR); - - // Setup pullup register - Wire.write( GPIO_EXP_REG_PULLUP ); // First write selects register - Wire.write( (PIN_BTN1|PIN_BTN2|PIN_BTN3|PIN_BTN4) ); // Top four bits are buttons, - - - Wire.write( GPIO_EXP_REG_DATA ); // First write selects register - Wire.write( ~0x03 ); // Bit 0 selects command/data write, bit 1 Chip select, active low - - - Wire.write( GPIO_EXP_REG_DATA ); // First write selects register - Wire.write( ~0x03 ); // Bit 0 selects command/data write, bit 1 Chip select, active low - Wire.endTransmission(); - -} - -void setup() -{ - for( int i = 0; i < STAR_COUNT; ++i ) - { - stars[i].x = rand() & 0x7fff; - stars[i].y = rand() & 0x3f; - stars[i].speed = rand() & 0x3ff; - } - - Wire.beginTransmission(SCREEN_I2C_ADDR); - Wire.write( GPIO_EXP_REG_DATA ); // First write selects register - Wire.write( ~0x03 ); // Bit 0 selects command/data write, bit 1 Chip select, active low - Wire.endTransmission(); - - - SPI.transfer( 0xA0 ); // remap - SPI.transfer( (1<<6) ); - - // column start - SPI.transfer( 0x15 ); - SPI.transfer( 0 ); - SPI.transfer( 95 ); - - // row start - SPI.transfer( 0x75 ); - SPI.transfer( 0 ); - SPI.transfer( 63 ); - - Wire.beginTransmission(SCREEN_I2C_ADDR); - Wire.write( GPIO_EXP_REG_DATA ); // First write selects register - Wire.write( ~0x03 ); // Bit 0 selects command/data write, bit 1 Chip select, active low - Wire.endTransmission(); - -} - diff --git a/src/tinysim.cpp b/src/tinysim.cpp index 706737d..943d953 100644 --- a/src/tinysim.cpp +++ b/src/tinysim.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -28,13 +28,13 @@ SimSerial Serial; SimSPI SPI; SimWire Wire; -static SimTinyScreen display; +static SimTinyScreen simdisplay; int main(void) { printf("Starting TinySim\n"); - SPI.AddDevice( &display ); - Wire.AddDevice( &display, 0x20 ); + SPI.AddDevice( &simdisplay ); + Wire.AddDevice( &simdisplay, 0x20 ); int result = TinySimRun( ); @@ -64,7 +64,7 @@ int TinySimRun( ) uint32_t start = SDL_GetTicks(); loop(); - uint16_t* dispBuff = display.GetScreenBuffer(); + uint16_t* dispBuff = simdisplay.GetScreenBuffer(); TinySimBlit16( dispBuff ); SDL_Surface *dstSurface = SDL_GetWindowSurface(window); @@ -113,13 +113,13 @@ static void TinySimBlit16( const uint16_t* buffer ) for(int row = 0 ; row < surface->h; ++row ) { - uint32_t *rowData = frameBuffer; + uint32_t *rowData = frameBuffer; for( int x = 0 ; x < surface->w; ++x ) { uint16_t pixelData = *buffer++; // We need to convert the pixel data here. - // Since I'm not sure about the pixel format - // on the Tiny Arcade, I will pretend that I + // Since I'm not sure about the pixel format + // on the Tiny Arcade, I will pretend that I // know uint8_t r = (pixelData>>8) & 0xf8; @@ -138,7 +138,7 @@ static void TinySimBlit8( const uint8_t* buffer ) for(int row = 0 ; row < surface->h; ++row ) { - uint32_t *rowData = frameBuffer; + uint32_t *rowData = frameBuffer; for( int x = 0 ; x < surface->w; ++x ) { uint16_t pixelData = *buffer++; @@ -191,4 +191,3 @@ static void TinySimQuit() SDL_Quit(); } -