Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 4919

MicroPython • Re: Testing PIO on Pico 2

$
0
0
Here is an example that emulates piezo tone ringers of 1980s phones (like Telefon 01 or Tritel 85 series, etc.) using 2 state machines of any PIO using RX-FIFOs in TXGET mode. One state machine emulates a tone ringer IC (like U4076B/SAA1094-2/M1094, TCM1532, etc.) that plays a repeated 2-4 tone sequence. The other state machine creates the ringing cadences (like for EU/GB/US); basically an extended LED blinker. The inputs are actually inverted open-drain outputs with pull-ups, so that they can be controlled with switches or the Pico 2 itself (only the large passive piezo-ceramic transducer is required for the demo):
ringer.png

Code:

import rp2class StateMachine(rp2.StateMachine):  # augment with putget()    def __init__(self, id, *args, **kwargs):        super().__init__(id)        self._RXF_PUTGET = 0x5020_0128 + (id >> 2 << 20) + (id & 3) * 0x10        if args or kwargs:            self.init(*args, **kwargs)    def putget(self, index, value=None):        if value is not None:            machine.mem32[self._RXF_PUTGET + index * 4] = value        return machine.mem32[self._RXF_PUTGET + index * 4]def gcd(a, b):  # partially emulate math.gcd()    return gcd(b, a % b) if b != 0 else adef lcm(x, *y): # partially emulate math.lcm()    return lcm(x // gcd(x, y[0]) * y[0], *y[1:]) if y else xclass ToneRinger:    def __init__(self, id, pin, out_base, rate=9, freq=1625/6, sequence=(3, 4, 5)):        self.rate = self.freq = self.sequence = None        self.sm = StateMachine(id, self.tone_sequence, in_base=out_base, out_base=out_base, jmp_pin=pin)        self(rate, freq, sequence, True)    # sequence rate/Hz, ratio 1 tuning/Hz, integer ratio tone sequence    def __call__(self, rate=None, freq=None, sequence=None, restart=False):        self.rate = rate or self.rate        self.freq = freq or self.freq        self.sequence = sequence or self.sequence        common  = lcm(*self.sequence)        divisor = round(machine.freq() / (common * 2 * self.freq))        factor  = round(machine.freq() / (common * divisor * self.rate))        delay   = [common // i * divisor - 7 for i in self.sequence]        repeats = [i * factor for i in self.sequence]        while len(delay) < 4:  # pad with single-repeat delays            delay   = [delay[0], delay[0] - 3] + delay[1:]            repeats = [repeats[0] - 1, 0] + repeats[1:]        if restart:            self.sm.active(False)        for i in range(4):  # poke values reversed            self.sm.putget(i, delay[3 - i] << 12 | repeats[3 - i])        if restart:            self.sm.restart()            self.sm.active(True)    @staticmethod    @rp2.asm_pio(out_init=[rp2.PIO.OUT_LOW]*2, out_shiftdir=rp2.PIO.SHIFT_RIGHT)    def tone_sequence():        label("restart")        mov  (pins, null)           # difference 0 on out pins        set  (x, 0b01)              # out pins order is +/-        set  (y, 3)                 # start with 1st in reversed sequence        label("wait")        jmp  (pin, "wait")        mov  (pins, x)        wrap_target()        label("wrap_target")        word (0x8090)               # mov  (osr, rxfifo[y])        out  (x, 12)                # repeats (square wave half-cycles): 12 lower bits        label("tone")        mov  (isr, y)               # save y        mov  (y, osr)               # delay cycles (per half-cycle): 20 upper bits        label("delay")        jmp  (y_dec, "delay")        mov  (y, isr)               # restore y        jmp  (pin, "restart")        mov  (pins, invert(pins))   # toggle pins (in_base == out_base)        jmp  (x_dec, "tone")        jmp  (y_dec, "wrap_target")        wrap()ToneRinger.tone_sequence[4] |= 1 << 14  # fifo_join=rp2.PIO.JOIN_TXGET fixupclass CadenceGenerator:    def __init__(self, id, pin, out_base, cadence=(1, 4)):        self.sm = StateMachine(id, self.ring_sequence, out_base=out_base, jmp_pin=pin)        self(cadence, True)    # cadence sequence/s    def __call__(self, cadence=None, restart=True):        if cadence:            delay = [round((machine.freq() * i - 8) / 2) for i in cadence]            while len(delay) < 4:  # pad with 0-delays                delay = delay[:-1] + [0, delay[-1] - 2]        if restart:            self.sm.active(False)        if cadence:            for i in range(4):  # poke values reversed                self.sm.putget(i, delay[3 - i])        if restart:            self.sm.restart()            self.sm.active(True)    @rp2.asm_pio(out_init=[rp2.PIO.IN_LOW]*1)  # L-type open-circuit output (inverted open-drain)    def ring_sequence():        label("restart")        word (0xa063)                   # mov  (pindirs, null)        mov  (isr, null)                # state for pindirs (because it is not readable)        set  (y, 3)                     # start with 1st in reversed cadence        label("wait")        jmp  (pin, "wait")        word (0xa06b)                   # mov  (pindirs, invert(null))        wrap_target()        label("wrap_target")        word (0x8090)                   # mov  (osr, rxfifo[y])        mov  (x, osr)        jmp  (not_x, "skip")        label("delay")        jmp  (pin, "restart")        jmp  (x_dec, "delay")        word (0xa066)                   # mov  (pindirs, isr)        mov  (isr, invert(isr))         # toggle pindirs state        label("skip")        jmp  (y_dec, "wrap_target")        wrap()CadenceGenerator.ring_sequence[4] |= 1 << 14  # fifo_join=rp2.PIO.JOIN_TXGET fixupfrom machine import Pinfrom time import sleepenable    = Pin(10, Pin.OPEN_DRAIN, Pin.PULL_UP)indicator = Pin(19, Pin.OPEN_DRAIN, Pin.PULL_UP)piezo     = Pin(14, Pin.OUT, None, value=0), Pin(15, Pin.OUT, None, value=0)cadence = CadenceGenerator(1, pin=enable, out_base=indicator)ringer  = ToneRinger(0, pin=indicator, out_base=piezo[0])enable(0)sleep(20)enable(1)sleep(1)ringer(18)cadence((0.4, 2, 0.4, 0.2))enable(0)sleep(20)enable(1)sleep(1)ringer(rate=9.8, freq=2500/15, sequence=(8, 7))cadence((2, 4))enable(0)sleep(20)enable(1)

Statistics: Posted by PicoTinker — Fri Jan 10, 2025 5:24 am



Viewing all articles
Browse latest Browse all 4919

Trending Articles