Skip to content

Instantly share code, notes, and snippets.

@glenpike
Last active December 1, 2023 02:34
Show Gist options
  • Save glenpike/971e45d1a586e3baaa45192ea74d1509 to your computer and use it in GitHub Desktop.
Save glenpike/971e45d1a586e3baaa45192ea74d1509 to your computer and use it in GitHub Desktop.
# Raspberry Pi Pico + YM2149 Soundchip test example
# Raspberry Pi Pico + YM2149 Soundchip test example
# Based on https://github.com/FlorentFlament/ym2149-test
# http://www.ym2149.com/ym2149.pdf
# Pins
# Pico -> YM2149 (DIL)
# GP5 -> CLOCK (22)
# GP6 -> BC1 (29)
# GP7 -> BDIR (27)
# GP8 to GP15 -> DA0 to DA7 (37 to 30)
# VBUS to RESET (23)
# VBUS to BC2 (28)
# VBUS to VCC (40)
from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
import utime
PIO_FREQ = 50000000 #gives us a 20ns cycle time which we can use for delay multipliers
ADDRESS_MODE = 3
WRITE_MODE = 2
READ_MODE = 1 #unused
INACTIVE_MODE = 0
# TODO: Can we combine these 2 functions - the only difference is the control pin values for the mode?
# 8-bit parallel data bus with 2 control pins
# sets the address mode on the bus control pins and writes the supplied data to 8 pins
# cycle time is 1 / PIO_FREQ
# each instruction takes 1 cycle and you can delay for [n] more cycles after each instruction
@asm_pio(sideset_init=PIO.OUT_LOW, out_init=(rp2.PIO.OUT_LOW,) * 8, out_shiftdir=PIO.SHIFT_RIGHT,
autopull=True, pull_thresh=16 )
def set_address():
pull() #pull data from the transmit SR
nop() .side(3) #set the mode to address on pins
out(pins, 8) [7] #output data to 8 pins and wait for 7 more cycles
nop() [6] #wait for another 7 cycles (1 instruction + 6) - 300ns total
nop() .side(0) [3] #set mode to inactive for 80ns (1 instruction + 3 cycles)
# 8-bit parallel data bus with 2 control pins
# sets the write mode on the bus control pins and behaves as above
@asm_pio(sideset_init=PIO.OUT_LOW, out_init=(rp2.PIO.OUT_LOW,) * 8, out_shiftdir=PIO.SHIFT_RIGHT,
autopull=True, pull_thresh=16 )
def set_data():
pull() #pull data from the transmit SR
nop() .side(2) #set the mode to write on pins
out(pins, 8) [7] #output data to 8 pins and wait for 7 more cycles
nop() [6] #wait for another 7 cycles (1 instruction + 6) - 300ns total
nop() .side(0) [3] #set mode to inactive for 80ns (1 instruction + 3 cycles)
address_sm = StateMachine(0, set_address, freq=PIO_FREQ, sideset_base=Pin(6), out_base=Pin(8))
data_sm = StateMachine(0, set_data, freq=PIO_FREQ, sideset_base=Pin(6), out_base=Pin(8))
address_sm.active(1) #Activate address program in first PIO
data_sm.active(2) #Activate address program in second PIO
def send_data(address, data):
address_sm.put(address)
data_sm.put(data)
# 2MHz clock frequency for the YM1249 (affects frequency multiplier for notes)
CLOCK_FREQ = 2000000
def setup_clock():
clock = machine.PWM(machine.Pin(5))
clock.freq(CLOCK_FREQ)
clock.duty_u16(32768)
# Simple C, D, E, F, G, A, B note sequence
note_freqs = [130.81, 146.83, 164.81, 174.61, 196.00, 220.00, 246.94]
# Convert this to values that YM2149 understands
def note_to_data_val(freq):
return int((CLOCK_FREQ / (16 * freq)))
notes = list(map(note_to_data_val, note_freqs))
setup_clock()
# Initialise registers to 0
for i in range(16):
send_data(i, 0)
send_data(7, 0xf8) #Mixer setup: Only output clear sound (no noise)
send_data(8, 0x0f) # Volume A - fixed, no envelope
send_data(9, 0x0f) # Volume B - fixed, no envelope
send_data(10, 0x0f) # Volume C - fixed, no envelope
while True:
for i in range(7):
send_data(0, notes[i] & 0xff)
send_data(1, notes[i] >> 8)
send_data(2, notes[i] >> 1 & 0xff)
send_data(3, notes[i] >> 9)
send_data(4, notes[i] >> 2 & 0xff)
send_data(5, notes[i] >> 10)
utime.sleep(0.5)
@glenpike
Copy link
Author

Testing CircuitPython's PIO

import array
import time
import random
import usb_midi
import adafruit_midi

# Raspberry Pi Pico + CircuitPython, testing 8bit bus

# http://www.ym2149.com/ym2149.pdf
# Pins
# Pico -> YM2149 (DIL)
# GP5  -> CLOCK (22)
# GP6  -> BC1 (29)
# GP7  -> BDIR (27)
# GP8 to GP15 -> DA0 to DA7 (37 to 30)
# VBUS to RESET (23)
# VBUS to BC2 (28)
# VBUS to VCC (40)

import board
import rp2pio
import adafruit_pioasm
import pwmio

PIO_FREQ = 5000000 #gives us a 20ns cycle time which we can use for delay multipliers

pio_test = """
.side_set 2
.wrap_target
    pull        side 3       ;set the mode to address on pins
    out pins, 8         [1]  ;output first byte (address) from FIFO to 8 pins and wait for 7 more cycles
    nop                 [3]  
    nop                 [3]  
    nop                 [1]  ;wait for another 7 cycles (1 instruction + 6) - 300ns total
    nop                 [3]  
    nop                 [3]
    nop         side 0  [3]  ;set mode to inactive for 80ns (1 instruction + 3 cycles)
    nop         side 2
    out pins, 8         [1]  ;output second byte (data) from FIFO to 8 pins and wait for 7 more cycles
    nop                 [3]  
    nop                 [3]  
    nop                 [1]  ;wait for another 7 cycles (1 instruction + 6) - 300ns total
    nop                 [3]  
    nop                 [3]
    nop         side 0  [3]  ;set mode to inactive for 80ns (1 instruction + 3 cycles)
.wrap

"""

assembled = adafruit_pioasm.assemble(pio_test)

ym2149_out = rp2pio.StateMachine(
    assembled,
    frequency=PIO_FREQ,
    first_out_pin=board.GP8,
    first_sideset_pin=board.GP6,
    sideset_pin_count=2,
    out_pin_count=8,
    out_shift_right=True,
    pull_threshold=16,
    push_threshold=8
)

# 2MHz clock on pin 5
CLOCK_FREQ = 2000000

def setup_clock():
    clock = pwmio.PWMOut(board.GP5, frequency=CLOCK_FREQ, duty_cycle=32768)

led = pwmio.PWMOut(board.LED, frequency=5000, duty_cycle=32768)

setup_clock()

def set_register(address, data):
    combined = array.array("B", [address, data])
    ym2149_out.write(combined)
    time.sleep(0.005)

while True:
    set_register(0x55, 0xaa)
    time.sleep(0.05)

Output on PiScope - doesn't seem to hold the bus values for the duration as it does above:
2022-03-16-140149_1280x768_scrot
O

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment