# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""Drive a 7-segment display entirely from the PIO peripheral

By updating the buffer being written to the display, the shown digits can be changed.

The main program just counts up, looping back to 0000 after 9999.

This example is designed for a Raspberry Pi Pico and bare LED display. For
simplicity, it is wired without any current limiting resistors, instead relying
on a combination of the RP2040's pin drive strength and the 1/4 duty cycle to
limit LED current to an acceptable level, and longevity of the display was not
a priority.

Before integrating a variant of this example code in a project, evaluate
whether your design needs to add current-limiting resistors.

https://www.adafruit.com/product/4864
https://www.adafruit.com/product/865

Wiring:
 * Pico GP15 to LED matrix 1 (E SEG)
 * Pico GP14 to LED matrix 2 (D SEG)
 * Pico GP13 to LED matrix 3 (DP SEG)
 * Pico GP12 to LED matrix 4 (C SEG)
 * Pico GP11 to LED matrix 5 (G SEG)
 * Pico GP10 to LED matrix 6 (COM4)
 * Pico GP9 to LED matrix 7 (COLON COM)
 * Pico GP22 to LED matrix 8 (COLON SEG)
 * Pico GP21 to LED matrix 9 (B SEG)
 * Pico GP20 to LED matrix 10 (COM3)
 * Pico GP19 to LED matrix 11 (COM2)
 * Pico GP18 to LED matrix 12 (F SEG)
 * Pico GP17 to LED matrix 13 (A SEG)
 * Pico GP16 to LED matrix 14 (COM1)
"""

import array
import time

import board
import rp2pio

import adafruit_pioasm

_program = adafruit_pioasm.Program(
    """
    out pins, 14       ; set the pins to their new state
    """
)

# Display Pins 1-7 are GP 15-9
# Display Pins 8-12 are GP 22-16
COM1_WT = 1 << 7
COM2_WT = 1 << 10
COM3_WT = 1 << 11
COM4_WT = 1 << 1
COMC_WT = 1 << 0

SEGA_WT = 1 << 8
SEGB_WT = 1 << 12
SEGC_WT = 1 << 3
SEGD_WT = 1 << 5
SEGE_WT = 1 << 6
SEGF_WT = 1 << 9
SEGG_WT = 1 << 2

SEGDP_WT = 1 << 4
SEGCOL_WT = 1 << 13

ALL_COM = COM1_WT | COM2_WT | COM3_WT | COM4_WT | COMC_WT

SEG_WT = [
    SEGA_WT,
    SEGB_WT,
    SEGC_WT,
    SEGD_WT,
    SEGE_WT,
    SEGF_WT,
    SEGG_WT,
    SEGDP_WT,
    SEGCOL_WT,
]
COM_WT = [COM1_WT, COM2_WT, COM3_WT, COM4_WT, COMC_WT]

DIGITS = [
    0b0111111,  # 0
    0b0000110,  # 1
    0b1011011,  # 2
    0b1001111,  # 3
    0b1100110,  # 4
    0b1101101,  # 5
    0b1111100,  # 6
    0b0000111,  # 7
    0b1111111,  # 8
    0b1101111,  # 9
]


def make_digit_wt(v):
    val = ALL_COM
    seg = DIGITS[v]
    for i in range(8):
        if seg & (1 << i):
            val |= SEG_WT[i]
    return val


DIGITS_WT = [make_digit_wt(i) for i in range(10)]


class SMSevenSegment:
    def __init__(self, first_pin=board.GP9):
        self._buf = array.array("H", (DIGITS_WT[0] & ~COM_WT[i] for i in range(4)))
        self._sm = rp2pio.StateMachine(
            _program.assembled,
            frequency=4000,
            first_out_pin=first_pin,
            out_pin_count=14,
            auto_pull=True,
            pull_threshold=14,
            **_program.pio_kwargs,
        )
        self._sm.background_write(loop=self._buf)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.deinit()

    def deinit(self):
        self._sm.deinit()

    def __setitem__(self, i, v):
        if v is None:
            self._buf[i] = 0
        else:
            self._buf[i] = DIGITS_WT[v] & ~COM_WT[i]

    def set_number(self, number):
        for j in range(4):
            self[3 - j] = number % 10
            number //= 10


def count(start=0):
    val = start
    while True:
        yield val
        val += 1


def main():
    with SMSevenSegment(board.GP9) as s:
        for i in count():
            s.set_number(i)
            time.sleep(0.05)


if __name__ == "__main__":
    main()
