diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..945a7ba --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8) +# 3rd party tools +find_package(Qt5 COMPONENTS Widgets Qml Quick REQUIRED) +# Directory with source code +add_subdirectory(src) +add_subdirectory(qt_gui) + diff --git a/README.md b/README.md index 4058de3..9f30673 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # STC DIY Calculator Firmware -This is a replacement firmware for the [diyleyuan calculator](http://www.diyleyuan.com/jc/L8Q.html). The calculator uses an STC IAP15W413AS microcontroller (an 8051 instruction set-compatible microcontorller) with a built in serial bootloder. This project uses SDCC to compile the C code and [stcgal](https://github.com/grigorig/stcgal) to load the new firmware. +This is a replacement firmware for the [diyleyuan calculator](http://www.diyleyuan.com/jc/L8Q.html). The calculator is available for purchase for less than $13 shipped from eBay by searching for "diy calculator kit". The calculator uses an STC IAP15W413AS microcontroller (an 8051 instruction set-compatible microcontorller) with a built in serial bootloder. This project uses SDCC to compile the C code and [stcgal](https://github.com/grigorig/stcgal) to load the new firmware. ![calculator](./calc.jpg) @@ -10,7 +10,8 @@ This is a replacement firmware for the [diyleyuan calculator](http://www.diyleyu - (you must already have SDCC installed and setup so it can be found in your PATH) - this will create a `main.hex` output file - there is also a prebuilt binary checked in at `binaries/main.hex` - +- CMakeLists.txt is for building the Qt desktop application. + - (still a work in progress, currently doesn't do anything) # Installing The STC microcontroller used has a bootloader permanently stored in ROM that allows downloading new firmware over a serial port. You can re-program it using a USB-to-logic-level-serial (5V) dongle, and the stcgal program. WARNING: a lot of USB-to-logic-level-serial dongles are for 3.3V logic levels. The diyleyuan calculator runs at 5V to make it easier to power/drive the LCD display. You have a couple of options: diff --git a/qt_gui/CMakeLists.txt b/qt_gui/CMakeLists.txt new file mode 100644 index 0000000..c3b93ec --- /dev/null +++ b/qt_gui/CMakeLists.txt @@ -0,0 +1,29 @@ +include_directories(${Qt5Widgets_INCLUDE_DIRS} ${QtQml_INCLUDE_DIRS}) +add_definitions(${Qt5Widgets_DEFINITIONS} ${QtQml_DEFINITIONS} ${${Qt5Quick_DEFINITIONS}}) + +qt5_add_resources(QT_RESOURCES main.qrc) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) +set(PROJECT "RPN_Calculator") +project(${PROJECT}) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++11 -fstrict-aliasing -pedantic ") + +if(NOT DEFINED HEADERS) + file(GLOB HEADERS ${CMAKE_CURRENT_SOURCE_DIR}⁄*.h) +endif() +# if(NOT DEFINED SOURCES) +# file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}⁄*.cpp) +# endif() +source_group("Header Files" FILES ${HEADERS}) +# source_group("Source Files" FILES ${SOURCES}) +set(SOURCES main.cpp calculator.cpp) +add_executable(${PROJECT} ${HEADERS} ${SOURCES} ${QT_RESOURCES}) +target_link_libraries(${PROJECT} + Qt5::Widgets + Qt5::Qml + Qt5::Quick + calc +) diff --git a/qt_gui/calculator.cpp b/qt_gui/calculator.cpp new file mode 100644 index 0000000..ab1d873 --- /dev/null +++ b/qt_gui/calculator.cpp @@ -0,0 +1,12 @@ +#include + +#include "calculator.h" + +Calculator::Calculator(QObject *parent) : + QObject(parent) +{ +} + +void Calculator::buttonClicked(const QString& in) { + qDebug() << in; +} diff --git a/qt_gui/calculator.h b/qt_gui/calculator.h new file mode 100644 index 0000000..c0eba2f --- /dev/null +++ b/qt_gui/calculator.h @@ -0,0 +1,20 @@ +#ifndef QTGUI_CALCULATOR_H +#define QTGUI_CALCULATOR_H + +#include + +class Calculator : public QObject +{ + Q_OBJECT + +public: + explicit Calculator(QObject *parent = 0); + +public slots: + void buttonClicked(const QString& in); + +}; + + + +#endif //include guard diff --git a/qt_gui/main.cpp b/qt_gui/main.cpp new file mode 100644 index 0000000..a772de9 --- /dev/null +++ b/qt_gui/main.cpp @@ -0,0 +1,16 @@ +#include +#include +#include +#include "calculator.h" + +int main(int argc, char** argv) +{ + QApplication app(argc, argv); + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + Calculator calculator; + engine.rootContext()->setContextProperty("_calculator", &calculator); + + return app.exec(); +} diff --git a/qt_gui/main.qml b/qt_gui/main.qml new file mode 100644 index 0000000..65e79b3 --- /dev/null +++ b/qt_gui/main.qml @@ -0,0 +1,55 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.0 +import Qt3D.Input 2.0 + +ApplicationWindow +{ + visible: true + width: 1000 + height: 1000 + title: qsTr("RPN Calculator") + + //calculator + Column { + id: base; + spacing: 5; + width: 4 * (100 + 5) + height: 5 * (100 + 5) + 200 + + //LCD + Rectangle { + id: lcd; + color: "gray" + width: 4 * (100 + 5) - 5 + height: 200 + } + + //Keyboard + Repeater { + model: 5; + //row + delegate: Row { + id: key_row; + spacing: 5; + //keys within row + Repeater { + model: 4; + delegate: Rectangle { + id: key_key; + width: 100; + height: 100; + color: "blue" + border { width: 1; color: "black" } + Text { + text: index + anchors.centerIn: parent + } + MouseHandler { + onClicked: _calculator.buttonClicked(index) + } + } + } + } + } + } +} diff --git a/qt_gui/main.qrc b/qt_gui/main.qrc new file mode 100644 index 0000000..18d02bd --- /dev/null +++ b/qt_gui/main.qrc @@ -0,0 +1,5 @@ + + + main.qml + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..cc1a7f3 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,4 @@ + +add_library(decn decn/decn.c) + +add_library(calc main.c calc.c utils.c lcd_emulator.c key.c) diff --git a/src/lcd_emulator.c b/src/lcd_emulator.c new file mode 100644 index 0000000..1e8e641 --- /dev/null +++ b/src/lcd_emulator.c @@ -0,0 +1,93 @@ +/* + * lcd_emulator.c + * + * Created on: Apr 1, 2019 + */ + +#include +#include +#include "lcd.h" + +#define CR 13 // \r +#define TAB 9 // \n + +static int lcd_row, lcd_col; +static char lcd_buf[MAX_ROWS][MAX_CHARS_PER_LINE]; + +void LCD_Open(void){ + +} + +void LCD_Clear(void){ + lcd_row=0; + lcd_col=0; + for (int i = 0; i < MAX_ROWS; i++){ + for (int j = 0; j < MAX_CHARS_PER_LINE; j++){ + lcd_buf[i][j] = 0; + } + } +} + +void LCD_GoTo(unsigned int row, unsigned int col){ + if (row < MAX_ROWS && col < MAX_CHARS_PER_LINE){ + lcd_row = row; + lcd_col = col; + } else { + printf("LCD_GoTo(%u, %u) out of range\n", lcd_row, lcd_col); + } +} + +static void to_row(unsigned char row_to){ + if (row_to == 0){ + lcd_row = 0; + } else { + lcd_row = 1; + } + lcd_col = 0; +} + +void LCD_OutString(const char *string, uint8_t max_chars) { + const char *s; + for (s = string; *s && max_chars > 0; s++, max_chars--) { + TERMIO_PutChar(*s); + } +} + +short TERMIO_PutChar(unsigned char letter) { + if (letter == CR || letter == '\n') { + LCD_Clear(); + } else if (letter == TAB || letter == '\t') { + if (lcd_row == 0) { + to_row(1); + } else { + to_row(0); + } + } else { + lcd_buf[lcd_row][lcd_col] = letter; + lcd_col++; + if (lcd_col > MAX_CHARS_PER_LINE) { + if (lcd_row == 0) { + to_row(1); + } else { + to_row(0); + } + } + } + + return 1; +} + +void LCD_ClearToEnd(uint8_t curr_row){ + while (lcd_col != 0 && lcd_row == curr_row){ + TERMIO_PutChar(' '); + } +} + +void LCD_OutNibble(uint8_t x){ + x &= 0xf; //ensure only bottom nibble + if (x <= 9){ + TERMIO_PutChar(x + '0'); + } else { + TERMIO_PutChar(x - 10 + 'a'); + } +}