Initial commit

This commit is contained in:
zervo 2025-01-25 01:47:24 +01:00
commit 99c45c5650
14 changed files with 1142 additions and 0 deletions

734
ArduinoISP/ArduinoISP.ino Normal file
View file

@ -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);
}
}
}

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# RKTNTSTRP Firmware
Source code repository for the RKTNTSTRP firmware.
Very odd name, I know.

5
RKTNTSTRP-FIRMWARE/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View file

@ -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"
]
}

View file

@ -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

View file

@ -0,0 +1,31 @@
/***************************************************
7-segment display driver.
***************************************************/
#ifndef DISPLAY_H
#define DISPLAY_H
#include <stdint.h> // 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

View file

@ -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

View file

@ -0,0 +1,11 @@
/***************************************************
System test utility.
***************************************************/
#ifndef SYSTEST_H
#define SYSTEST_H
// System test function
void systest(void);
#endif // SYSTEST_H

View file

@ -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 <Foo.h>
#include <Bar.h>
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

View file

@ -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

View file

@ -0,0 +1,91 @@
#include "display.h"
#include "pins.h"
#include <avr/io.h> // For pin manipulation
#include <util/delay.h> // 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);
}

View file

@ -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 <avr/io.h>
#include <util/delay.h>
#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;
}

View file

@ -0,0 +1,54 @@
/***************************************************
System test function.
Tests the various mechanics and feedbacks
of the system to aid in troubleshooting.
***************************************************/
#include <avr/io.h>
#include <util/delay.h>
#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
//}
}

View file

@ -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