diff --git a/CMakeLists.txt b/CMakeLists.txt index ca7178b..4bdb555 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,6 @@ set(CMAKE_CXX_STANDARD 20) if (NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) message("Using by default the Release build") - message("To build the Debug version pass the option -DCMAKE_BUILD_TYPE=Debug to CMake") endif () # Include the Raspberry Pi Pico SDK import script @@ -21,8 +20,10 @@ project(tic-tac-toe C CXX ASM) pico_sdk_init() # Add executable -add_executable(tic-tac-toe main.cpp Game.cpp Move.cpp LCD_I2C.cpp TM1637.cpp Keypad.cpp Player.cpp BoardManager.cpp IPlayerStrategy.cpp) -pico_generate_pio_header(tic-tac-toe ${CMAKE_CURRENT_LIST_DIR}/TM1637.pio) +file(GLOB TIC_TAC_TOE_SOURCES "src/*.cpp") +add_executable(tic-tac-toe ${TIC_TAC_TOE_SOURCES}) +target_include_directories(tic-tac-toe PRIVATE include) +pico_generate_pio_header(tic-tac-toe ${CMAKE_CURRENT_LIST_DIR}/pio/TM1637.pio) # Add program info pico_set_program_name(tic-tac-toe "Tic-Tac-Toe LCD Game") @@ -45,9 +46,9 @@ pico_set_float_implementation(tic-tac-toe pico) pico_set_double_implementation(tic-tac-toe pico) # Set Debug build compiler arguments -set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wfatal-errors -Wpedantic -Wall -Wextra -Wshadow=local -Wdouble-promotion -Wformat=2 -Wformat-overflow=2 -Wformat-nonliteral -Wformat-security -Wformat-truncation=2 -Wnull-dereference -Wimplicit-fallthrough=3 -Wshift-overflow=2 -Wswitch-default -Wunused-parameter -Wunused-const-variable=2 -Wstrict-overflow=4 -Wstringop-overflow=3 -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wmissing-noreturn -Wsuggest-attribute=malloc -Wsuggest-attribute=format -Wmissing-format-attribute -Wsuggest-attribute=cold -Walloc-zero -Walloca -Wattribute-alias=2 -Wduplicated-branches -Wcast-qual") -set(CMAKE_C_FLAGS_DEBUG "-g -O0 -Wfatal-errors -Wpedantic -Wall -Wextra -Wshadow=local -Wdouble-promotion -Wformat=2 -Wformat-overflow=2 -Wformat-nonliteral -Wformat-security -Wformat-truncation=2 -Wnull-dereference -Wimplicit-fallthrough=3 -Wshift-overflow=2 -Wswitch-default -Wunused-parameter -Wunused-const-variable=2 -Wstrict-overflow=4 -Wstringop-overflow=3 -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wmissing-noreturn -Wsuggest-attribute=malloc -Wsuggest-attribute=format -Wmissing-format-attribute -Wsuggest-attribute=cold -Walloc-zero -Walloca -Wattribute-alias=2 -Wduplicated-branches -Wcast-qual") +set(CMAKE_CXX_FLAGS_DEBUG "-pipe -g -O0 -Wfatal-errors -Wpedantic -Wall -Wextra -Wshadow=local -Wdouble-promotion -Wformat=2 -Wformat-overflow=2 -Wformat-nonliteral -Wformat-security -Wformat-truncation=2 -Wnull-dereference -Wimplicit-fallthrough=3 -Wshift-overflow=2 -Wswitch-default -Wunused-parameter -Wunused-const-variable=2 -Wstrict-overflow=4 -Wstringop-overflow=3 -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wmissing-noreturn -Wsuggest-attribute=malloc -Wsuggest-attribute=format -Wmissing-format-attribute -Wsuggest-attribute=cold -Walloc-zero -Walloca -Wattribute-alias=2 -Wduplicated-branches -Wcast-qual") +set(CMAKE_C_FLAGS_DEBUG "-pipe -g -O0 -Wfatal-errors -Wpedantic -Wall -Wextra -Wshadow=local -Wdouble-promotion -Wformat=2 -Wformat-overflow=2 -Wformat-nonliteral -Wformat-security -Wformat-truncation=2 -Wnull-dereference -Wimplicit-fallthrough=3 -Wshift-overflow=2 -Wswitch-default -Wunused-parameter -Wunused-const-variable=2 -Wstrict-overflow=4 -Wstringop-overflow=3 -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wmissing-noreturn -Wsuggest-attribute=malloc -Wsuggest-attribute=format -Wmissing-format-attribute -Wsuggest-attribute=cold -Walloc-zero -Walloca -Wattribute-alias=2 -Wduplicated-branches -Wcast-qual") # Set Release build compiler arguments -set(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wfatal-errors -Wpedantic -Wall -Wextra -Wshadow=local -Wdouble-promotion -Wformat=2 -Wformat-overflow=2 -Wformat-nonliteral -Wformat-security -Wformat-truncation=2 -Wnull-dereference -Wimplicit-fallthrough=3 -Wshift-overflow=2 -Wswitch-default -Wunused-parameter -Wunused-const-variable=2 -Wstrict-overflow=4 -Wstringop-overflow=3 -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wmissing-noreturn -Wsuggest-attribute=malloc -Wsuggest-attribute=format -Wmissing-format-attribute -Wsuggest-attribute=cold -Walloc-zero -Walloca -Wattribute-alias=2 -Wduplicated-branches -Wcast-qual -fno-builtin") -set(CMAKE_C_FLAGS_RELEASE "-O2 -Wfatal-errors -Wpedantic -Wall -Wextra -Wshadow=local -Wdouble-promotion -Wformat=2 -Wformat-overflow=2 -Wformat-nonliteral -Wformat-security -Wformat-truncation=2 -Wnull-dereference -Wimplicit-fallthrough=3 -Wshift-overflow=2 -Wswitch-default -Wunused-parameter -Wunused-const-variable=2 -Wstrict-overflow=4 -Wstringop-overflow=3 -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=noreturn -Wmissing-noreturn -Wsuggest-attribute=malloc -Wsuggest-attribute=format -Wmissing-format-attribute -Wsuggest-attribute=cold -Walloc-zero -Walloca -Wattribute-alias=2 -Wduplicated-branches -Wcast-qual -fno-builtin") \ No newline at end of file +set(CMAKE_CXX_FLAGS_RELEASE "-pipe -O2 -fno-builtin") +set(CMAKE_C_FLAGS_RELEASE "-pipe -O2 -fno-builtin") diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ca7adc --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Raspberry Pi Pico - Tic Tac Toe Game + +## Build + +After you clone the repo you run the following commands to build the executable. You have to have installed *CMake* and *Make*. Also you need to have the SDK on your system and point `PICO_SDK_PATH` to it. +```sh +export PICO_SDK_PATH='/Path/to/SDK' +``` +```sh +cd tic-tac-toe +``` +```sh +mkdir build +``` +```sh +cd build +``` +```sh +cmake .. +``` +```sh +make -j4 +``` +### How to connect the LCD, LEDs and Keypad to the board +![Fritzing drawing](img/fritzing.png) diff --git a/img/fritzing.png b/img/fritzing.png new file mode 100644 index 0000000..fda3c8d Binary files /dev/null and b/img/fritzing.png differ diff --git a/BoardManager.hpp b/include/BoardManager.hpp similarity index 100% rename from BoardManager.hpp rename to include/BoardManager.hpp diff --git a/Game.hpp b/include/Game.hpp similarity index 90% rename from Game.hpp rename to include/Game.hpp index caa031d..9b4e58e 100644 --- a/Game.hpp +++ b/include/Game.hpp @@ -31,14 +31,16 @@ class Game final { private: - static constexpr LCD_I2C::byte LOCATION_LEFT = 0; - static constexpr LCD_I2C::byte LOCATION_CENTER = 1; - static constexpr LCD_I2C::byte LOCATION_RIGHT = 2; - static constexpr LCD_I2C::byte LOCATION_X = 3; - static constexpr LCD_I2C::byte LOCATION_0 = 4; - static constexpr LCD_I2C::byte LOCATION_SPACE = 5; + using byte = uint8_t; - static constexpr LCD_I2C::byte TEXT_START_COLUMN = 8; + static constexpr byte LOCATION_LEFT = 0; + static constexpr byte LOCATION_CENTER = 1; + static constexpr byte LOCATION_RIGHT = 2; + static constexpr byte LOCATION_X = 3; + static constexpr byte LOCATION_0 = 4; + static constexpr byte LOCATION_SPACE = 5; + + static constexpr byte TEXT_START_COLUMN = 8; std::unique_ptr first_player {std::make_unique(Utility::PlayerSymbol::UNK, new HumanStrategy)}; std::unique_ptr second_player; @@ -61,7 +63,7 @@ class Game final * @param symbol The piece to be converted * @return The resulting memory location */ - inline static auto LCD_Char_Location_From_Player_Symbol(Utility::PlayerSymbol symbol) noexcept -> LCD_I2C::byte; + inline static auto LCD_Char_Location_From_Player_Symbol(Utility::PlayerSymbol symbol) noexcept -> byte; /** * Draws on the LCD the game board. diff --git a/IPlayerStrategy.hpp b/include/IPlayerStrategy.hpp similarity index 100% rename from IPlayerStrategy.hpp rename to include/IPlayerStrategy.hpp diff --git a/Keypad.hpp b/include/Keypad.hpp similarity index 100% rename from Keypad.hpp rename to include/Keypad.hpp diff --git a/LCD_I2C.hpp b/include/LCD_I2C.hpp similarity index 90% rename from LCD_I2C.hpp rename to include/LCD_I2C.hpp index a51dca4..7d61443 100644 --- a/LCD_I2C.hpp +++ b/include/LCD_I2C.hpp @@ -1,10 +1,10 @@ /******************************************************************************* - * @file Lcd.hpp + * @file LCD_I2C.hpp * @author Cristian Cristea - * @date July 08, 2021 + * @date September 27, 2021 * @brief Header file for the LCD_I2C class. * - * @copyright Copyright (C) 2021 Cristian Cristea. All rights reserved. + * @copyright Copyright (C) 2021 Cristian Cristea ******************************************************************************/ #pragma once @@ -18,13 +18,12 @@ class LCD_I2C final { - public: - using byte = uint8_t; - static constexpr size_t CUSTOM_SYMBOL_SIZE = 8; - private: + using byte = uint8_t; + // Commands + static constexpr byte CLEAR_DISPLAY = 0x01; static constexpr byte RETURN_HOME = 0x02; static constexpr byte ENTRY_MODE_SET = 0x04; @@ -35,12 +34,14 @@ class LCD_I2C final static constexpr byte SET_DDRAM_ADDR = 0x80; // Flags for display entry mode set + static constexpr byte ENTRY_RIGHT = 0x00; static constexpr byte ENTRY_LEFT = 0x02; static constexpr byte ENTRY_SHIFT_INCREMENT = 0x01; static constexpr byte ENTRY_SHIFT_DECREMENT = 0x00; // Flags for display on/off control + static constexpr byte DISPLAY_ON = 0x04; static constexpr byte DISPLAY_OFF = 0x00; static constexpr byte CURSOR_ON = 0x02; @@ -49,12 +50,14 @@ class LCD_I2C final static constexpr byte BLINK_OFF = 0x00; // Flags for cursor or display shift + static constexpr byte DISPLAY_MOVE = 0x08; static constexpr byte CURSOR_MOVE = 0x00; static constexpr byte MOVE_RIGHT = 0x04; static constexpr byte MOVE_LEFT = 0x00; // Flags for function set + static constexpr byte MODE_8_BIT = 0x10; static constexpr byte MODE_4_BIT = 0x00; static constexpr byte LINE_2 = 0x08; @@ -63,16 +66,25 @@ class LCD_I2C final static constexpr byte DOTS_5x8 = 0x00; // Flags for backlight control + static constexpr byte BACKLIGHT = 0x08; static constexpr byte NO_BACKLIGHT = 0x00; // Special flags + static constexpr byte ENABLE = 0x04; static constexpr byte READ_WRITE = 0x02; static constexpr byte REGISTER_SELECT = 0x01; static constexpr byte COMMAND = 0x00; static constexpr byte CHAR = 0x01; + public: + + static constexpr byte CUSTOM_SYMBOL_SIZE = 8; + using array = std::array; + + private: + byte address {}; byte columns {}; byte rows {}; @@ -81,7 +93,7 @@ class LCD_I2C final byte display_control {}; byte display_mode {}; - i2c_inst * I2C_instance = nullptr; + i2c_inst * I2C_instance {nullptr}; /** * Wrapper function for SDK's internal I2C protocol write function. @@ -181,9 +193,9 @@ class LCD_I2C final void BacklightOff() noexcept; /** - * Sets the backlight light_on/off according to the parameter. + * Sets the backlight light on/off according to the parameter. * - * @param light_on True for light_on and False for off + * @param light_on True for light on and False for off */ void SetBacklight(bool light_on) noexcept; @@ -217,26 +229,6 @@ class LCD_I2C final */ void SetTextRightToLeft() noexcept; - /** - * Right justify the text. - */ - void JustifyRight() noexcept; - - /** - * Left justify the text. - */ - void JustifyLeft() noexcept; - - /** - * Scrolls the display to the left. - */ - void ScrollDisplayLeft() const noexcept; - - /** - * Scrolls the display to the right. - */ - void ScrollDisplayRight() const noexcept; - /** * Clears the display and sets cursor's position at the beginning of the * screen. @@ -289,6 +281,6 @@ class LCD_I2C final * @param location The memory address * @param char_map The byte array */ - void CreateCustomChar(byte location, std::array char_map) const noexcept; + void CreateCustomChar(byte location, array char_map) const noexcept; }; diff --git a/Move.hpp b/include/Move.hpp similarity index 100% rename from Move.hpp rename to include/Move.hpp diff --git a/Player.hpp b/include/Player.hpp similarity index 100% rename from Player.hpp rename to include/Player.hpp diff --git a/TM1637.hpp b/include/TM1637.hpp similarity index 100% rename from TM1637.hpp rename to include/TM1637.hpp diff --git a/Utility.hpp b/include/Utility.hpp similarity index 100% rename from Utility.hpp rename to include/Utility.hpp diff --git a/TM1637.pio b/pio/TM1637.pio similarity index 100% rename from TM1637.pio rename to pio/TM1637.pio diff --git a/BoardManager.cpp b/src/BoardManager.cpp similarity index 100% rename from BoardManager.cpp rename to src/BoardManager.cpp diff --git a/Game.cpp b/src/Game.cpp similarity index 95% rename from Game.cpp rename to src/Game.cpp index af42cf2..85bfb6d 100644 --- a/Game.cpp +++ b/src/Game.cpp @@ -19,7 +19,7 @@ Game::Game(LCD_I2C * lcd, TM1637 * led_segments, Keypad * keypad) noexcept { static constexpr size_t NO_SYMBOLS = 6; - static constexpr std::array, NO_SYMBOLS> CUSTOM_SYMBOLS + static constexpr std::array, NO_SYMBOLS> CUSTOM_SYMBOLS {{{0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x07}, /* LEFT */ {0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1F}, /* CENTER */ {0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1C}, /* RIGHT */ @@ -44,7 +44,7 @@ void Game::Init_Second_Core() const noexcept multicore_fifo_push_blocking(reinterpret_cast(led_segments.get())); } -auto Game::LCD_Char_Location_From_Player_Symbol(PlayerSymbol symbol) noexcept -> LCD_I2C::byte +auto Game::LCD_Char_Location_From_Player_Symbol(PlayerSymbol symbol) noexcept -> byte { switch (symbol) { @@ -59,10 +59,10 @@ auto Game::LCD_Char_Location_From_Player_Symbol(PlayerSymbol symbol) noexcept -> inline void Game::Draw_Game() const noexcept { - static constexpr LCD_I2C::byte FIRST_COLUMN = 0; - static constexpr LCD_I2C::byte SECOND_COLUMN = 2; - static constexpr LCD_I2C::byte THIRD_COLUMN = 4; - static constexpr LCD_I2C::byte FOURTH_COLUMN = 6; + static constexpr byte FIRST_COLUMN = 0; + static constexpr byte SECOND_COLUMN = 2; + static constexpr byte THIRD_COLUMN = 4; + static constexpr byte FOURTH_COLUMN = 6; #pragma GCC unroll 3 for (size_t row = 0; row < BOARD_SIZE; ++row) @@ -80,9 +80,9 @@ inline void Game::Draw_Game() const noexcept inline void Game::Draw_Board_State() const noexcept { - static constexpr LCD_I2C::byte FIRST_COLUMN = 1; - static constexpr LCD_I2C::byte SECOND_COLUMN = 3; - static constexpr LCD_I2C::byte THIRD_COLUMN = 5; + static constexpr byte FIRST_COLUMN = 1; + static constexpr byte SECOND_COLUMN = 3; + static constexpr byte THIRD_COLUMN = 5; #pragma GCC unroll 3 for (size_t row = 0; row < BOARD_SIZE; ++row) diff --git a/IPlayerStrategy.cpp b/src/IPlayerStrategy.cpp similarity index 100% rename from IPlayerStrategy.cpp rename to src/IPlayerStrategy.cpp diff --git a/Keypad.cpp b/src/Keypad.cpp similarity index 100% rename from Keypad.cpp rename to src/Keypad.cpp diff --git a/LCD_I2C.cpp b/src/LCD_I2C.cpp similarity index 81% rename from LCD_I2C.cpp rename to src/LCD_I2C.cpp index 1f413b3..6f1accc 100644 --- a/LCD_I2C.cpp +++ b/src/LCD_I2C.cpp @@ -1,10 +1,10 @@ /******************************************************************************* - * @file Lcd.cpp + * @file LCD_I2C.cpp * @author Cristian Cristea - * @date July 08, 2021 + * @date September 27, 2021 * @brief Source file for the LCD_I2C class. * - * @copyright Copyright (C) 2021 Cristian Cristea. All rights reserved. + * @copyright Copyright (C) 2021 Cristian Cristea ******************************************************************************/ #include "LCD_I2C.hpp" @@ -49,7 +49,7 @@ inline void LCD_I2C::Send_Nibble(byte val) const noexcept inline void LCD_I2C::Send_Byte(byte val, byte mode) const noexcept { - static constexpr size_t UPPER_NIBBLE = 0B1111'0000; + static constexpr byte UPPER_NIBBLE = 0B1111'0000; static byte high; static byte low; @@ -166,28 +166,6 @@ void LCD_I2C::SetTextRightToLeft() noexcept Send_Command(ENTRY_MODE_SET | display_mode); } -void LCD_I2C::JustifyRight() noexcept -{ - display_mode |= ENTRY_SHIFT_INCREMENT; - Send_Command(ENTRY_MODE_SET | display_mode); -} - -void LCD_I2C::JustifyLeft() noexcept -{ - display_mode &= ~ENTRY_SHIFT_INCREMENT; - Send_Command(ENTRY_MODE_SET | display_mode); -} - -void LCD_I2C::ScrollDisplayLeft() const noexcept -{ - Send_Command(CURSOR_SHIFT | DISPLAY_MOVE | MOVE_LEFT); -} - -void LCD_I2C::ScrollDisplayRight() const noexcept -{ - Send_Command(CURSOR_SHIFT | DISPLAY_MOVE | MOVE_RIGHT); -} - void LCD_I2C::Clear() const noexcept { Send_Command(CLEAR_DISPLAY); @@ -200,10 +178,10 @@ void LCD_I2C::Home() const noexcept void LCD_I2C::SetCursor(byte row, byte column) const noexcept { - static constexpr std::array ROW_OFFSETS = {0x00, 0x40, 0x14, 0x54}; + static const std::array ROW_OFFSETS = {0x00, 0x40, 0x00 + columns, 0x40 + columns}; - row %= rows; - column %= columns; + row = std::min(rows, row); + column = std::min(columns, column); Send_Command(SET_DDRAM_ADDR | (ROW_OFFSETS.at(row) + column)); } @@ -225,11 +203,11 @@ void LCD_I2C::PrintCustomChar(byte location) const noexcept Send_Register_Select(location); } -void LCD_I2C::CreateCustomChar(byte location, std::array char_map) const noexcept +void LCD_I2C::CreateCustomChar(byte location, array const char_map) const noexcept { - static constexpr size_t MAX_CUSTOM_CHARS = 8; + static constexpr byte MAX_CUSTOM_CHARS = 8; - location %= MAX_CUSTOM_CHARS; + location = std::min(MAX_CUSTOM_CHARS, location); Send_Command(SET_CGRAM_ADDR | (location << 3)); for (size_t i = 0; i < CUSTOM_SYMBOL_SIZE; ++i) { diff --git a/Move.cpp b/src/Move.cpp similarity index 100% rename from Move.cpp rename to src/Move.cpp diff --git a/Player.cpp b/src/Player.cpp similarity index 100% rename from Player.cpp rename to src/Player.cpp diff --git a/TM1637.cpp b/src/TM1637.cpp similarity index 100% rename from TM1637.cpp rename to src/TM1637.cpp diff --git a/main.cpp b/src/main.cpp similarity index 100% rename from main.cpp rename to src/main.cpp