~ruther/avr-guess-the-number

0f40a4c3191c5f420d7dd3ca4203f63b6f1dd813 — František Boháček 2 years ago
Initial commit
A  => .cargo/config.toml +5 -0
@@ 1,5 @@
[build]
target = "avr-specs/avr-atmega8.json"

[unstable]
build-std = ["core"]

A  => .github/Dockerfile.ci +22 -0
@@ 1,22 @@
FROM ubuntu:18.04

RUN useradd -m avr-rust

# Install dependencies
RUN apt-get update -y && apt-get install -y wget gcc binutils gcc-avr avr-libc

RUN mkdir -p /code && chown avr-rust:avr-rust /code

USER avr-rust

# Install Rustup along with nightly
RUN wget -q https://sh.rustup.rs -O /tmp/rustup.sh && sh /tmp/rustup.sh -y --profile minimal --default-toolchain nightly -c rust-src --quiet
ENV PATH=/home/avr-rust/.cargo/bin:$PATH

COPY --chown=avr-rust:avr-rust . /code

WORKDIR /code

ENV AVR_CPU_FREQUENCY_HZ=16000000

ENTRYPOINT ["cargo"]

A  => .github/workflows/docker-image.yml +23 -0
@@ 1,23 @@
name: Test suite

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
  schedule:
  - cron: "0 2 * * 1-5"

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Prepare the Rust build environment
      run:
        docker build . --file .github/Dockerfile.ci --tag rust-avr-ci:$GITHUB_RUN_NUMBER

    - name: Compile the crate
      run:
        docker run rust-avr-ci:$GITHUB_RUN_NUMBER build --release

A  => .gitignore +1 -0
@@ 1,1 @@
/target

A  => Cargo.lock +199 -0
@@ 1,199 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "atmega-hal"
version = "0.1.0"
dependencies = [
 "avr-device",
 "avr-hal-generic",
]

[[package]]
name = "avr-device"
version = "0.4.0"
dependencies = [
 "avr-device-macros",
 "bare-metal",
 "cfg-if",
 "vcell",
]

[[package]]
name = "avr-device-macros"
version = "0.4.0"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "avr-hal-generic"
version = "0.1.0"
dependencies = [
 "avr-device",
 "cfg-if",
 "embedded-hal",
 "embedded-storage",
 "nb 0.1.3",
 "paste",
 "rustversion",
 "ufmt",
 "void",
]

[[package]]
name = "bare-metal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"

[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"

[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
 "nb 0.1.3",
 "void",
]

[[package]]
name = "embedded-storage"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "723dce4e9f25b6e6c5f35628e144794e5b459216ed7da97b7c4b66cdb3fa82ca"

[[package]]
name = "guess-the-number"
version = "0.1.0"
dependencies = [
 "atmega-hal",
 "avr-hal-generic",
 "nb 0.1.3",
 "panic-halt",
 "ufmt",
]

[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
 "nb 1.0.0",
]

[[package]]
name = "nb"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"

[[package]]
name = "panic-halt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"

[[package]]
name = "paste"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"

[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"

[[package]]
name = "proc-macro2"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9d89e5dba24725ae5678020bf8f1357a9aa7ff10736b551adbcd3f8d17d766f"
dependencies = [
 "unicode-ident",
]

[[package]]
name = "quote"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556d0f47a940e895261e77dc200d5eadfc6ef644c179c6f5edfc105e3a2292c8"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "rustversion"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"

[[package]]
name = "syn"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ee3a69cd2c7e06684677e5629b3878b253af05e4714964204279c6bc02cf0b"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-ident",
]

[[package]]
name = "ufmt"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31d3c0c63312dfc9d8e5c71114d617018a19f6058674003c0da29ee8d8036cdd"
dependencies = [
 "proc-macro-hack",
 "ufmt-macros",
 "ufmt-write",
]

[[package]]
name = "ufmt-macros"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ab6c92f30c996394a8bd525aef9f03ce01d0d7ac82d81902968057e37dd7d9"
dependencies = [
 "proc-macro-hack",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "ufmt-write"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69"

[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"

[[package]]
name = "vcell"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"

[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

A  => Cargo.toml +36 -0
@@ 1,36 @@
[package]
name = "guess-the-number"
version = "0.1.0"
authors = ["František Boháček <fandabohacek@gmail.com>"]
edition = "2021"

[[bin]]
path = "src/entrypoint.rs"
name = "guess-the-number"
test = false
bench = false

[dependencies]
panic-halt = "0.2.0"
ufmt = "0.1.0"
nb = "0.1.2"

[dependencies.atmega-hal]
path = "../avr-hal/mcu/atmega-hal"
features = ["rt","atmega8"]

[dependencies.avr-hal-generic]
path = "../avr-hal/avr-hal-generic"

[profile.dev]
panic = "abort"
lto = true
opt-level = "s"

[profile.release]
panic = "abort"
strip = true
codegen-units = 1
debug = false
lto = true
opt-level = "s"

A  => README.md +28 -0
@@ 1,28 @@
# Rust AVR executable template

A template for Rust based AVR executables. Use with cargo-generate:

```
  cargo generate --git https://github.com/astepkowski/rust-avr-template.git
```
then select AVR family (ATmega, ATtiny) and chip.

**NOTE**: This software template repository is offered in the public domain. It is free to use, adapt, modify, distribute, with no restrictions and no crediting required.

## Build instructions

Install Rust nightly.

```
rustup toolchain install nightly-2022-06-13
rustup component add rust-src --toolchain nightly-2022-06-13
```

Then run:

```
cargo build --release
```

The final ELF executable file will then be available at `target/avr-<mcu_name>/release/template-bin.elf`.


A  => avr-specs/avr-atmega1280.json +27 -0
@@ 1,27 @@
{
  "arch": "avr",
  "atomic-cas": false,
  "cpu": "atmega1280",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "eh-frame-header": false,
  "exe-suffix": ".elf",
  "executables": true,
  "late-link-args": {
    "gcc": [
      "-lgcc"
    ]
  },
  "linker": "avr-gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "max-atomic-width": 8,
  "no-default-libraries": false,
  "pre-link-args": {
    "gcc": [
      "-mmcu=atmega1280",
      "-Wl,--as-needed"
    ]
  },
  "target-c-int-width": "16",
  "target-pointer-width": "16"
}

A  => avr-specs/avr-atmega168.json +27 -0
@@ 1,27 @@
{
  "arch": "avr",
  "atomic-cas": false,
  "cpu": "atmega168",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "eh-frame-header": false,
  "exe-suffix": ".elf",
  "executables": true,
  "late-link-args": {
    "gcc": [
      "-lgcc"
    ]
  },
  "linker": "avr-gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "max-atomic-width": 8,
  "no-default-libraries": false,
  "pre-link-args": {
    "gcc": [
      "-mmcu=atmega168",
      "-Wl,--as-needed"
    ]
  },
  "target-c-int-width": "16",
  "target-pointer-width": "16"
}

A  => avr-specs/avr-atmega2560.json +27 -0
@@ 1,27 @@
{
  "arch": "avr",
  "atomic-cas": false,
  "cpu": "atmega2560",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "eh-frame-header": false,
  "exe-suffix": ".elf",
  "executables": true,
  "late-link-args": {
    "gcc": [
      "-lgcc"
    ]
  },
  "linker": "avr-gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "max-atomic-width": 8,
  "no-default-libraries": false,
  "pre-link-args": {
    "gcc": [
      "-mmcu=atmega2560",
      "-Wl,--as-needed"
    ]
  },
  "target-c-int-width": "16",
  "target-pointer-width": "16"
}

A  => avr-specs/avr-atmega328p.json +27 -0
@@ 1,27 @@
{
  "arch": "avr",
  "atomic-cas": false,
  "cpu": "atmega328p",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "eh-frame-header": false,
  "exe-suffix": ".elf",
  "executables": true,
  "late-link-args": {
    "gcc": [
      "-lgcc"
    ]
  },
  "linker": "avr-gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "max-atomic-width": 8,
  "no-default-libraries": false,
  "pre-link-args": {
    "gcc": [
      "-mmcu=atmega328p",
      "-Wl,--as-needed"
    ]
  },
  "target-c-int-width": "16",
  "target-pointer-width": "16"
}

A  => avr-specs/avr-atmega32u4.json +27 -0
@@ 1,27 @@
{
  "arch": "avr",
  "atomic-cas": false,
  "cpu": "atmega32u4",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "eh-frame-header": false,
  "exe-suffix": ".elf",
  "executables": true,
  "late-link-args": {
    "gcc": [
      "-lgcc"
    ]
  },
  "linker": "avr-gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "max-atomic-width": 8,
  "no-default-libraries": false,
  "pre-link-args": {
    "gcc": [
      "-mmcu=atmega32u4",
      "-Wl,--as-needed"
    ]
  },
  "target-c-int-width": "16",
  "target-pointer-width": "16"
}

A  => avr-specs/avr-atmega48p.json +27 -0
@@ 1,27 @@
{
  "arch": "avr",
  "atomic-cas": false,
  "cpu": "atmega48p",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "eh-frame-header": false,
  "exe-suffix": ".elf",
  "executables": true,
  "late-link-args": {
    "gcc": [
      "-lgcc"
    ]
  },
  "linker": "avr-gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "max-atomic-width": 8,
  "no-default-libraries": false,
  "pre-link-args": {
    "gcc": [
      "-mmcu=atmega48p",
      "-Wl,--as-needed"
    ]
  },
  "target-c-int-width": "16",
  "target-pointer-width": "16"
}

A  => avr-specs/avr-atmega8.json +31 -0
@@ 1,31 @@
{
  "arch": "avr",
  "cpu": "atmega8",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "env": "",
  "executables": true,
  "linker": "avr-gcc",
  "linker-flavor": "gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "os": "unknown",
  "position-independent-executables": false,
  "exe-suffix": ".elf",
  "eh-frame-header": false,
  "pre-link-args": {
    "gcc": [
      "-Os",
      "-mmcu=atmega8"
    ]
  },
  "late-link-args": {
    "gcc": [
      "-lc",
      "-lgcc"
    ]
  },
  "target-c-int-width": "16",
  "target-endian": "little",
  "target-pointer-width": "16",
  "vendor": "unknown"
}

A  => avr-specs/avr-attiny85.json +27 -0
@@ 1,27 @@
{
  "arch": "avr",
  "atomic-cas": false,
  "cpu": "attiny85",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "eh-frame-header": false,
  "exe-suffix": ".elf",
  "executables": true,
  "late-link-args": {
    "gcc": [
      "-lgcc"
    ]
  },
  "linker": "avr-gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "max-atomic-width": 8,
  "no-default-libraries": false,
  "pre-link-args": {
    "gcc": [
      "-mmcu=attiny85",
      "-Wl,--as-needed"
    ]
  },
  "target-c-int-width": "16",
  "target-pointer-width": "16"
}

A  => avr-specs/avr-attiny88.json +27 -0
@@ 1,27 @@
{
  "arch": "avr",
  "atomic-cas": false,
  "cpu": "attiny88",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "eh-frame-header": false,
  "exe-suffix": ".elf",
  "executables": true,
  "late-link-args": {
    "gcc": [
      "-lgcc"
    ]
  },
  "linker": "avr-gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "max-atomic-width": 8,
  "no-default-libraries": false,
  "pre-link-args": {
    "gcc": [
      "-mmcu=attiny88",
      "-Wl,--as-needed"
    ]
  },
  "target-c-int-width": "16",
  "target-pointer-width": "16"
}

A  => main.rs +0 -0
A  => rust-toolchain.toml +2 -0
@@ 1,2 @@
[toolchain]
channel = "nightly-2022-05-10"

A  => src/animation.rs +172 -0
@@ 1,172 @@
use super::filled_seven_segment;
use super::led_matrix;

#[derive(PartialEq, Eq)]
pub enum AnimationState {
    Running,
    End
}

pub trait Animation {
    fn step(&mut self, seven_segment: &mut filled_seven_segment::FilledSevenSegment, led_matrix: &mut led_matrix::LEDMatrix) -> AnimationState;
    fn cleanup(&mut self, seven_segment: &mut filled_seven_segment::FilledSevenSegment, led_matrix: &mut led_matrix::LEDMatrix);
    fn running(&self) -> bool;
}

pub struct HelloAnimation {
    pub inner_step: u16,
    pub outer_step: u16,
    pub hidden: bool
}

pub struct WinAnimation {
    pub number: u16,
    pub led_step: u8,
    pub led_quarter: u8,
    pub led_inner: u16,
    pub hidden: bool
}

const WIN_ANIMATION_MAX_LED_OUTER_STEP: u8 = 5; // max led_step
const WIN_ANIMATION_MAX_LED_STEP: u8 = 4; // add led_step
const WIN_ANIMATION_MAX_LED_INNER_STEP: u16 = 2500; // 10000; // add led_quarter

const HELO_ANIMATION_MAX_INNER_STEP: u16 = 5000; // 20000;
const HELO_ANIMATION_MAX_OUTER_STEP: u16 = 5;

impl WinAnimation {
    pub fn create(number: u16) -> WinAnimation {
        WinAnimation {
            number,
            led_inner: 0,
            led_quarter: 0,
            led_step: 0,
            hidden: true
        }
    }

    pub fn reset(&mut self, number: u16) {
        self.number = number;
        self.led_step = 0;
        self.led_quarter = 0;
        self.led_inner = 0;
        self.hidden = true;
    }
}

impl Animation for WinAnimation {
    fn step(&mut self, seven_segment: &mut filled_seven_segment::FilledSevenSegment, led_matrix: &mut led_matrix::LEDMatrix) -> AnimationState {
        if self.led_inner == 0 && self.led_quarter == 0 && self.led_step == 0 {
            seven_segment.set_number(self.number);
            led_matrix.clear();
        }

        if self.led_inner > WIN_ANIMATION_MAX_LED_INNER_STEP {
            self.led_inner = 0;
            self.led_quarter += 1;
        }

        if self.led_quarter >= WIN_ANIMATION_MAX_LED_STEP {
            self.led_quarter = 0;
            self.led_step += 1;
        }

        if self.led_step > WIN_ANIMATION_MAX_LED_OUTER_STEP {
            return AnimationState::End;
        }

        led_matrix.clear();

        if self.led_step < 2 {
            led_matrix.set(self.led_quarter, self.led_step, true);
        } else if self.led_step == 2 {
            led_matrix.set(self.led_quarter, 0, true);
            led_matrix.set(self.led_quarter, 1, true);
        } else if self.led_step == 3 {
            led_matrix.set(3 - self.led_quarter, 0, true);
            led_matrix.set(3 - self.led_quarter, 1, true);
        } else {
            for i in 0..=self.led_quarter {
                led_matrix.set(i, 0, true);
                led_matrix.set(i, 1, true);
            }
        }

        if (self.led_quarter == 2 || self.led_quarter == 0) && self.led_inner == 0 {
            if self.hidden {
                seven_segment.show_all_digits();
            } else {
                seven_segment.hide_all_digits();
            }

            self.hidden = !self.hidden;
        }

        self.led_inner += 1;
        AnimationState::Running
    }

    fn cleanup(&mut self, seven_segment: &mut filled_seven_segment::FilledSevenSegment, led_matrix: &mut led_matrix::LEDMatrix) {
        led_matrix.set_data(0xFF);
        seven_segment.show_all_digits();
    }

    fn running(&self) -> bool {
        self.led_step < WIN_ANIMATION_MAX_LED_OUTER_STEP
    }
}

impl HelloAnimation {
    pub fn create() -> HelloAnimation {
        HelloAnimation {
            inner_step: 0,
            outer_step: 0,
            hidden: false
        }
    }
}

impl Animation for HelloAnimation {

    fn step(&mut self, seven_segment: &mut filled_seven_segment::FilledSevenSegment, led_matrix: &mut led_matrix::LEDMatrix) -> AnimationState {
        // Helo text
        if self.inner_step == 0 && self.outer_step == 0 {
            seven_segment.set_digit(3, Some(72)); // H
            seven_segment.set_digit(2, Some(69)); // E
            seven_segment.set_digit(1, Some(76)); // L
            seven_segment.set_digit(0, Some(79)); // O
            led_matrix.set_data(0xFF);
        }

        if self.inner_step >= HELO_ANIMATION_MAX_INNER_STEP {
            self.inner_step = 0;
            self.outer_step += 1;

            let matrix_data = led_matrix.data();
            led_matrix.set_data(!matrix_data);

            if self.hidden {
                seven_segment.show_all_digits();
            } else {
                seven_segment.hide_all_digits();
            }
            self.hidden = !self.hidden;
        }

        if self.outer_step == HELO_ANIMATION_MAX_OUTER_STEP {
            return AnimationState::End
        }

        self.inner_step += 1;
        AnimationState::Running
    }

    fn cleanup(&mut self, seven_segment: &mut filled_seven_segment::FilledSevenSegment, led_matrix: &mut led_matrix::LEDMatrix) {
        led_matrix.clear();
        seven_segment.show_all_digits();
    }

    fn running(&self) -> bool {
        self.outer_step < HELO_ANIMATION_MAX_OUTER_STEP
    }
}

A  => src/button.rs +77 -0
@@ 1,77 @@
use atmega_hal::port::{Pin, mode};

#[derive(PartialEq, Eq)]
pub enum ButtonState {
    Inactive, // button is not pressed and the state is same from last time
    Active, // button is pressed and the state is same from last time
    Pressed, // The button was just pressed
    Released // The button was just released
}

const DEBOUNCECYCLES: u8 = 50;

pub struct Button {
    input: Pin<mode::Input>,
    active_high: bool,
    last_active: bool,
    active: bool,
    integrator: u8
}

impl Button {
    pub fn create(input: Pin<mode::Input>, active_high: bool) -> Button {
        Button {
            input,
            active_high,
            last_active: false,
            active: false,
            integrator: 0,
        }
    }

    pub fn step(&mut self) {
        let mut btn_active = self.input.is_low();
        if self.active_high {
            btn_active = !btn_active;
        }

        if !btn_active {
            if self.integrator > 0 {
                self.integrator -= 1;
            }
        } else if self.integrator < DEBOUNCECYCLES {
            self.integrator += 1;
        }

        self.active = self.pressed();
    }

    fn pressed(&mut self) -> bool{
        if self.integrator == 0 {
            self.last_active = self.active;
            self.active = false;
        } else if self.integrator >= DEBOUNCECYCLES {
            self.integrator = DEBOUNCECYCLES;
            self.last_active = self.active;
            self.active = true;
        }

        self.active
    }

    pub fn state(&self) -> ButtonState {
        if self.active {
            if self.last_active {
                return ButtonState::Active;
            }

            return ButtonState::Pressed;
        }

        if !self.last_active {
            return ButtonState::Inactive;
        }

        return ButtonState::Released;
    }
}

A  => src/entrypoint.rs +281 -0
@@ 1,281 @@
#![no_std]
#![no_main]

mod sipo;
mod filled_sipo;
mod seven_segment;
mod filled_seven_segment;
mod led_matrix;
mod animation;
mod button;
mod rng;

use panic_halt as _;

const DIGITS: usize = 4;
const LED_MATRIX_CORRECT_ROW: u8 = 0;
const LED_MATRIX_INCORRECT_POSITION_ROW: u8 = 1;

static mut HELLO_ANIMATION: animation::HelloAnimation = animation::HelloAnimation { inner_step: 0, outer_step: 0, hidden: false };
static mut WIN_ANIMATION: animation::WinAnimation = animation::WinAnimation { number: 0, led_step: 0, led_quarter: 0, led_inner: 0, hidden: true } ;

#[atmega_hal::entry]
fn main() -> ! {
    // PERIPHERALS
    let dp = atmega_hal::Peripherals::take().unwrap();
    let pins = atmega_hal::pins!(dp);

    let srclr = pins.pc2.into_output().downgrade();
    let srclk = pins.pc3.into_output().downgrade();
    let rclk = pins.pc4.into_output().downgrade();
    let ser = pins.pc5.into_output().downgrade();

    let shift_register = sipo::Sipo::create(srclk, srclr, ser, rclk);

    let shift_register = filled_sipo::FilledSipo::create(shift_register);
    let seven_segment = seven_segment::SevenSegment::create(4, true, false);
    let seven_segment = filled_seven_segment::FilledSevenSegment::create(seven_segment, shift_register);

    let mut matrix = led_matrix::LEDMatrix::create(4, 2);
    matrix.add_anode(pins.pd3.into_output().downgrade());
    matrix.add_anode(pins.pd2.into_output().downgrade());
    matrix.add_anode(pins.pd1.into_output().downgrade());
    matrix.add_anode(pins.pd0.into_output().downgrade());

    matrix.add_cathode(pins.pd5.into_output().downgrade());
    matrix.add_cathode(pins.pd6.into_output().downgrade());

    let in_1 = pins.pc1.into_pull_up_input().downgrade().forget_imode();
    let in_2 = pins.pb2.into_pull_up_input().downgrade().forget_imode();
    let in_3 = pins.pb1.into_pull_up_input().downgrade().forget_imode();
    let in_4 = pins.pd7.into_pull_up_input().downgrade().forget_imode();
    let in_confirm = pins.pb0.into_pull_up_input().downgrade().forget_imode();

    let btn_1 = button::Button::create(in_1, false);
    let btn_2 = button::Button::create(in_2, false);
    let btn_3 = button::Button::create(in_3, false);
    let btn_4 = button::Button::create(in_4, false);
    let btn_confirm = button::Button::create(in_confirm, false);
    // PERIPHERALS END

    // RNG
    let rng = rng::Rng::init(123, 111, 45);

    // GAME
    let mut game = Game {
        seven_segment,
        led_matrix: matrix,
        state: GameState::Start,
        guessing_number: None,
        current_number: None,
        animation: None,
        rng,
        buttons: [btn_1, btn_2, btn_3, btn_4],
        confirm: btn_confirm
    };

    unsafe {
        HELLO_ANIMATION = animation::HelloAnimation::create();
        WIN_ANIMATION = animation::WinAnimation::create(0);
        game.animation = Some(&mut HELLO_ANIMATION);
    }

    let mut step: u64 = 0;
    loop {
        // Show seven segment, matrix data
        step += 1;
        game.seven_segment.step();
        if step > 50 {
            game.led_matrix.step();
            step = 0;
        }

        for button in game.buttons.iter_mut() {
            button.step();
        }
        game.confirm.step();

        // Animation logic
        if let Some(animation) = &mut game.animation {
            if animation.running() {
                let state = animation.step(&mut game.seven_segment, &mut game.led_matrix);

                if state == animation::AnimationState::End {
                    animation.cleanup(&mut game.seven_segment, &mut game.led_matrix);
                    game.animation = None;
                }
            } else {
                animation.cleanup(&mut game.seven_segment, &mut game.led_matrix);
                game.animation = None;
            }
        }

        game.step();
    }
}

pub struct Game {
    seven_segment: filled_seven_segment::FilledSevenSegment,
    led_matrix: led_matrix::LEDMatrix,
    state: GameState,
    guessing_number: Option<u16>,
    current_number: Option<u16>,
    animation: Option<&'static mut dyn animation::Animation>,
    rng: rng::Rng,
    buttons: [button::Button; 4],
    confirm: button::Button
}

pub enum GameState {
    Start,
    Play,
    Won
}

impl Game {
    pub fn step(&mut self) {
        match self.state {
            GameState::Start | GameState::Won => {
                if self.any_button_pressed() {
                    self.start_new_game();
                }
            },
            GameState::Play => {
                if self.confirm.state() == button::ButtonState::Pressed {
                    if self.current_number == self.guessing_number {
                        self.end_current_game();
                        return;
                    }

                    self.update_led_matrix();
                }

                let mut btns_pressed: [bool; DIGITS] = [false; DIGITS];
                for (i, button) in self.buttons.iter().enumerate() {
                    let state = button.state();
                    btns_pressed[i] = state == button::ButtonState::Pressed;
                }

                for (i, pressed) in btns_pressed.iter().enumerate() {
                    if *pressed {
                        self.increase_digit(DIGITS - 1 - i);
                    }
                }
            }
        }
    }

    fn get_digit(number: u16, digit_index: usize) -> u8 {
        let mut digit = number;
        for _ in 0..digit_index {
            digit /= 10;
        }

        (digit % 10).try_into().unwrap()
    }

    fn update_led_matrix(&mut self) {
        self.led_matrix.clear();
        let current_number = self.current_number.unwrap();
        let guessing_number = self.guessing_number.unwrap();

        let mut current_digits: [u8; DIGITS] = [0, 0, 0, 0];
        let mut guessing_digits: [u8; DIGITS] = [0, 0, 0, 0];


        for i in 0..DIGITS {
            current_digits[i] = Self::get_digit(current_number, i);
            guessing_digits[i] = Self::get_digit(guessing_number, i);
        }

        for j in 0..2 {
            self.led_matrix.set(
                0,
                1,
                false
            );
        }

        for i in 0..DIGITS {
            if current_digits[i] == guessing_digits[i] {
                self.led_matrix.set(
                    i.try_into().unwrap(),
                    0,
                    true
                );
            }
            else {
                for j in 0..DIGITS {
                    if current_digits[j] != guessing_digits[j] && current_digits[i] == guessing_digits[j] {
                        /*self.led_matrix.set(
                            i.try_into().unwrap(),
                            1,
                            true
                        );*/
                    }
                }

            }
        }
    }

    fn increase_digit(&mut self, digit_index: usize) {
        let current_number = self.current_number.unwrap();
        let mut order = 1;
        for _ in 0..digit_index {
            order *= 10;
        }

        let current_digit = Self::get_digit(current_number, digit_index);
        let new_digit: u16 = ((current_digit + 1) % 10).into();

        let trimmed_number = current_number % order;
        let mut new_number = current_number - (current_number % (order*10));
        new_number += new_digit * order + trimmed_number;

        self.current_number = Some(new_number);
        self.seven_segment.set_number(new_number);
    }

    fn end_current_game(&mut self) {
        // TODO: win animation
        //self.animation = None;
        unsafe {
            WIN_ANIMATION.reset(self.guessing_number.unwrap());
            self.animation = Some(&mut WIN_ANIMATION);
        }
        self.cleanup_current_game();
        self.state = GameState::Won;
    }

    fn cleanup_current_game(&mut self) {
        self.guessing_number = None;
        self.current_number = None;
    }

    fn start_new_game(&mut self) {
        if let Some(animation) = &mut self.animation {
            animation.cleanup(&mut self.seven_segment, &mut self.led_matrix);
            self.animation = None;
        }

        self.guessing_number = Some(self.rng.take_u16() % 10000);
        self.current_number = Some(self.guessing_number.unwrap());
        self.seven_segment.set_number(self.current_number.unwrap());
        self.led_matrix.clear();

        self.state = GameState::Play;
    }

    fn any_button_pressed(&mut self) -> bool {
        for btn in self.buttons.iter() {
            let state = btn.state();

            if state == button::ButtonState::Pressed {
                return true;
            }
        }

        self.confirm.state() == button::ButtonState::Pressed
    }
}

A  => src/filled_seven_segment.rs +111 -0
@@ 1,111 @@
use super::seven_segment;
use super::filled_sipo;

pub struct FilledSevenSegment {
    seven_segment: seven_segment::SevenSegment,
    sipo: filled_sipo::FilledSipo,
    digits: [Option<u8>; 4],
    hide: u8,
    update_step: usize
}

impl FilledSevenSegment {
    pub fn create(seven_segment: seven_segment::SevenSegment, sipo: filled_sipo::FilledSipo) -> FilledSevenSegment {
        FilledSevenSegment {
            seven_segment,
            sipo,
            digits: [None, None, None, None],
            hide: 0,
            update_step: 0
        }
    }

    fn get_digit(digit: u16, digit_index: usize) -> u8 {
        let mut digit = digit;
        for _ in 0..digit_index {
            digit /= 10;
        }

        return (digit % 10).try_into().unwrap();
    }

    pub fn hide_digit(&mut self, digit_index: usize) {
        self.hide |= 1 << digit_index;
    }

    pub fn show_digit(&mut self, digit_index: usize) {
        self.hide &= !(1 << digit_index);
    }

    pub fn hide_all_digits(&mut self) {
        self.hide = 0xFF;
    }

    pub fn show_all_digits(&mut self) {
        self.hide = 0;
    }

    pub fn set_digit(&mut self, digit_index: usize, digit: Option<u8>) {
        if digit_index < 4 {
            self.digits[digit_index] = digit;
        }
    }

    pub fn set_number(&mut self, number: u16) {
        for i in 0..4_usize {
            let digit = Self::get_digit(number, i);
            self.digits[i] = Some(digit);
        }
    }

    pub fn show_number_block(&mut self) {
        while !self.step() {}
    }

    fn fill_digit(&mut self, digit_index: usize) {
        if digit_index > 3 {
            return
        }

        if (self.hide & (1 << digit_index)) != 0 {
            self.sipo.clear();
            return
        }

        if let Some(digit) = self.digits[digit_index] {
            self.seven_segment.fill_digit(&mut self.sipo, digit, digit_index);
        } else {
            self.sipo.clear();
        }
    }

    pub fn step(&mut self) -> bool {
        if self.update_step == 0 {
            self.fill_digit(0);
            self.update_step = 1;
        }

        if self.sipo.step() {
            self.update_step += 1;

            if self.update_step <= self.seven_segment.digits().into() {
                self.fill_digit(self.update_step - 1);
            }
        }

        if self.update_step > self.seven_segment.digits().into() {
            self.update_step = 0;
            return true;
        }

        return false;
    }

    pub fn reset(&mut self) {
        self.update_step = 0;
    }

    pub fn clear(&mut self) {
        self.digits = [None, None, None, None];
    }
}

A  => src/filled_sipo.rs +49 -0
@@ 1,49 @@
use super::sipo;

pub struct FilledSipo {
    shift_register: sipo::Sipo,
    data: u16,
    update_step: u8
}

impl FilledSipo {
    pub fn create(shift_register: sipo::Sipo) -> FilledSipo {
        FilledSipo {
            shift_register,
            data: 0,
            update_step: 0
        }
    }

    pub fn set_data(&mut self, data: u16) {
        self.data = data;
        self.reset();
    }

    pub fn push_block(&mut self) {
        while !self.step() {
        }
    }

    pub fn step(&mut self) -> bool {
        self.shift_register.shift_value((self.data >> (15 - self.update_step)) & 1 == 1);

        if self.update_step >= 15 {
            self.update_step = 0;
            self.shift_register.show();
            return true;
        }

        self.update_step += 1;
        return false;
    }

    pub fn reset(&mut self) {
        self.update_step = 0;
    }

    pub fn clear(&mut self) {
        self.set_data(0);
        self.reset();
    }
}

A  => src/led_matrix.rs +102 -0
@@ 1,102 @@
use atmega_hal::port::{Pin, mode};

pub struct LEDMatrix {
    width: u8,
    height: u8,
    data: u8,
    anodes: [Option<Pin<mode::Output>>; 8],
    cathodes: [Option<Pin<mode::Output>>; 8],
    anodes_count: usize,
    cathodes_count: usize,
    update_step: usize
}

impl LEDMatrix {
    pub fn create(width: u8, height: u8) -> LEDMatrix {
        LEDMatrix {
            width,
            height,
            data: 0,
            anodes: [None, None, None, None, None, None, None, None],
            cathodes: [None, None, None, None, None, None, None, None],
            anodes_count: 0,
            cathodes_count: 0,
            update_step: 0
        }
    }

    fn get_position(width: u8, x: u8, y: u8) -> u8 {
        return width*y + x;
    }

    pub fn data(&self) -> u8 {
        self.data
    }

    pub fn set_data(&mut self, data: u8) {
        self.data = data;
    }

    pub fn set(&mut self, x: u8, y: u8, value: bool) {
        if x >= self.width || y >= self.height {
            return
        }

        let mask = 1 << Self::get_position(self.width, x, y);
        if value {
            self.data |= mask;
        } else {
            self.data &= !mask;
        }
    }

    pub fn add_anode(&mut self, anode: Pin<mode::Output>) {
        self.anodes[self.anodes_count] = Some(anode);
        self.anodes_count += 1;
    }

    pub fn add_cathode(&mut self, cathode: Pin<mode::Output>) {
        self.cathodes[self.cathodes_count] = Some(cathode);
        self.cathodes_count += 1;
    }

    pub fn step(&mut self) -> bool {
        let update_unsigned: u8 = self.update_step.try_into().unwrap();
        let first_position: u8 = update_unsigned * self.width;

        for x in 0..self.cathodes_count {
            let cathode = &mut self.cathodes[x];
            if let Some(cathode) = cathode {
                cathode.set_high();
            }
        }

        for x in 0..self.anodes_count {
            let anode = &mut self.anodes[x];
            if let Some(anode) = anode {
                let x_unsigned: u8 = x.try_into().unwrap();
                if self.data & (1 << (first_position + x_unsigned)) != 0 {
                    anode.set_high();
                } else {
                    anode.set_low();
                }
            }
        }

        if let Some(cathode) = &mut self.cathodes[self.update_step] {
            cathode.set_low();
        }

        self.update_step += 1;
        if self.update_step >= self.height.into() {
            self.update_step = 0;
            return true;
        }

        return false;
    }

    pub fn clear(&mut self) {
        self.data = 0;
    }
}

A  => src/rng.rs +44 -0
@@ 1,44 @@
pub struct Rng {
    a: u8,
    b: u8,
    c: u8,
    x: u8
}

impl Rng {
    pub fn init(s1: u8, s2: u8, s3: u8) -> Rng {
        let mut rng = Rng {
            a: 0,
            b: 0,
            c: 0,
            x: 0
        };

        rng.a ^= s1;
        rng.b ^= s2;
        rng.c ^= s3;
        rng.randomize();

        rng
    }

    fn randomize(&mut self) -> u8 {
        self.x += 1;
        self.a = self.a^self.c^self.x;
        self.b = self.b+self.a;
        self.c = self.c + (self.b >> 1)^self.a;

        self.c
    }

    pub fn take_u8(&mut self) -> u8 {
        self.randomize()
    }

    pub fn take_u16(&mut self) -> u16 {
        let first: u16 = self.randomize().into();
        let second: u16 = self.randomize().into();

        first << 8 | second
    }
}

A  => src/seven_segment.rs +100 -0
@@ 1,100 @@
use super::filled_sipo;

pub struct SevenSegment {
    digits: u8,
    dp: bool,
    common_cathode: bool
}

impl SevenSegment {
    pub fn create(digits: u8, dp: bool, common_cathode: bool) -> SevenSegment {
        SevenSegment {
            digits,
            dp,
            common_cathode
        }
    }

    fn get_digit_segments(digit: u8) -> u8 {
        match digit {
            //     HGFEDCBA
            0 => 0b00111111,   // 0
            1 => 0b00000110,   // 1
            2 => 0b01011011,   // 2
            3 => 0b01001111,   // 3
            4 => 0b01100110,   // 4
            5 => 0b01101101,   // 5
            6 => 0b01111101,   // 6
            7 => 0b00000111,   // 7
            8 => 0b01111111,   // 8
            9 => 0b01101111,   // 9
            65 => 0b01110111,  // A
            98 => 0b01111100,  // b
            67 => 0b00111001,  // C
            99 => 0b01011000,  // c
            100 => 0b01011110, // d
            69 => 0b01111001,  // E
            70 => 0b01110001,  // F
            71 => 0b00111101,  // G
            103 => 0b01101111, // g
            72 => 0b011110110, // H
            104 => 0b01110100, // h
            73 => 0b00000110,  // I
            76 => 0b00111000,  // L
            108 => 0b00110000, // l
            110 => 0b01010100, // n
            78 => 0b00110111, // N
            79 => 0b00111111,  // O
            111 => 0b01011100, // o
            80 => 0b01110011,  // P
            81 => 0b01100111,  // Q
            114 => 0b01010000, // r
            83 => 0b01101101,  // S
            116 => 0b01111000, // t
            85 => 0b00111110,  // U
            _ => 0b00000000,  // nothing
        }
    }

    pub fn digits(&self) -> u8 {
        return self.digits;
    }

    pub fn dp(&self) -> bool {
        return self.dp;
    }

    pub fn common_cathode(&self) -> bool {
        return self.common_cathode;
    }

    fn get_digit_selector(digit_count: u8, digit_index: usize) -> u8 {
        let digit_count: usize = digit_count.into();
        return 1 << (digit_count - 1 - digit_index);
    }

    pub fn fill_digit(&self, sipo: &mut filled_sipo::FilledSipo, digit: u8, digit_index: usize) -> bool {
        if digit_index >= self.digits.into() {
            return false;
        }

        let mut segments = SevenSegment::get_digit_segments(digit);
        let mut digit_selector = SevenSegment::get_digit_selector(self.digits, digit_index);

        if self.common_cathode {
            digit_selector = !digit_selector;
        } else {
            segments = !segments;
        }

        let segments: u16 = segments.into();
        let digit_selector: u16 = digit_selector.into();

        if self.dp {
            sipo.set_data(digit_selector << 8 | segments);
        } else {
            sipo.set_data(digit_selector << 7 | (segments & 0x7F));
        }
        return true;
    }
}

A  => src/sipo.rs +57 -0
@@ 1,57 @@
use atmega_hal::port::{Pin, mode};

pub struct Sipo {
    srclk : Pin<mode::Output>,
    srclr : Pin<mode::Output>,
    ser : Pin<mode::Output>,
    rclk : Pin<mode::Output>,
}

impl Sipo {
    pub fn create(srclk: Pin<mode::Output>, srclr: Pin<mode::Output>, ser: Pin<mode::Output>, rclk: Pin<mode::Output>) -> Sipo {
        let mut sipo = Sipo {
            srclk,
            srclr,
            ser,
            rclk
        };

        sipo.setup();
        return sipo;
    }

    pub fn setup(&mut self) {
        self.clear();
        self.show();
    }

    pub fn set(&mut self, value: bool) {
        if value {
            self.ser.set_high();
        } else {
            self.ser.set_low();
        }
    }

    pub fn shift(&mut self) {
        self.srclk.set_low();
        self.srclk.set_high();
        self.srclk.set_low();
    }

    pub fn shift_value(&mut self, value: bool) {
        self.set(value);
        self.shift();
    }

    pub fn show(&mut self) {
        self.rclk.set_low();
        self.rclk.set_high();
        self.rclk.set_low();
    }

    pub fn clear(&mut self) {
        self.srclr.set_low();
        self.srclr.set_high();
    }
}

Do not follow this link