From 99c45c5650a1a1c3288e39aea27d510fccee71cb Mon Sep 17 00:00:00 2001 From: zervo Date: Sat, 25 Jan 2025 01:47:24 +0100 Subject: [PATCH] Initial commit --- ArduinoISP/ArduinoISP.ino | 734 +++++++++++++++++++++ README.md | 5 + RKTNTSTRP-FIRMWARE/.gitignore | 5 + RKTNTSTRP-FIRMWARE/.vscode/extensions.json | 10 + RKTNTSTRP-FIRMWARE/include/README | 39 ++ RKTNTSTRP-FIRMWARE/include/display.h | 31 + RKTNTSTRP-FIRMWARE/include/pins.h | 34 + RKTNTSTRP-FIRMWARE/include/systest.h | 11 + RKTNTSTRP-FIRMWARE/lib/README | 46 ++ RKTNTSTRP-FIRMWARE/platformio.ini | 29 + RKTNTSTRP-FIRMWARE/src/display.cpp | 91 +++ RKTNTSTRP-FIRMWARE/src/main.cpp | 42 ++ RKTNTSTRP-FIRMWARE/src/systest.cpp | 54 ++ RKTNTSTRP-FIRMWARE/test/README | 11 + 14 files changed, 1142 insertions(+) create mode 100644 ArduinoISP/ArduinoISP.ino create mode 100644 README.md create mode 100644 RKTNTSTRP-FIRMWARE/.gitignore create mode 100644 RKTNTSTRP-FIRMWARE/.vscode/extensions.json create mode 100644 RKTNTSTRP-FIRMWARE/include/README create mode 100644 RKTNTSTRP-FIRMWARE/include/display.h create mode 100644 RKTNTSTRP-FIRMWARE/include/pins.h create mode 100644 RKTNTSTRP-FIRMWARE/include/systest.h create mode 100644 RKTNTSTRP-FIRMWARE/lib/README create mode 100644 RKTNTSTRP-FIRMWARE/platformio.ini create mode 100644 RKTNTSTRP-FIRMWARE/src/display.cpp create mode 100644 RKTNTSTRP-FIRMWARE/src/main.cpp create mode 100644 RKTNTSTRP-FIRMWARE/src/systest.cpp create mode 100644 RKTNTSTRP-FIRMWARE/test/README diff --git a/ArduinoISP/ArduinoISP.ino b/ArduinoISP/ArduinoISP.ino new file mode 100644 index 0000000..801529e --- /dev/null +++ b/ArduinoISP/ArduinoISP.ino @@ -0,0 +1,734 @@ +// ArduinoISP +// Copyright (c) 2008-2011 Randall Bohn +// If you require a license, see +// https://opensource.org/licenses/bsd-license.php +// +// This sketch turns the Arduino into a AVRISP using the following Arduino pins: +// +// Pin 10 is used to reset the target microcontroller. +// +// By default, the hardware SPI pins MISO, MOSI and SCK are used to communicate +// with the target. On all Arduinos, these pins can be found +// on the ICSP/SPI header: +// +// MISO °. . 5V (!) Avoid this pin on Due, Zero... +// SCK . . MOSI +// . . GND +// +// On some Arduinos (Uno,...), pins MOSI, MISO and SCK are the same pins as +// digital pin 11, 12 and 13, respectively. That is why many tutorials instruct +// you to hook up the target to these pins. If you find this wiring more +// practical, have a define USE_OLD_STYLE_WIRING. This will work even when not +// using an Uno. (On an Uno this is not needed). +// +// Alternatively you can use any other digital pin by configuring +// software ('BitBanged') SPI and having appropriate defines for PIN_MOSI, +// PIN_MISO and PIN_SCK. +// +// IMPORTANT: When using an Arduino that is not 5V tolerant (Due, Zero, ...) as +// the programmer, make sure to not expose any of the programmer's pins to 5V. +// A simple way to accomplish this is to power the complete system (programmer +// and target) at 3V3. +// +// Put an LED (with resistor) on the following pins: +// 9: Heartbeat - shows the programmer is running +// 8: Error - Lights up if something goes wrong (use red if that makes sense) +// 7: Programming - In communication with the slave +// + +#include "Arduino.h" +#undef SERIAL + + +#define PROG_FLICKER true + +// Configure SPI clock (in Hz). +// E.g. for an ATtiny @ 128 kHz: the datasheet states that both the high and low +// SPI clock pulse must be > 2 CPU cycles, so take 3 cycles i.e. divide target +// f_cpu by 6: +// #define SPI_CLOCK (128000/6) +// +// A clock slow enough for an ATtiny85 @ 1 MHz, is a reasonable default: + +#define SPI_CLOCK (1000000/6) + + +// Select hardware or software SPI, depending on SPI clock. +// Currently only for AVR, for other architectures (Due, Zero,...), hardware SPI +// is probably too fast anyway. + +#if defined(ARDUINO_ARCH_AVR) + + #if SPI_CLOCK > (F_CPU / 128) + #define USE_HARDWARE_SPI + #endif + +#endif + +// Configure which pins to use: + +// The standard pin configuration. +#ifndef ARDUINO_HOODLOADER2 + + #define RESET 10 // Use pin 10 to reset the target rather than SS + #define LED_HB 9 + #define LED_ERR 8 + #define LED_PMODE 7 + + // Uncomment following line to use the old Uno style wiring + // (using pin 11, 12 and 13 instead of the SPI header) on Leonardo, Due... + + #define USE_OLD_STYLE_WIRING + + #ifdef USE_OLD_STYLE_WIRING + + #define PIN_MOSI 11 + #define PIN_MISO 12 + #define PIN_SCK 13 + + #endif + + // HOODLOADER2 means running sketches on the ATmega16U2 serial converter chips + // on Uno or Mega boards. We must use pins that are broken out: +#else + + #define RESET 4 + #define LED_HB 7 + #define LED_ERR 6 + #define LED_PMODE 5 + +#endif + +// By default, use hardware SPI pins: +#ifndef PIN_MOSI + #define PIN_MOSI MOSI +#endif + +#ifndef PIN_MISO + #define PIN_MISO MISO +#endif + +#ifndef PIN_SCK + #define PIN_SCK SCK +#endif + +// Force bitbanged SPI if not using the hardware SPI pins: +#if (PIN_MISO != MISO) || (PIN_MOSI != MOSI) || (PIN_SCK != SCK) + #undef USE_HARDWARE_SPI +#endif + + +// Configure the serial port to use. +// +// Prefer the USB virtual serial port (aka. native USB port), if the Arduino has one: +// - it does not autoreset (except for the magic baud rate of 1200). +// - it is more reliable because of USB handshaking. +// +// Leonardo and similar have an USB virtual serial port: 'Serial'. +// Due and Zero have an USB virtual serial port: 'SerialUSB'. +// +// On the Due and Zero, 'Serial' can be used too, provided you disable autoreset. +// To use 'Serial': #define SERIAL Serial + +#ifdef SERIAL_PORT_USBVIRTUAL + #define SERIAL SERIAL_PORT_USBVIRTUAL +#else + #define SERIAL Serial +#endif + + +// Configure the baud rate: + +#define BAUDRATE 19200 +// #define BAUDRATE 115200 +// #define BAUDRATE 1000000 + + +#define HWVER 2 +#define SWMAJ 1 +#define SWMIN 18 + +// STK Definitions +#define STK_OK 0x10 +#define STK_FAILED 0x11 +#define STK_UNKNOWN 0x12 +#define STK_INSYNC 0x14 +#define STK_NOSYNC 0x15 +#define CRC_EOP 0x20 //ok it is a space... + +void pulse(int pin, int times); + +#ifdef USE_HARDWARE_SPI +#include "SPI.h" +#else + +#define SPI_MODE0 0x00 + +#if !defined(ARDUINO_API_VERSION) || ARDUINO_API_VERSION != 10001 // A SPISettings class is declared by ArduinoCore-API 1.0.1 +class SPISettings { + public: + // clock is in Hz + SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) : clockFreq(clock) { + (void) bitOrder; + (void) dataMode; + }; + + uint32_t getClockFreq() const { + return clockFreq; + } + + private: + uint32_t clockFreq; +}; +#endif // !defined(ARDUINO_API_VERSION) + +class BitBangedSPI { + public: + void begin() { + digitalWrite(PIN_SCK, LOW); + digitalWrite(PIN_MOSI, LOW); + pinMode(PIN_SCK, OUTPUT); + pinMode(PIN_MOSI, OUTPUT); + pinMode(PIN_MISO, INPUT); + } + + void beginTransaction(SPISettings settings) { + pulseWidth = (500000 + settings.getClockFreq() - 1) / settings.getClockFreq(); + if (pulseWidth == 0) { + pulseWidth = 1; + } + } + + void end() {} + + uint8_t transfer(uint8_t b) { + for (unsigned int i = 0; i < 8; ++i) { + digitalWrite(PIN_MOSI, (b & 0x80) ? HIGH : LOW); + digitalWrite(PIN_SCK, HIGH); + delayMicroseconds(pulseWidth); + b = (b << 1) | digitalRead(PIN_MISO); + digitalWrite(PIN_SCK, LOW); // slow pulse + delayMicroseconds(pulseWidth); + } + return b; + } + + private: + unsigned long pulseWidth; // in microseconds +}; + +static BitBangedSPI SPI; + +#endif + +void setup() { + SERIAL.begin(BAUDRATE); + + pinMode(LED_PMODE, OUTPUT); + pulse(LED_PMODE, 2); + pinMode(LED_ERR, OUTPUT); + pulse(LED_ERR, 2); + pinMode(LED_HB, OUTPUT); + pulse(LED_HB, 2); + +} + +int ISPError = 0; +int pmode = 0; +// address for reading and writing, set by 'U' command +unsigned int here; +uint8_t buff[256]; // global block storage + +#define beget16(addr) (*addr * 256 + *(addr+1) ) +typedef struct param { + uint8_t devicecode; + uint8_t revision; + uint8_t progtype; + uint8_t parmode; + uint8_t polling; + uint8_t selftimed; + uint8_t lockbytes; + uint8_t fusebytes; + uint8_t flashpoll; + uint16_t eeprompoll; + uint16_t pagesize; + uint16_t eepromsize; + uint32_t flashsize; +} +parameter; + +parameter param; + +// this provides a heartbeat on pin 9, so you can tell the software is running. +uint8_t hbval = 128; +int8_t hbdelta = 8; +void heartbeat() { + static unsigned long last_time = 0; + unsigned long now = millis(); + if ((now - last_time) < 40) { + return; + } + last_time = now; + if (hbval > 192) { + hbdelta = -hbdelta; + } + if (hbval < 32) { + hbdelta = -hbdelta; + } + hbval += hbdelta; + analogWrite(LED_HB, hbval); +} + +static bool rst_active_high; + +void reset_target(bool reset) { + digitalWrite(RESET, ((reset && rst_active_high) || (!reset && !rst_active_high)) ? HIGH : LOW); +} + +void loop(void) { + // is pmode active? + if (pmode) { + digitalWrite(LED_PMODE, HIGH); + } else { + digitalWrite(LED_PMODE, LOW); + } + // is there an error? + if (ISPError) { + digitalWrite(LED_ERR, HIGH); + } else { + digitalWrite(LED_ERR, LOW); + } + + // light the heartbeat LED + heartbeat(); + if (SERIAL.available()) { + avrisp(); + } +} + +uint8_t getch() { + while (!SERIAL.available()); + return SERIAL.read(); +} +void fill(int n) { + for (int x = 0; x < n; x++) { + buff[x] = getch(); + } +} + +#define PTIME 30 +void pulse(int pin, int times) { + do { + digitalWrite(pin, HIGH); + delay(PTIME); + digitalWrite(pin, LOW); + delay(PTIME); + } while (times--); +} + +void prog_lamp(int state) { + if (PROG_FLICKER) { + digitalWrite(LED_PMODE, state); + } +} + +uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { + SPI.transfer(a); + SPI.transfer(b); + SPI.transfer(c); + return SPI.transfer(d); +} + +void empty_reply() { + if (CRC_EOP == getch()) { + SERIAL.print((char)STK_INSYNC); + SERIAL.print((char)STK_OK); + } else { + ISPError++; + SERIAL.print((char)STK_NOSYNC); + } +} + +void breply(uint8_t b) { + if (CRC_EOP == getch()) { + SERIAL.print((char)STK_INSYNC); + SERIAL.print((char)b); + SERIAL.print((char)STK_OK); + } else { + ISPError++; + SERIAL.print((char)STK_NOSYNC); + } +} + +void get_version(uint8_t c) { + switch (c) { + case 0x80: + breply(HWVER); + break; + case 0x81: + breply(SWMAJ); + break; + case 0x82: + breply(SWMIN); + break; + case 0x93: + breply('S'); // serial programmer + break; + default: + breply(0); + } +} + +void set_parameters() { + // call this after reading parameter packet into buff[] + param.devicecode = buff[0]; + param.revision = buff[1]; + param.progtype = buff[2]; + param.parmode = buff[3]; + param.polling = buff[4]; + param.selftimed = buff[5]; + param.lockbytes = buff[6]; + param.fusebytes = buff[7]; + param.flashpoll = buff[8]; + // ignore buff[9] (= buff[8]) + // following are 16 bits (big endian) + param.eeprompoll = beget16(&buff[10]); + param.pagesize = beget16(&buff[12]); + param.eepromsize = beget16(&buff[14]); + + // 32 bits flashsize (big endian) + param.flashsize = buff[16] * 0x01000000 + + buff[17] * 0x00010000 + + buff[18] * 0x00000100 + + buff[19]; + + // AVR devices have active low reset, AT89Sx are active high + rst_active_high = (param.devicecode >= 0xe0); +} + +void start_pmode() { + + // Reset target before driving PIN_SCK or PIN_MOSI + + // SPI.begin() will configure SS as output, so SPI master mode is selected. + // We have defined RESET as pin 10, which for many Arduinos is not the SS pin. + // So we have to configure RESET as output here, + // (reset_target() first sets the correct level) + reset_target(true); + pinMode(RESET, OUTPUT); + SPI.begin(); + SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0)); + + // See AVR datasheets, chapter "SERIAL_PRG Programming Algorithm": + + // Pulse RESET after PIN_SCK is low: + digitalWrite(PIN_SCK, LOW); + delay(20); // discharge PIN_SCK, value arbitrarily chosen + reset_target(false); + // Pulse must be minimum 2 target CPU clock cycles so 100 usec is ok for CPU + // speeds above 20 KHz + delayMicroseconds(100); + reset_target(true); + + // Send the enable programming command: + delay(50); // datasheet: must be > 20 msec + spi_transaction(0xAC, 0x53, 0x00, 0x00); + pmode = 1; +} + +void end_pmode() { + SPI.end(); + // We're about to take the target out of reset so configure SPI pins as input + pinMode(PIN_MOSI, INPUT); + pinMode(PIN_SCK, INPUT); + reset_target(false); + pinMode(RESET, INPUT); + pmode = 0; +} + +void universal() { + uint8_t ch; + + fill(4); + ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]); + breply(ch); +} + +void flash(uint8_t hilo, unsigned int addr, uint8_t data) { + spi_transaction(0x40 + 8 * hilo, + addr >> 8 & 0xFF, + addr & 0xFF, + data); +} +void commit(unsigned int addr) { + if (PROG_FLICKER) { + prog_lamp(LOW); + } + spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0); + if (PROG_FLICKER) { + delay(PTIME); + prog_lamp(HIGH); + } +} + +unsigned int current_page() { + if (param.pagesize == 32) { + return here & 0xFFFFFFF0; + } + if (param.pagesize == 64) { + return here & 0xFFFFFFE0; + } + if (param.pagesize == 128) { + return here & 0xFFFFFFC0; + } + if (param.pagesize == 256) { + return here & 0xFFFFFF80; + } + return here; +} + + +void write_flash(int length) { + fill(length); + if (CRC_EOP == getch()) { + SERIAL.print((char) STK_INSYNC); + SERIAL.print((char) write_flash_pages(length)); + } else { + ISPError++; + SERIAL.print((char) STK_NOSYNC); + } +} + +uint8_t write_flash_pages(int length) { + int x = 0; + unsigned int page = current_page(); + while (x < length) { + if (page != current_page()) { + commit(page); + page = current_page(); + } + flash(LOW, here, buff[x++]); + flash(HIGH, here, buff[x++]); + here++; + } + + commit(page); + + return STK_OK; +} + +#define EECHUNK (32) +uint8_t write_eeprom(unsigned int length) { + // here is a word address, get the byte address + unsigned int start = here * 2; + unsigned int remaining = length; + if (length > param.eepromsize) { + ISPError++; + return STK_FAILED; + } + while (remaining > EECHUNK) { + write_eeprom_chunk(start, EECHUNK); + start += EECHUNK; + remaining -= EECHUNK; + } + write_eeprom_chunk(start, remaining); + return STK_OK; +} +// write (length) bytes, (start) is a byte address +uint8_t write_eeprom_chunk(unsigned int start, unsigned int length) { + // this writes byte-by-byte, page writing may be faster (4 bytes at a time) + fill(length); + prog_lamp(LOW); + for (unsigned int x = 0; x < length; x++) { + unsigned int addr = start + x; + spi_transaction(0xC0, (addr >> 8) & 0xFF, addr & 0xFF, buff[x]); + delay(45); + } + prog_lamp(HIGH); + return STK_OK; +} + +void program_page() { + char result = (char) STK_FAILED; + unsigned int length = 256 * getch(); + length += getch(); + char memtype = getch(); + // flash memory @here, (length) bytes + if (memtype == 'F') { + write_flash(length); + return; + } + if (memtype == 'E') { + result = (char)write_eeprom(length); + if (CRC_EOP == getch()) { + SERIAL.print((char) STK_INSYNC); + SERIAL.print(result); + } else { + ISPError++; + SERIAL.print((char) STK_NOSYNC); + } + return; + } + SERIAL.print((char)STK_FAILED); + return; +} + +uint8_t flash_read(uint8_t hilo, unsigned int addr) { + return spi_transaction(0x20 + hilo * 8, + (addr >> 8) & 0xFF, + addr & 0xFF, + 0); +} + +char flash_read_page(int length) { + for (int x = 0; x < length; x += 2) { + uint8_t low = flash_read(LOW, here); + SERIAL.print((char) low); + uint8_t high = flash_read(HIGH, here); + SERIAL.print((char) high); + here++; + } + return STK_OK; +} + +char eeprom_read_page(int length) { + // here again we have a word address + int start = here * 2; + for (int x = 0; x < length; x++) { + int addr = start + x; + uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF); + SERIAL.print((char) ee); + } + return STK_OK; +} + +void read_page() { + char result = (char)STK_FAILED; + int length = 256 * getch(); + length += getch(); + char memtype = getch(); + if (CRC_EOP != getch()) { + ISPError++; + SERIAL.print((char) STK_NOSYNC); + return; + } + SERIAL.print((char) STK_INSYNC); + if (memtype == 'F') { + result = flash_read_page(length); + } + if (memtype == 'E') { + result = eeprom_read_page(length); + } + SERIAL.print(result); +} + +void read_signature() { + if (CRC_EOP != getch()) { + ISPError++; + SERIAL.print((char) STK_NOSYNC); + return; + } + SERIAL.print((char) STK_INSYNC); + uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00); + SERIAL.print((char) high); + uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00); + SERIAL.print((char) middle); + uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00); + SERIAL.print((char) low); + SERIAL.print((char) STK_OK); +} +////////////////////////////////////////// +////////////////////////////////////////// + + +//////////////////////////////////// +//////////////////////////////////// +void avrisp() { + uint8_t ch = getch(); + switch (ch) { + case '0': // signon + ISPError = 0; + empty_reply(); + break; + case '1': + if (getch() == CRC_EOP) { + SERIAL.print((char) STK_INSYNC); + SERIAL.print("AVR ISP"); + SERIAL.print((char) STK_OK); + } else { + ISPError++; + SERIAL.print((char) STK_NOSYNC); + } + break; + case 'A': + get_version(getch()); + break; + case 'B': + fill(20); + set_parameters(); + empty_reply(); + break; + case 'E': // extended parameters - ignore for now + fill(5); + empty_reply(); + break; + case 'P': + if (!pmode) { + start_pmode(); + } + empty_reply(); + break; + case 'U': // set address (word) + here = getch(); + here += 256 * getch(); + empty_reply(); + break; + + case 0x60: //STK_PROG_FLASH + getch(); // low addr + getch(); // high addr + empty_reply(); + break; + case 0x61: //STK_PROG_DATA + getch(); // data + empty_reply(); + break; + + case 0x64: //STK_PROG_PAGE + program_page(); + break; + + case 0x74: //STK_READ_PAGE 't' + read_page(); + break; + + case 'V': //0x56 + universal(); + break; + case 'Q': //0x51 + ISPError = 0; + end_pmode(); + empty_reply(); + break; + + case 0x75: //STK_READ_SIGN 'u' + read_signature(); + break; + + // expecting a command, not CRC_EOP + // this is how we can get back in sync + case CRC_EOP: + ISPError++; + SERIAL.print((char) STK_NOSYNC); + break; + + // anything else we will return STK_UNKNOWN + default: + ISPError++; + if (CRC_EOP == getch()) { + SERIAL.print((char)STK_UNKNOWN); + } else { + SERIAL.print((char)STK_NOSYNC); + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..cbfca7a --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# RKTNTSTRP Firmware + +Source code repository for the RKTNTSTRP firmware. + +Very odd name, I know. diff --git a/RKTNTSTRP-FIRMWARE/.gitignore b/RKTNTSTRP-FIRMWARE/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/RKTNTSTRP-FIRMWARE/.vscode/extensions.json b/RKTNTSTRP-FIRMWARE/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/RKTNTSTRP-FIRMWARE/include/README b/RKTNTSTRP-FIRMWARE/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/RKTNTSTRP-FIRMWARE/include/display.h b/RKTNTSTRP-FIRMWARE/include/display.h new file mode 100644 index 0000000..fe12605 --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/include/display.h @@ -0,0 +1,31 @@ +/*************************************************** + 7-segment display driver. +***************************************************/ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include // For uint8_t + +class Display { +public: + // Constructor with the inverse flag (default false) + Display(bool inverse = false); + + // Write a digit (0-9) to the display + void write(uint8_t digit); + + // Multiplexed display update + void update(uint8_t displayNum, uint8_t digit); + +private: + bool _inverse; + + // Helper method to map digit to segment pattern + uint8_t _digitToPattern(uint8_t digit); + + // Latch the display (make only selected display active) + void _latch(uint8_t displayNum); +}; + +#endif // DISPLAY_H diff --git a/RKTNTSTRP-FIRMWARE/include/pins.h b/RKTNTSTRP-FIRMWARE/include/pins.h new file mode 100644 index 0000000..83ad04a --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/include/pins.h @@ -0,0 +1,34 @@ +/*************************************************** + Pin definitions. +***************************************************/ + +#ifndef PINS_H +#define PINS_H + +// "Attempt" LEDs, the 5 on the front. + +#define LED_T1 PA7 // Attempt LED 1 +#define LED_T2 PA6 // Attempt LED 2 +#define LED_T3 PA5 // Attempt LED 3 +#define LED_T4 PA4 // Attempt LED 4 +#define LED_T5 PA3 // Attempt LED 5 + +// "Status" LEDs, aka Insert Coin, Too Late and Too Early. + +#define LED_LATE PC2 // Too Late LED +#define LED_EARLY PC1 // Too Early LED +#define LED_COIN PC0 // Insert Coin LED + +// Display data pins +#define DP_DATA_A PD3 // Display databit A +#define DP_DATA_B PD4 // Display databit B +#define DP_DATA_C PD5 // Display databit C +#define DP_DATA_D PD6 // Display databit D + +// Display latch pins +#define DP_LATCH_1 PC6 // Display 1 latch +#define DP_LATCH_2 PC7 // Display 2 latch +#define DP_LATCH_3 PB0 // Display 3 latch +#define DP_LATCH_4 PB1 // Display 4 latch + +#endif // PINS_H \ No newline at end of file diff --git a/RKTNTSTRP-FIRMWARE/include/systest.h b/RKTNTSTRP-FIRMWARE/include/systest.h new file mode 100644 index 0000000..401305e --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/include/systest.h @@ -0,0 +1,11 @@ +/*************************************************** + System test utility. +***************************************************/ + +#ifndef SYSTEST_H +#define SYSTEST_H + +// System test function +void systest(void); + +#endif // SYSTEST_H diff --git a/RKTNTSTRP-FIRMWARE/lib/README b/RKTNTSTRP-FIRMWARE/lib/README new file mode 100644 index 0000000..2593a33 --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/RKTNTSTRP-FIRMWARE/platformio.ini b/RKTNTSTRP-FIRMWARE/platformio.ini new file mode 100644 index 0000000..00cf6a1 --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/platformio.ini @@ -0,0 +1,29 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:ATmega1284P] +platform = atmelavr +board = ATmega1284P +framework = arduino +upload_protocol = custom +upload_port = /dev/ttyACM0 +upload_speed = 19200 +upload_flags = + -C$PROJECT_PACKAGES_DIR/tool-avrdude/avrdude.conf + -v + -p + atmega1284p + -c + stk500v1 + -P + $UPLOAD_PORT + -b + $UPLOAD_SPEED +upload_command = avrdude $UPLOAD_FLAGS -U flash:w:$SOURCE:i \ No newline at end of file diff --git a/RKTNTSTRP-FIRMWARE/src/display.cpp b/RKTNTSTRP-FIRMWARE/src/display.cpp new file mode 100644 index 0000000..5d28159 --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/src/display.cpp @@ -0,0 +1,91 @@ +#include "display.h" +#include "pins.h" +#include // For pin manipulation +#include // For delay functions + +// Constructor to initialize the CD4511 +Display::Display(bool inverse) +{ + // Set data pins as output + DDRD |= (1 << DP_DATA_A) | (1 << DP_DATA_B) | (1 << DP_DATA_C) | (1 << DP_DATA_D); + + // Set latch pins as output + DDRC |= (1 << DP_LATCH_1) | (1 << DP_LATCH_2); + DDRB |= (1 << DP_LATCH_3) | (1 << DP_LATCH_4); + + // Set the inverse flag + _inverse = inverse; +} + +// Write a digit (0-9) to the CD4511 +void Display::write(uint8_t digit) +{ + uint8_t pattern = _digitToPattern(digit); + + // Write the pattern to PORTD + PORTD = (PORTD & ~0x7F) | (pattern & 0x7F); // Mask out the lower 7 bits and set the correct pattern +} + +// Convert the digit (0-9) to the 4-bit pattern for the CD4511 +uint8_t Display::_digitToPattern(uint8_t digit) +{ + uint8_t patterns[10] = { + 0b0000, // 0 + 0b0001, // 1 + 0b0010, // 2 + 0b0011, // 3 + 0b0100, // 4 + 0b0101, // 5 + 0b0110, // 6 + 0b0111, // 7 + 0b1000, // 8 + 0b1001 // 9 + }; + + // If inverse logic is set, flip the bits + if (_inverse) { + return ~patterns[digit] & 0x0F; // Mask to keep only the lower 4 bits + } else { + return patterns[digit]; + } +} + +// Latch the display (make only selected display active) +void Display::_latch(uint8_t displayNum) +{ + // Turn off all latches first (to ensure only one display is active at a time) + PORTC &= ~((1 << DP_LATCH_1) | (1 << DP_LATCH_2)); + PORTB &= ~((1 << DP_LATCH_3) | (1 << DP_LATCH_4)); + + // Activate the appropriate display latch + switch (displayNum) { + case 1: + PORTC |= (1 << DP_LATCH_1); + break; + case 2: + PORTC |= (1 << DP_LATCH_2); + break; + case 3: + PORTB |= (1 << DP_LATCH_3); + break; + case 4: + PORTB |= (1 << DP_LATCH_4); + break; + default: + // Invalid display number, do nothing + return; + } +} + +// Activate display at selected position and display given digit +void Display::update(uint8_t displayNum, uint8_t digit) +{ + // Latch selected display + _latch(displayNum); + + // Write digit to the display + write(digit); + + // Allow some time for the displays to update before continuing + _delay_ms(1); +} \ No newline at end of file diff --git a/RKTNTSTRP-FIRMWARE/src/main.cpp b/RKTNTSTRP-FIRMWARE/src/main.cpp new file mode 100644 index 0000000..e2b9bd1 --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/src/main.cpp @@ -0,0 +1,42 @@ +/*************************************************** + RKTN_TST_RP FIRMWARE R1 V0.1 + + Developed for the board of the same name. + Based on the ATMega1284P/644P. +***************************************************/ + +#include "pins.h" +#include +#include +#include "systest.h" + +int main(void) +{ + + /************************************************ + Configure the IO of the system. + + DDRB &= ~(1 << PB0) <-- as input + DDRB |= (1 << PB0) <-- as output + *************************************************/ + + DDRA |= (1 << LED_T1); + DDRA |= (1 << LED_T2); + DDRA |= (1 << LED_T3); + DDRA |= (1 << LED_T4); + DDRA |= (1 << LED_T5); + + DDRC |= (1 << LED_LATE); + DDRC |= (1 << LED_EARLY); + DDRC |= (1 << LED_COIN); + + // Infinite loop + while(1) + { + systest(); + + _delay_ms(1000); + } + + return 0; +} \ No newline at end of file diff --git a/RKTNTSTRP-FIRMWARE/src/systest.cpp b/RKTNTSTRP-FIRMWARE/src/systest.cpp new file mode 100644 index 0000000..8beac7a --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/src/systest.cpp @@ -0,0 +1,54 @@ +/*************************************************** + System test function. + Tests the various mechanics and feedbacks + of the system to aid in troubleshooting. +***************************************************/ + +#include +#include +#include "pins.h" +#include "display.h" +#include "systest.h" + +// Perform a system test +void systest(void) +{ + uint8_t i; + + // Blink front LEDs 5 times + for (i = 0; i < 5; i++) { + // Turn on all LEDs + PORTA |= (1 << LED_T1); + PORTA |= (1 << LED_T2); + PORTA |= (1 << LED_T3); + PORTA |= (1 << LED_T4); + PORTA |= (1 << LED_T5); + PORTC |= (1 << LED_LATE); + PORTC |= (1 << LED_EARLY); + PORTC |= (1 << LED_COIN); + _delay_ms(500); + + // Turn off all LEDs + PORTA &= ~(1 << LED_T1); + PORTA &= ~(1 << LED_T2); + PORTA &= ~(1 << LED_T3); + PORTA &= ~(1 << LED_T4); + PORTA &= ~(1 << LED_T5); + PORTC &= ~(1 << LED_LATE); + PORTC &= ~(1 << LED_EARLY); + PORTC &= ~(1 << LED_COIN); + _delay_ms(500); + } + + _delay_ms(1000); + // Create an instance of the CD4511 class + Display display(true); // Set inverse to false (or true if you need inverted logic) + display.update(1, 0); + + // Display digits 0 to 9 in sequence + //for (uint8_t i = 0; i < 10; ++i) { + // display.update(1, i); // Write the current digit to the CD4511 + // _delay_ms(500); // Wait for 1 second before changing the digit + //} + +} \ No newline at end of file diff --git a/RKTNTSTRP-FIRMWARE/test/README b/RKTNTSTRP-FIRMWARE/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/RKTNTSTRP-FIRMWARE/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html