#
# hello.A4949.RP2040.py
#    A4949 BLDC RP2040 hello-world
#
# Neil Gershenfeld 10/27/24
#
# This work may be reproduced, modified, distributed,
# performed, and displayed for any purpose, but must
# acknowledge this project. Copyright is retained and
# must be preserved. The work is provided as is; no
# warranty is provided, and users accept all liability.
#
# install MicroPython
#    https://micropython.org/download/RPI_PICO/
#

from rp2 import PIO,StateMachine,asm_pio
import machine,sys,time

machine.freq(250_000_000)

pwm_freq = 100_000_000
pwm_range = 1000
pwm_pin = 0

@asm_pio(sideset_init=PIO.OUT_LOW)
def pwm_prog():
    pull(noblock).side(0) # empty FIFO moves X to OSR
    mov(x,osr) # OSR has PWM threshold
    mov(y,isr) # ISR has PWM range
    label("loop")
    jmp(x_not_y,"skip")
    nop().side(1) # toggle output when X matches Y
    label("skip")
    jmp(y_dec,"loop") # loop over Y

class pwm_pio:
    def __init__(self,sm_id,pwm_pin,pwm_range,pwm_freq):
        self._sm = StateMachine(sm_id,pwm_prog,freq=pwm_freq,sideset_base=machine.Pin(pwm_pin))
        self._sm.put(pwm_range)
        self._sm.exec("pull()")
        self._sm.exec("mov(isr,osr)")
        self._sm.active(1)
        self._max_count = pwm_range
        self.put = self._sm.put

pwm = pwm_pio(0,pwm_pin,pwm_range,pwm_freq)
pwm.put(0)

@asm_pio()
def cycle_timer_prog():
    label("start")
    #
    pull() # load and save timer init
    mov(x,osr)
    mov(y,x)
    #
    # wait for pin high
    #
    label("wait high")
    jmp(pin,"wait high continue")
    jmp(x_dec,"wait high")
    jmp("timeout")
    #
    label("wait high continue")
    #
    # wait for pin low
    #
    label("wait low")
    jmp(pin,"wait low skip")
    jmp("wait low continue")
    label("wait low skip")
    jmp(x_dec,"wait low")
    jmp("timeout")
    #
    label("wait low continue")
    mov(x,y) # reset x
    #
    # time low
    #
    label("time low")
    jmp(pin,"time low continue") # 1 clock cycle
    jmp(x_dec,"time low") # 1 clock cycle
    jmp("timeout")
    #
    label("time low continue")
    #
    # time high
    #
    label("time high")
    jmp(pin,"time high skip") # 1 clock cycle
    jmp("time high continue")
    label("time high skip")
    jmp(x_dec,"time high") # 1 clock cycle
    jmp("timeout")
    #
    label("time high continue")
    #
    # return
    #
    mov(isr,x)
    push()
    jmp("start")
    #
    # timeout
    #
    label("timeout")
    mov(isr,y)
    push()
    jmp("start")

class cycle_timer_pio:
    def __init__(self,sm_id,cycle_timer_pin,cycle_timer_freq):
        self._sm = rp2.StateMachine(sm_id,cycle_timer_prog,
            jmp_pin=machine.Pin(cycle_timer_pin),freq=cycle_timer_freq)
        self._sm.active(1)
        self.get = self._sm.get
        self.put = self._sm.put

cycle_timer_pin = 7
cycle_timer_freq = 10_000_000
cycle_timer_init = 1_000_000
cycle_timer = cycle_timer_pio(1,cycle_timer_pin,cycle_timer_freq)

def get_count():
    cycle_timer.put(cycle_timer_init)
    return cycle_timer_init-cycle_timer.get()

while True:
    percent = float(input("PWM percent (0-100)? "))
    count = int((percent/100)*pwm_range)
    pwm.put(count)
    count = get_count()
    lastcount = -1
    tolerance = 0.0001
    while True:
        lastcount = count
        count = get_count()
        if (count != 0):
           freq = int(cycle_timer_freq/(2*count)) # 2 clock cycles/count
           print(f"   {freq} Hz")
           if (abs(count-lastcount)/count < tolerance): break
        else:
            print(f"   0 Hz")
            if (count == lastcount): break
        time.sleep_ms(10)
