Last active
December 1, 2023 02:34
-
-
Save glenpike/971e45d1a586e3baaa45192ea74d1509 to your computer and use it in GitHub Desktop.
# Raspberry Pi Pico + YM2149 Soundchip test example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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) |
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:
O
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Outputting sound on one channel, but more than one seems to be problematic. Still dodgy wiring. This works - some of the time...