adafruit / Adafruit_CircuitPython_PIOASM

Simple assembler to convert pioasm to bytes
MIT License
27 stars 17 forks source link

OUT pin and SIDE_SET pin clash #55

Closed backgroundprocessing closed 1 year ago

backgroundprocessing commented 1 year ago

The UART_TX program, and my similar program, fail.

Single OUT pin and single SIDE pin mapped to same GPIO. GPIO does not change with out pins, 1 but stays at the last SIDE setting.

In the UART program, the stop and start bits are output, but no data. Setting the SIDE pin to a different GPIO sees the data on the OUT GPIO and the stop/start bits on the SIDE GPIO.

.program uart_tx
.side_set 1 opt

; An 8n1 UART transmit program.
; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.

    pull       side 1 [7]  ; Assert stop bit, or stall with line in idle state
    set x, 7   side 0 [7]  ; Preload bit counter, assert start bit for 8 clocks
bitloop:                   ; This loop will run 8 times (8n1 UART)
    out pins, 1            ; Shift 1 bit from OSR to the first OUT pin
    jmp x-- bitloop   [6]  ; Each loop iteration is 8 cycles.
tannewt commented 1 year ago

What version of CircuitPython are you running? Are any exceptions raised or does it just not work?

backgroundprocessing commented 1 year ago

Adafruit CircuitPython 8.0.4 on 2023-03-15; Raspberry Pi Pico with rp2040

Thonny is not showing any errors.

backgroundprocessing commented 1 year ago

Seems to be a false alarm. I was accessing the state machine directly, like the Adafruit learning examples to flash the LED etc. Now I am trying to use a class, and it looks to be working.

Thanks for your time.

backgroundprocessing commented 1 year ago

I have adapted the Morse demo program to show the (apparent) different behaviours. Run the program, then call one of morseClassSamePin() morseClassAltPin() morseSamePin() morseAltPin()

The first three work as expected, showing morse on the builtin LED, the 4th does not show any LED activity.

# SPDX-FileCopyrightText: 2022 Jeff Epler, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
# Modified 21 April 2023 Bruce Wilson to show usage with Class, and issue with using sideset on the same pin.
# Run the program, then call one of
#   morseClassSamePin()
#   morseClassAltPin() 
#   morseSamePin()
#   morseAltPin()

"""Demonstrate background writing, including loop writing, with morse code.

On any rp2040 board with board.LED, this will alternately send 'SOS' and 'TEST'
via the LED, demonstrating that Python code continues to run while the morse
code data is transmitted. Alternately, change one line below to make it send
'TEST' forever in a loop, again while Python code continues to run.

The combination of "LED status" and duration is sent to the PIO as 16-bit number:
The top bit is 1 if the LED is turned on and 0 otherwise.  The other 15 bits form a delay
value from 1 to 32767. A subset of the morse code 'alphabit' is created, with everthing
based on the 'DIT duration' of about 128ms (1MHz / 32 / 4000).

https://en.wikipedia.org/wiki/Morse_code
"""

import array
import time
import board
import rp2pio
from adafruit_pioasm import Program
from rp2pio import StateMachine

myLEDpin = board.LED
mySidePin = board.GP0

pio_code = Program(
    """
    .side_set 1 opt
        pull
        out x, 1
        mov pins, x
        out x, 15
    busy_wait:
        nop [7]
        nop [7]
        nop [7]
        jmp x--, busy_wait [7]
        """
)

# The top bit of the command is the LED value, on or off
LED_ON = 0x8000
LED_OFF = 0x0000

# The other 15 bits are a delay duration.
# It must be the case that 4 * DIT_DURATION < 32768
DIT_DURATION = 4000
DAH_DURATION = 3 * DIT_DURATION

# Build up some elements of morse code, based on the wikipedia article.
DIT = array.array("H", [LED_ON | DIT_DURATION, LED_OFF | DIT_DURATION])
DAH = array.array("H", [LED_ON | DAH_DURATION, LED_OFF | DIT_DURATION])
# That is, two more DAH-length gaps for a total of three
LETTER_SPACE = array.array("H", [LED_OFF | (2 * DAH_DURATION)])
# That is, four more DAH-length gaps (after a letter space) for a total of seven
WORD_SPACE = array.array("H", [LED_OFF | (4 * DIT_DURATION)])

# Letters and words can be created by concatenating ("+") the elements
E = DIT + LETTER_SPACE
O = DAH + DAH + DAH + LETTER_SPACE
S = DIT + DIT + DIT + LETTER_SPACE
T = DAH + LETTER_SPACE
SOS = S + O + S + WORD_SPACE
TEST = T + E + S + T + WORD_SPACE

class MORSE:
    def __init__(self, *, led_pin, side_pin):
        self.pio = rp2pio.StateMachine(
            pio_code.assembled,
            first_out_pin=led_pin,
            first_sideset_pin=side_pin,
            frequency=1000000,
            auto_pull=False,
            initial_sideset_pin_state=1,
            initial_sideset_pin_direction=1,
            initial_out_pin_state=0,
            initial_out_pin_direction=1,
            out_shift_right=False,
            **pio_code.pio_kwargs,
        )
    def write(self, buf):
        print ("writing")
        return self.pio.write(buf)

def morseClassSamePin():
    myMorse = MORSE(led_pin = myLEDpin, side_pin = myLEDpin)
    while True:
        myMorse.write(SOS)
        time.sleep(2)

def morseClassAltPin():
    myMorse = MORSE(led_pin = myLEDpin, side_pin = mySidePin)
    while True:
        myMorse.write(SOS)
        time.sleep(2)

def morseAltPin():
    sm = StateMachine(
        pio_code.assembled,
        frequency=1_000_000,
        first_out_pin=myLEDpin,
        first_sideset_pin=mySidePin,
        pull_threshold=16,
        auto_pull=False,
        out_shift_right=False,
        initial_sideset_pin_state=1,
        initial_sideset_pin_direction=1,
        initial_out_pin_state=0,
        initial_out_pin_direction=1,
    )
    while True:
        for plain, morse in (
            ("SOS", SOS),
            ("TEST", TEST),
        ):
            print(f"Sending out {plain}", end="")
            sm.background_write(morse)
            sm.clear_txstall()
            while not sm.txstall:
                print(end=".")
                time.sleep(0.1)
            print()
            print("Message all sent to StateMachine (including emptying FIFO)")
            print()
            time.sleep(2)

def morseSamePin():
    sm = StateMachine(
        pio_code.assembled,
        frequency=1_000_000,
        first_out_pin=myLEDpin,
        first_sideset_pin=myLEDpin,
        pull_threshold=16,
        auto_pull=False,
        out_shift_right=False,
        initial_sideset_pin_state=1,
        initial_sideset_pin_direction=1,
        initial_out_pin_state=1,
        initial_out_pin_direction=1,
    )
    while True:
        for plain, morse in (
            ("SOS", SOS),
            ("TEST", TEST),
        ):
            print(f"Sending out {plain}", end="")
            sm.background_write(morse)
            sm.clear_txstall()
            while not sm.txstall:
                print(end=".")
                time.sleep(0.1)
            print()
            print("Message all sent to StateMachine (including emptying FIFO)")
            print()
            time.sleep(2)
jepler commented 1 year ago

Hi! I think you have made a mistake with the StateMachine constructor, possibly carried over from an error in the examples.

Certain directives, like .sideset 1 opt have to becommunicated from the assembler to the StateMachine. This is done by making the last argument to StateMachine's constructor be **pio_code.pio_kwargs.

As you can see in this session, this will make the StateMachine constructor receive the keyword arguments sideset_enable=True and sideset_pin_count=1.

Without this, some defaults (sideset_enable=False) are used. This causes the bit pattern in the instruction without an explicit side-set [0] or [1] to be interpreted like [0], setting the side pins to 0.

>>> import adafruit_pioasm
>>> p = adafruit_pioasm.Program(".side_set 1 opt")
>>> p.pio_kwargs
{'sideset_enable': True, 'sideset_pin_count': 1}

I'll make a pull request fixing the morse code example.