Compare commits
35 commits
not-workin
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c74b1472d1 | ||
| e57801a82e | |||
|
|
1aa7fad692 | ||
|
|
3fa2e069a2 | ||
| ca4463174d | |||
| 17f4b35e58 | |||
|
|
bd9c3e1cae | ||
|
|
15bb242b02 | ||
|
|
1b8203677f | ||
| 1c71dd765a | |||
| eebab0afa4 | |||
| 0e46434843 | |||
|
|
646926f066 | ||
|
|
baef470b95 | ||
|
|
ad1c9265be | ||
| b1c20d06ea | |||
| 41a8e0e9b6 | |||
| d41ccd1082 | |||
| 05d4c11f56 | |||
|
|
6965161fec | ||
|
|
34e184b5fe | ||
| 1b3eb17cee | |||
| 2593d665b0 | |||
|
|
873994107f | ||
|
|
e98a74b69c | ||
| bfc9f6f0f0 | |||
| 082f3175f0 | |||
| 7d815b8fd3 | |||
| 6ed6eae9bc | |||
| 43fee452a2 | |||
| 3edf26ed1d | |||
| c611126e85 | |||
| f02bb8e260 | |||
| 5e307d8167 | |||
| a566102759 |
12 changed files with 468 additions and 464 deletions
22
.github/workflows/build.yaml
vendored
22
.github/workflows/build.yaml
vendored
|
|
@ -36,17 +36,35 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./fetch-rom-dsk.sh
|
./fetch-rom-dsk.sh
|
||||||
./fruitjam-build.sh -m 4096
|
./fruitjam-build.sh -m 4096
|
||||||
|
./fruitjam-build.sh -m 4096 -v
|
||||||
|
./fruitjam-build.sh -m 4096 -o
|
||||||
|
./fruitjam-build.sh -m 4096 -v -o
|
||||||
|
./fruitjam-build.sh -m 400
|
||||||
./fruitjam-build.sh -m 400 -v
|
./fruitjam-build.sh -m 400 -v
|
||||||
|
./fruitjam-build.sh -m 400 -o
|
||||||
|
./fruitjam-build.sh -m 400 -v -o
|
||||||
|
mkdir uf2s
|
||||||
|
cp build*/*.uf2 uf2s/
|
||||||
|
mkdir elfs
|
||||||
|
cp build*/*.elf elfs/
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: uf2 files
|
name: uf2 files
|
||||||
path: build*/*.uf2
|
path: uf2s/*
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: elf files
|
name: elf files
|
||||||
path: build*/*.elf
|
path: elfs/*
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: build*/*.uf2
|
||||||
|
fail_on_unmatched_files: true
|
||||||
|
body: "Select a uf2 from the list below."
|
||||||
|
|
||||||
|
|
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
*.img
|
||||||
|
*.bin
|
||||||
|
os.7z
|
||||||
|
picotool
|
||||||
106
CMakeLists.txt
106
CMakeLists.txt
|
|
@ -55,6 +55,8 @@ set(HSTX_D0P 15 CACHE STRING "HSTX D0+ PIN")
|
||||||
set(HSTX_D1P 17 CACHE STRING "HSTX D1+ PIN")
|
set(HSTX_D1P 17 CACHE STRING "HSTX D1+ PIN")
|
||||||
set(HSTX_D2P 19 CACHE STRING "HSTX D2+ PIN")
|
set(HSTX_D2P 19 CACHE STRING "HSTX D2+ PIN")
|
||||||
|
|
||||||
|
option(OVERCLOCK "Overclock to 264MHz (known incompatible with psram)" OFF)
|
||||||
|
|
||||||
# Options for analog VGA output
|
# Options for analog VGA output
|
||||||
option(USE_VGA_RES "Video uses VGA (640x480) resolution" OFF)
|
option(USE_VGA_RES "Video uses VGA (640x480) resolution" OFF)
|
||||||
set(VIDEO_PIN 18 CACHE STRING "VGA Video GPIO base pin (followed by VS, CLK, HS)")
|
set(VIDEO_PIN 18 CACHE STRING "VGA Video GPIO base pin (followed by VS, CLK, HS)")
|
||||||
|
|
@ -71,12 +73,55 @@ set(PIN_AUDIO_PWM 41 CACHE STRING "Pin for PWM audio")
|
||||||
|
|
||||||
# See below, -DMEMSIZE=<size in KB> will configure umac's memory size,
|
# See below, -DMEMSIZE=<size in KB> will configure umac's memory size,
|
||||||
# overriding defaults.
|
# overriding defaults.
|
||||||
|
set(MEMSIZE 128 CACHE STRING "Memory size, in KB")
|
||||||
|
|
||||||
|
set(DISC_IMAGE ${CMAKE_CURRENT_SOURCE_DIR}/umac0ro.img CACHE FILEPATH "Built-in disk image")
|
||||||
|
|
||||||
|
if (USE_HSTX)
|
||||||
|
add_compile_definitions(USE_VGA_RES=1)
|
||||||
|
add_compile_definitions(HSTX_CKP=${HSTX_CKP} HSTX_D0P=${HSTX_D0P} HSTX_D1P=${HSTX_D1P} HSTX_D2P=${HSTX_D2P})
|
||||||
|
set(VIDEO_SRC src/video_hstx.c)
|
||||||
|
else()
|
||||||
|
add_compile_definitions(GPIO_VID_BASE=${VIDEO_PIN})
|
||||||
|
set(VIDEO_SRC src/video_vga.c)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (OVERCLOCK)
|
||||||
|
add_compile_definitions(OVERCLOCK=1)
|
||||||
|
set(OPT_OC "-oc")
|
||||||
|
else()
|
||||||
|
set(OPT_OC "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_VGA_RES)
|
||||||
|
add_compile_definitions(USE_VGA_RES=1)
|
||||||
|
add_compile_definitions(DISP_WIDTH=640)
|
||||||
|
add_compile_definitions(DISP_HEIGHT=480)
|
||||||
|
set(RES "640x480")
|
||||||
|
set(RESFLAG "-v")
|
||||||
|
else()
|
||||||
|
add_compile_definitions(DISP_WIDTH=512)
|
||||||
|
add_compile_definitions(DISP_HEIGHT=342)
|
||||||
|
set(RES "512x342")
|
||||||
|
set(RESFLAG "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_PSRAM)
|
||||||
|
add_compile_definitions(PIN_PSRAM_CS=${PSRAM_CS} USE_PSRAM=1)
|
||||||
|
set(OPT_PSRAM "-psram")
|
||||||
|
else()
|
||||||
|
add_compile_definitions(USE_PSRAM=0)
|
||||||
|
set(OPT_PSRAM "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(FIRMWARE "pico-mac-${PICO_BOARD}-${MEMSIZE}k-${RES}${OPT_PSRAM}${OPT_OC}")
|
||||||
|
|
||||||
|
|
||||||
# initialize the SDK based on PICO_SDK_PATH
|
# initialize the SDK based on PICO_SDK_PATH
|
||||||
# note: this must happen before project()
|
# note: this must happen before project()
|
||||||
include(pico_sdk_import.cmake)
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
project(firmware)
|
project(${FIRMWARE})
|
||||||
|
|
||||||
# initialize the Raspberry Pi Pico SDK
|
# initialize the Raspberry Pi Pico SDK
|
||||||
pico_sdk_init()
|
pico_sdk_init()
|
||||||
|
|
@ -103,9 +148,9 @@ set(UMAC_SOURCES
|
||||||
${UMAC_MUSASHI_PATH}/softfloat/softfloat.c
|
${UMAC_MUSASHI_PATH}/softfloat/softfloat.c
|
||||||
)
|
)
|
||||||
|
|
||||||
set(MEMSIZE 128 CACHE STRING "Memory size, in KB")
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -g3 -O3 -DPICO -DMUSASHI_CNF=\\\"../include/m68kconf.h\\\" -DUMAC_MEMSIZE=${MEMSIZE}")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -g3 -O3 -DPICO -DMUSASHI_CNF=\\\"../include/m68kconf.h\\\" -DUMAC_MEMSIZE=${MEMSIZE}")
|
||||||
|
|
||||||
|
|
||||||
if (USE_SD)
|
if (USE_SD)
|
||||||
add_compile_definitions(USE_SD=1)
|
add_compile_definitions(USE_SD=1)
|
||||||
set(FF_DISABLE_RTC ${PICO_RP2350}) # RP2350 doesn't have RTC, so disable it
|
set(FF_DISABLE_RTC ${PICO_RP2350}) # RP2350 doesn't have RTC, so disable it
|
||||||
|
|
@ -115,23 +160,6 @@ if (USE_SD)
|
||||||
add_compile_definitions(SD_TX=${SD_TX} SD_RX=${SD_RX} SD_SCK=${SD_SCK} SD_CS=${SD_CS} SD_MHZ=${SD_MHZ})
|
add_compile_definitions(SD_TX=${SD_TX} SD_RX=${SD_RX} SD_SCK=${SD_SCK} SD_CS=${SD_CS} SD_MHZ=${SD_MHZ})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_HSTX)
|
|
||||||
add_compile_definitions(USE_VGA_RES=1)
|
|
||||||
add_compile_definitions(HSTX_CKP=${HSTX_CKP} HSTX_D0P=${HSTX_D0P} HSTX_D1P=${HSTX_D1P} HSTX_D2P=${HSTX_D2P})
|
|
||||||
set(VIDEO_SRC src/video_hstx.c)
|
|
||||||
else()
|
|
||||||
add_compile_definitions(GPIO_VID_BASE=${VIDEO_PIN})
|
|
||||||
set(VIDEO_SRC src/video_vga.c)
|
|
||||||
endif()
|
|
||||||
if (USE_VGA_RES)
|
|
||||||
add_compile_definitions(USE_VGA_RES=1)
|
|
||||||
add_compile_definitions(DISP_WIDTH=640)
|
|
||||||
add_compile_definitions(DISP_HEIGHT=480)
|
|
||||||
else()
|
|
||||||
add_compile_definitions(DISP_WIDTH=512)
|
|
||||||
add_compile_definitions(DISP_HEIGHT=342)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_compile_definitions(PIN_USB_HOST_DP=${PIN_USB_HOST_DP})
|
add_compile_definitions(PIN_USB_HOST_DP=${PIN_USB_HOST_DP})
|
||||||
add_compile_definitions(PIN_USB_HOST_DM=${PIN_USB_HOST_DM})
|
add_compile_definitions(PIN_USB_HOST_DM=${PIN_USB_HOST_DM})
|
||||||
add_compile_definitions(PICO_DEFAULT_PIO_USB_DP_PIN=${PIN_USB_HOST_DP})
|
add_compile_definitions(PICO_DEFAULT_PIO_USB_DP_PIN=${PIN_USB_HOST_DP})
|
||||||
|
|
@ -147,26 +175,21 @@ if (NOT UART_RX STREQUAL "")
|
||||||
add_compile_definitions(PICO_DEFAULT_UART_RX_PIN=${UART_RX})
|
add_compile_definitions(PICO_DEFAULT_UART_RX_PIN=${UART_RX})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_PSRAM)
|
|
||||||
add_compile_definitions(PIN_PSRAM_CS=${PSRAM_CS} USE_PSRAM=1)
|
|
||||||
else()
|
|
||||||
add_compile_definitions(USE_PSRAM=0)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (USE_AUDIO)
|
if (USE_AUDIO)
|
||||||
add_subdirectory(external/pico-extras/src/rp2_common/pico_audio_i2s)
|
add_subdirectory(external/pico-extras/src/rp2_common/pico_audio_i2s)
|
||||||
add_subdirectory(external/pico-extras/src/common/pico_audio)
|
add_subdirectory(external/pico-extras/src/common/pico_audio)
|
||||||
add_subdirectory(external/pico-extras/src/common/pico_util_buffer)
|
add_subdirectory(external/pico-extras/src/common/pico_util_buffer)
|
||||||
add_compile_definitions(ENABLE_AUDIO=1 PICO_AUDIO_I2S_PIO=1 PICO_AUDIO_I2S_DMA_IRQ=0 PICO_AUDIO_I2S_DATA_PIN=24 PICO_AUDIO_I2S_CLOCK_PIN_BASE=25 PICO_AUDIO_I2S_MONO_INPUT=1 PICO_AUDIO_I2S_SWAP_CLOCK=1)
|
add_compile_definitions(ENABLE_AUDIO=1 PICO_AUDIO_I2S_PIO=1 PICO_AUDIO_I2S_DMA_IRQ=0 PICO_AUDIO_I2S_DATA_PIN=24 PICO_AUDIO_I2S_CLOCK_PIN_BASE=26 PICO_AUDIO_I2S_MONO_INPUT=1)
|
||||||
set(EXTRA_AUDIO_LIB pico_util_buffer pico_audio pico_audio_i2s hardware_i2c)
|
set(EXTRA_AUDIO_LIB pico_util_buffer pico_audio pico_audio_i2s hardware_i2c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (TARGET tinyusb_device)
|
if (TARGET tinyusb_device)
|
||||||
add_executable(firmware
|
add_executable(${FIRMWARE}
|
||||||
src/main.c
|
src/main.c
|
||||||
${VIDEO_SRC}
|
${VIDEO_SRC}
|
||||||
src/kbd.c
|
src/kbd.c
|
||||||
src/hid.c
|
src/hid.c
|
||||||
|
src/clocking.c
|
||||||
${EXTRA_SD_SRC}
|
${EXTRA_SD_SRC}
|
||||||
|
|
||||||
${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
|
${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
|
||||||
|
|
@ -178,6 +201,21 @@ if (TARGET tinyusb_device)
|
||||||
)
|
)
|
||||||
|
|
||||||
# The umac sources need to prepare Musashi (some sources are generated):
|
# The umac sources need to prepare Musashi (some sources are generated):
|
||||||
|
add_custom_command(OUTPUT incbin/umac-rom.h
|
||||||
|
COMMAND echo "*** Patching ROM ***"
|
||||||
|
COMMAND set -xe && mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/incbin && make -C ${UMAC_PATH} patcher && ${UMAC_PATH}/patcher ${RESFLAG} -m ${MEMSIZE} -r "${CMAKE_CURRENT_LIST_DIR}/rom.bin" -w ${CMAKE_CURRENT_BINARY_DIR}/incbin/umac-rom.h
|
||||||
|
)
|
||||||
|
add_custom_target(prepare_rom
|
||||||
|
DEPENDS incbin/umac-rom.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT incbin/umac-disc.h
|
||||||
|
COMMAND echo "DISC_IMAGE is ${DISC_IMAGE}" && mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/incbin && xxd -i < "${DISC_IMAGE}" > ${CMAKE_CURRENT_BINARY_DIR}/incbin/umac-disc.h
|
||||||
|
)
|
||||||
|
add_custom_target(prepare_disc
|
||||||
|
DEPENDS incbin/umac-disc.h
|
||||||
|
)
|
||||||
|
|
||||||
add_custom_command(OUTPUT ${UMAC_MUSASHI_PATH}/m68kops.c
|
add_custom_command(OUTPUT ${UMAC_MUSASHI_PATH}/m68kops.c
|
||||||
COMMAND echo "*** Preparing umac source ***"
|
COMMAND echo "*** Preparing umac source ***"
|
||||||
COMMAND make -C ${UMAC_PATH} prepare
|
COMMAND make -C ${UMAC_PATH} prepare
|
||||||
|
|
@ -185,9 +223,9 @@ if (TARGET tinyusb_device)
|
||||||
add_custom_target(prepare_umac
|
add_custom_target(prepare_umac
|
||||||
DEPENDS ${UMAC_MUSASHI_PATH}/m68kops.c
|
DEPENDS ${UMAC_MUSASHI_PATH}/m68kops.c
|
||||||
)
|
)
|
||||||
add_dependencies(firmware prepare_umac)
|
add_dependencies(${FIRMWARE} prepare_umac prepare_rom prepare_disc)
|
||||||
|
|
||||||
target_link_libraries(firmware
|
target_link_libraries(${FIRMWARE}
|
||||||
pico_stdlib
|
pico_stdlib
|
||||||
pico_multicore
|
pico_multicore
|
||||||
tinyusb_host
|
tinyusb_host
|
||||||
|
|
@ -199,24 +237,24 @@ if (TARGET tinyusb_device)
|
||||||
${EXTRA_AUDIO_LIB}
|
${EXTRA_AUDIO_LIB}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(firmware PRIVATE
|
target_include_directories(${FIRMWARE} PRIVATE
|
||||||
${CMAKE_CURRENT_LIST_DIR}/include
|
${CMAKE_CURRENT_LIST_DIR}/include
|
||||||
${PICO_TINYUSB_PATH}/hw
|
${PICO_TINYUSB_PATH}/hw
|
||||||
${PICO_TINYUSB_PATH}/src
|
${PICO_TINYUSB_PATH}/src
|
||||||
${UMAC_INCLUDE_PATHS}
|
${UMAC_INCLUDE_PATHS}
|
||||||
${PIOUSB_PATH}/src
|
${PIOUSB_PATH}/src
|
||||||
incbin
|
${CMAKE_CURRENT_BINARY_DIR}/incbin
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (NOT USE_HSTX)
|
if (NOT USE_HSTX)
|
||||||
pico_generate_pio_header(firmware ${CMAKE_CURRENT_LIST_DIR}/src/pio_video.pio)
|
pico_generate_pio_header(${FIRMWARE} ${CMAKE_CURRENT_LIST_DIR}/src/pio_video.pio)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
pico_enable_stdio_uart(firmware 1)
|
pico_enable_stdio_uart(${FIRMWARE} 1)
|
||||||
|
|
||||||
# Needed for UF2:
|
# Needed for UF2:
|
||||||
pico_add_extra_outputs(firmware)
|
pico_add_extra_outputs(${FIRMWARE})
|
||||||
|
|
||||||
elseif(PICO_ON_DEVICE)
|
elseif(PICO_ON_DEVICE)
|
||||||
message(WARNING "not building firmware because TinyUSB submodule is not initialized in the SDK")
|
message(WARNING "not building firmware because TinyUSB submodule is not initialized in the SDK")
|
||||||
|
|
|
||||||
301
README.md
301
README.md
|
|
@ -1,37 +1,45 @@
|
||||||
# Pico Micro Mac (pico-umac)
|
# Pico Micro Mac (pico-umac)
|
||||||
|
|
||||||
v0.21-fruitjam 22 March 2025
|
v0.21-fruitjam 28 March 2025
|
||||||
|
|
||||||
I (@jepler) have run roughshod across the code, breaking things willy-nilly and adding
|
I (@jepler) have run roughshod across the code, breaking things willy-nilly and adding
|
||||||
|
|
||||||
* 512x342 & 640x480 digital output on HSTX
|
* 512x342 & 640x480 digital output on HSTX
|
||||||
* PIO USB
|
* PIO USB
|
||||||
* PSRAM support
|
* PSRAM support
|
||||||
* Some Sound support
|
* Some Sound support on the onboard I2S DAC (speaker and headphones)
|
||||||
* To enable that, some VIA timer 2 support
|
|
||||||
|
|
||||||
The two main variants offered are the "400kB" mac with a 640x480 resolution & a
|
Several pre-compiled variants are offered:
|
||||||
4MB mac with 512x342 resolution (presented centered on a 640x480 display).
|
* 400kB or 4096kB (the latter uses PSRAM, and may perform slower overall but can run more software)
|
||||||
|
* 512x342 or 640x480 desktop resolution (512x342 is more compatible but has black screen margins)
|
||||||
For now, I2S is on pins A1 (data) A2 (LRCK) A3 (bit clock). With any luck it'll be moved to the on-board I2S soon.
|
* overclocked or not (overclocked may run faster but may be less reliable)
|
||||||
|
|
||||||
What works?
|
What works?
|
||||||
* System beep
|
* System beep
|
||||||
* A fair amount of hypercard, though not playing melodies with 'play "Boing" "a b c"'
|
|
||||||
* Hypercard 'play "Boing"' does play audio though (as does 'beep')
|
|
||||||
* Dark Castle including audio
|
* Dark Castle including audio
|
||||||
* After Dark screensavers including audio
|
* After Dark screensavers including audio
|
||||||
|
* Glider works, but without sound
|
||||||
|
|
||||||
What almost works
|
What doesn't work?
|
||||||
* Glider was working, but my sound changes made it boot with an error about missing coprocessor?? (appears linked to the timer2 implementation)
|
* Hypercard "play" and some hypercard screen transitions
|
||||||
|
|
||||||
There are artifacts that you can grab from the latest Actions build, at least until they expire.
|
Some of the software I tested with:
|
||||||
|
|
||||||
|
|
||||||
Some good Mac software:
|
|
||||||
* https://archive.org/details/HyperCardBootSystem7
|
* https://archive.org/details/HyperCardBootSystem7
|
||||||
* https://archive.org/details/mac_DarkCastle_1_2
|
* https://archive.org/details/mac\_DarkCastle\_1\_2
|
||||||
* https://archive.org/details/AfterDark2
|
* https://archive.org/details/AfterDark2
|
||||||
|
|
||||||
|
Plug mouse & keyboard into the USB ports of the fruit jam.
|
||||||
|
|
||||||
|
Put the software (a mac HFS volume with no additional headers or metadata) on a
|
||||||
|
SD card as "umac0w.img" (if you want to be able to write files) or
|
||||||
|
"umac0ro.img" (if you want the drive to be read only) and press the reset
|
||||||
|
button to start.
|
||||||
|
|
||||||
|
|
||||||
|
**Important note on overclocking:** The "oc" uf2 files overclock your RP2 chip to 264MHz. Simply including the `<Adafruit_dvhstx.h>` header enables this overclocking, separate from the option in the Arduino Tools menu.
|
||||||
|
Just like PC overclocking, there’s some risk of reduced component lifespan, though the extent (if any) can’t be precisely quantified and could vary from one chip to another.
|
||||||
|
Proceed at your own discretion.
|
||||||
|
|
||||||
v0.21 20 December 2024
|
v0.21 20 December 2024
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -72,98 +80,23 @@ couple of cheap components.
|
||||||
* git submodules
|
* git submodules
|
||||||
- Clone the repo with `--recursive`, or `git submodule update --init --recursive`
|
- Clone the repo with `--recursive`, or `git submodule update --init --recursive`
|
||||||
* Install/set up the [Pico/RP2040 SDK](https://github.com/raspberrypi/pico-sdk)
|
* Install/set up the [Pico/RP2040 SDK](https://github.com/raspberrypi/pico-sdk)
|
||||||
|
* Get a ROM & disc image with `sh fetch-rom-dsk.sh` (needs curl & 7z (debian package p7zip-full))
|
||||||
## Build umac
|
|
||||||
|
|
||||||
Install and build `umac` first. It'll give you a preview of the fun
|
|
||||||
to come, plus is required to generate a patched ROM image.
|
|
||||||
|
|
||||||
If you want to use a non-default memory size (i.e. >128K) you will
|
|
||||||
need to build `umac` with a matching `MEMSIZE` build parameter, for
|
|
||||||
example:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd external/umac
|
|
||||||
make MEMSIZE=208
|
|
||||||
```
|
|
||||||
|
|
||||||
This is because `umac` is used to patch the ROM, and when using
|
|
||||||
unsupported sizes between 128K and 512K the RAM size can't be probed
|
|
||||||
automatically, so the size needs to be embedded.
|
|
||||||
|
|
||||||
This is also the case for altering the video resolution, because the ROM
|
|
||||||
must be patched for this. Build `umac` with `DISP_WIDTH=640 DISP_HEIGHT=480`
|
|
||||||
when you intend to use the `USE_VGA_RES` option. For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd external/umac
|
|
||||||
make MEMSIZE=208 DISP_WIDTH=640 DISP_HEIGHT=480
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build pico-umac
|
## Build pico-umac
|
||||||
|
|
||||||
Do the initial Pico SDK `cmake` setup into an out-of-tree build dir,
|
Run the configure-and-build script:
|
||||||
providing config options if required.
|
|
||||||
|
|
||||||
From the top-level `pico-umac` directory:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
mkdir build
|
$ ./fruitjam-build.sh -h
|
||||||
(cd build ; PICO_SDK_PATH=/path/to/sdk cmake .. <options>)
|
Usage: ./fruitjam-build.sh [-v] [-m KiB] [-d diskimage]
|
||||||
|
|
||||||
|
-v: Use framebuffer resolution 640x480 instead of 512x342
|
||||||
|
-m: Set memory size in KiB (over 400kB requires psram)
|
||||||
|
-d: Specify disc image to include
|
||||||
|
-o: Overclock to 264MHz (known to be incompatible with psram)
|
||||||
|
|
||||||
|
PSRAM is automatically set depending on memory & framebuffer details
|
||||||
```
|
```
|
||||||
|
|
||||||
Options are required if you want SD support, more than the default 128K of memory,
|
|
||||||
higher resolution, to change pin configs, etc.:
|
|
||||||
|
|
||||||
* `-DUSE_SD=true`: Include SD card support. The GPIOs default to
|
|
||||||
`spi0` running at 5MHz, and GPIOs 2,3,4,5 for
|
|
||||||
`SCK`/`TX`/`RX`/`CS` respectively. These can be overridden for
|
|
||||||
your board/setup:
|
|
||||||
- `-DSD_TX=<gpio pin>`
|
|
||||||
- `-DSD_RX=<gpio pin>`
|
|
||||||
- `-DSD_SCK=<gpio pin>`
|
|
||||||
- `-DSD_CS=<gpio pin>`
|
|
||||||
- `-DSD_MHZ=<integer speed in MHz>`
|
|
||||||
* `-DMEMSIZE=<size in KB>`: The maximum practical size is about
|
|
||||||
208KB, but values between 128 and 208 should work on a RP2040.
|
|
||||||
Note that although apps and Mac OS seem to gracefully detect free
|
|
||||||
memory, these products never existed and some apps might behave
|
|
||||||
strangely.
|
|
||||||
- With the `Mac Plus` ROM, a _Mac 128K_ doesn't quite have
|
|
||||||
enough memory to run _MacPaint_. So, 192 or 208 (and a
|
|
||||||
writeable boot volume on SD) will allow _MacPaint_ to run.
|
|
||||||
- **NOTE**: When this option is used, the ROM image must be
|
|
||||||
built with an `umac` build with a corresponding `MEMSIZE`
|
|
||||||
* `-DUSE_VGA_RES=1`: Use 640x480 screen resolution instead of the
|
|
||||||
native 512x342. This uses an additional 16KB of RAM, so this
|
|
||||||
option makes a _Mac 128K_ configuration virtually unusable.
|
|
||||||
It is recommended only to use this when configuring >208K
|
|
||||||
using the option above.
|
|
||||||
* `-DVIDEO_PIN=<GPIO pin>`: Move the video output pins; defaults
|
|
||||||
to the pinout shown below.
|
|
||||||
|
|
||||||
Tip: `cmake` caches these variables, so if you see weird behaviour
|
|
||||||
having built previously and then changed an option, delete the `build`
|
|
||||||
directory and start again.
|
|
||||||
|
|
||||||
## ROM image
|
|
||||||
|
|
||||||
The flow is to use `umac` built on your workstation (e.g. Linux,
|
|
||||||
but WSL may work too) to prepare a patched ROM image.
|
|
||||||
|
|
||||||
`umac` is passed the 4D1F8172 MacPlusv3 ROM, and `-W` to write the
|
|
||||||
post-patching binary out:
|
|
||||||
|
|
||||||
```
|
|
||||||
./external/umac/main -r '4D1F8172 - MacPlus v3.ROM' -W rom.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Again, remember that if you are using the `-DMEMSIZE` option to
|
|
||||||
increase the `pico-umac` memory, or the `-DUSE_VGA_RES` option to
|
|
||||||
increase the `pico-umac` screen resolution, you will need to create
|
|
||||||
this ROM image with a `umac` built with the corresponding
|
|
||||||
`MEMSIZE`/`DISP_WIDTH`/`DISP_HEIGHT` options, as above.
|
|
||||||
|
|
||||||
## Disc image
|
## Disc image
|
||||||
|
|
||||||
If you don't build SD support, an internal read-only disc image is
|
If you don't build SD support, an internal read-only disc image is
|
||||||
|
|
@ -188,147 +121,15 @@ into _one_ of the following files in the root of the card:
|
||||||
|
|
||||||
* `umac0.img`: A normal read/write disc image
|
* `umac0.img`: A normal read/write disc image
|
||||||
* `umac0ro.img`: A read-only disc image
|
* `umac0ro.img`: A read-only disc image
|
||||||
|
|
||||||
## Putting it together, and building
|
|
||||||
|
|
||||||
Given the `rom.bin` prepared above and a `disc.bin` destinated for
|
|
||||||
flash, you can now generate includes from them and perform the build:
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdir incbin
|
|
||||||
xxd -i < rom.bin > incbin/umac-rom.h
|
|
||||||
|
|
||||||
# When using an internal disc image:
|
|
||||||
xxd -i < disc.bin > incbin/umac-disc.h
|
|
||||||
# OR, if using SD and if you do _not_ want an internal image:
|
|
||||||
echo > incbin/umac-disc.h
|
|
||||||
|
|
||||||
make -C build
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll get a `build/firmware.uf2` out the other end. Flash this to
|
|
||||||
your Pico: e.g. plug it in with button held/drag/drop. (When
|
|
||||||
iterating/testing during development, unplugging the OTG cable each
|
|
||||||
time is a pain – I ended up moving to SWD probe programming.)
|
|
||||||
|
|
||||||
The LED should flash at about 2Hz once powered up.
|
|
||||||
|
|
||||||
# Hardware contruction
|
|
||||||
|
|
||||||
It's a simple circuit in terms of having few components: just the
|
|
||||||
Pico, with three series resistors and a VGA connection, and DC power.
|
|
||||||
However, if you're not comfortable soldering then don't choose this as
|
|
||||||
your first project: I don't want you to zap your mouse, keyboard,
|
|
||||||
monitor, SD cards...
|
|
||||||
|
|
||||||
Disclaimer: This is a hardware project with zero warranty. All due
|
|
||||||
care has been taken in design/docs, but if you choose to build it then
|
|
||||||
I disclaim any responsibility for your hardware or personal safety.
|
|
||||||
|
|
||||||
With that out of the way...
|
|
||||||
|
|
||||||
## Theory of operation
|
|
||||||
|
|
||||||
Three 3.3V GPIO pins are driven by PIO to give VSYNC, HSYNC, and video
|
|
||||||
out signals.
|
|
||||||
|
|
||||||
The syncs are in many similar projects driven directly from GPIO, but
|
|
||||||
here I suggest a 66Ω series resistor on each in order to keep the
|
|
||||||
voltages at the VGA end (presumably into 75Ω termination?) in the
|
|
||||||
correct range.
|
|
||||||
|
|
||||||
For the video output, one GPIO drives R,G,B channels for mono/white
|
|
||||||
output. A 100Ω resistor gives roughly 0.7V (max intensity) into 3*75Ω
|
|
||||||
signals.
|
|
||||||
|
|
||||||
That's it... power in, USB adapter.
|
|
||||||
|
|
||||||
## Pinout and circuit
|
|
||||||
|
|
||||||
Parts needed:
|
|
||||||
|
|
||||||
* Pico/RP2040 board
|
|
||||||
* USB OTG micro-B to A adapter
|
|
||||||
* USB keyboard, mouse (and hub, if not integrated)
|
|
||||||
* 5V DC supply (600mA+), and maybe a DC jack
|
|
||||||
* 100Ω resistor
|
|
||||||
* 2x 66Ω resistors
|
|
||||||
* VGA DB15 connector, or janky chopped VGA cable
|
|
||||||
* (optional) SD card breakout, SD card
|
|
||||||
|
|
||||||
If you want to get fancy with an SD card, you will need some kind of
|
|
||||||
SD card SPI breakout adapter. (There are a lot of these around, but
|
|
||||||
many seem to have a buffer/level-converter for 5V operation. Find one
|
|
||||||
without, or modify your adapter for a 3.3V supply. Doing so, and
|
|
||||||
finding an SD card that works well with SPI is out of scope of this
|
|
||||||
doc.)
|
|
||||||
|
|
||||||
Pins are given for a RPi Pico board, but this will work on any RP2040
|
|
||||||
board with 2MB+ flash as long as all required GPIOs are pinned out:
|
|
||||||
|
|
||||||
| GPIO/pin | Pico pin | Usage |
|
|
||||||
| ------------ | ------------ | -------------- |
|
|
||||||
| GP0 | 1 | UART0 TX |
|
|
||||||
| GP1 | 2 | UART0 RX |
|
|
||||||
| GP18 | 24 | Video output % |
|
|
||||||
| GP19 | 25 | VSYNC |
|
|
||||||
| GP21 | 27 | HSYNC |
|
|
||||||
| Gnd | 23, 28 | Video ground |
|
|
||||||
| VBUS (5V) | 40 | +5V supply |
|
|
||||||
| Gnd | 38 | Supply ground |
|
|
||||||
|
|
||||||
%: The video pins default here, but can be moved by building with the
|
|
||||||
`-DVIDEO_PIN` option. This sets the position of the Video pin,
|
|
||||||
which is immediately followed by VSYNC, then a gap, then HSYNC.
|
|
||||||
For example, `-DVIDEO_PIN=20` configures the Video pin at 20,
|
|
||||||
VSYNC at 21, HSYNC at 23.
|
|
||||||
|
|
||||||
Method:
|
|
||||||
|
|
||||||
* Wire 5V supply to VBUS/Gnd
|
|
||||||
* Video output --> 100Ω --> VGA RGB (pins 1,2,3) all connected together
|
|
||||||
* HSYNC --> 66Ω --> VGA pin 13
|
|
||||||
* VSYNC --> 66Ω --> VGA pin 14
|
|
||||||
* Video ground --> VGA grounds (pins 5-8, 10)
|
|
||||||
|
|
||||||
If you don't have exactly 100Ω, using slightly more is OK but display
|
|
||||||
will be dimmer. If you don't have 66Ω for the syncs, connecting them
|
|
||||||
directly is "probably OK", but YMMV.
|
|
||||||
|
|
||||||
If you are including an SD card, the default pinout is as follows
|
|
||||||
(this can be changed at build time, above):
|
|
||||||
|
|
||||||
| GPIO/pin | Pico pin | Usage |
|
|
||||||
| ------------ | ------------ | -------------- |
|
|
||||||
| GP2 | 4 | SPI0 SCK |
|
|
||||||
| GP3 | 5 | SPI0 TX (MOSI) |
|
|
||||||
| GP4 | 6 | SPI0 RX (MISO) |
|
|
||||||
| GP5 | 7 | SPI0 /CS |
|
|
||||||
|
|
||||||
(The SD card needs a good ground, e.g. Pico pin 8 nearby, and 3.3V
|
|
||||||
supply from Pico pin 36.)
|
|
||||||
|
|
||||||
If your SD breakout board is "raw", i.e. has no buffer or series
|
|
||||||
resistors on-board, you may find adding a 66Ω resistor in series on
|
|
||||||
all of the four signal lines will help. Supply decoupling caps will
|
|
||||||
also be important (e.g. 1uF+0.1uF) to keep the SD card happy. _Keep
|
|
||||||
SD card wiring short._ The default SPI clock (5MHz) is
|
|
||||||
conservative/slow, but I suggest verifying the circuit/SD card works
|
|
||||||
before increasing it.
|
|
||||||
|
|
||||||
Test your connections: the key part is not getting over 0.7V into your
|
|
||||||
VGA connector's signals, or shorting SD card pins.
|
|
||||||
|
|
||||||
Connect USB mouse, and keyboard if you like, and power up.
|
|
||||||
|
|
||||||
# Software
|
# Software
|
||||||
|
|
||||||
Both CPU cores are used, and are overclocked (blush) to 250MHz so that
|
Both CPU cores are used, and are optionally overclocked (blush) to 264MHz so that
|
||||||
Missile Command is enjoyable to play.
|
Missile Command is enjoyable to play.
|
||||||
|
|
||||||
The `umac` emulator and video output runs on core 1, and core 0 deals
|
The `umac` emulator and video output runs on core 1, and core 0 deals
|
||||||
with USB HID input. Video DMA is initialised pointing to the
|
with USB HID input. Video DMA is initialised pointing to the
|
||||||
framebuffer in the Mac's RAM.
|
framebuffer in the Mac's RAM, or to a mirrored region in SRAM depending
|
||||||
|
on the configuration.
|
||||||
|
|
||||||
Other than that, it's just a main loop in `main.c` shuffling things
|
Other than that, it's just a main loop in `main.c` shuffling things
|
||||||
into `umac`.
|
into `umac`.
|
||||||
|
|
@ -345,32 +146,6 @@ VT220 emulator!).
|
||||||
The USB HID code is largely stolen from the TinyUSB example, but shows
|
The USB HID code is largely stolen from the TinyUSB example, but shows
|
||||||
how in practice you might capture keypresses/deal with mouse events.
|
how in practice you might capture keypresses/deal with mouse events.
|
||||||
|
|
||||||
## Video
|
|
||||||
|
|
||||||
The video system is pretty good and IMHO worth stealing for other
|
|
||||||
projects: It uses one PIO state machine and 3 DMA channels to provide
|
|
||||||
a rock-solid bitmapped 1BPP 640x480 video output. The Mac 512x342
|
|
||||||
framebuffer is centred inside this by using horizontal blanking
|
|
||||||
regions (programmed into the line scan-out) and vertical blanking
|
|
||||||
areas from a dummy "always black" mini-framebuffer.
|
|
||||||
|
|
||||||
It supports (at build time) flexible resolutions/timings. The one
|
|
||||||
caveat (or advantage?) is that it uses an HSYNC IRQ routine to
|
|
||||||
recalculate the next DMA buffer pointer; doing this at scan-time costs
|
|
||||||
about 1% of the CPU time (on core 1). However, it could be used to
|
|
||||||
generate video on-the-fly from characters/tiles without a true
|
|
||||||
framebuffer.
|
|
||||||
|
|
||||||
I'm considering improvements to the video system:
|
|
||||||
|
|
||||||
* Supporting multiple BPP/colour output
|
|
||||||
* Implement the rest of `DE`/display valid strobe support, making
|
|
||||||
driving LCDs possible.
|
|
||||||
* Using a video DMA address list and another DMA channel to reduce
|
|
||||||
the IRQ frequency (CPU overhead) to per-frame, at the cost of a
|
|
||||||
couple of KB of RAM.
|
|
||||||
|
|
||||||
|
|
||||||
# Licence
|
# Licence
|
||||||
|
|
||||||
`hid.c` and `tusb_config.h` are based on code from the TinyUSB
|
`hid.c` and `tusb_config.h` are based on code from the TinyUSB
|
||||||
|
|
|
||||||
2
external/pico-extras
vendored
2
external/pico-extras
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit c73f2f3a2b1ef65dc50113a22aadc5a262a92e0f
|
Subproject commit 601b1bfdb1089068f8515a623622fcb38648417a
|
||||||
2
external/umac
vendored
2
external/umac
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit ce55830a1babd681d0e9a639c9b23f10cd9e3c96
|
Subproject commit c265fb4e1eaaaed15776ac97f5b16a56cf5c454d
|
||||||
|
|
@ -1,21 +1,12 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
mkdir -p incbin
|
set -xe
|
||||||
|
|
||||||
if ! [ -f rom.bin ]; then
|
if ! [ -f rom.bin ]; then
|
||||||
if ! [ -f '4D1F8172 - MacPlus v3.ROM' ]; then
|
curl -L 'https://archive.org/download/mac_rom_archive_-_as_of_8-19-2011/mac_rom_archive_-_as_of_8-19-2011.zip/4D1F8172%20-%20MacPlus%20v3.ROM' > rom.bin
|
||||||
curl -L 'https://ia902205.us.archive.org/view_archive.php?archive=/18/items/mac_rom_archive_-_as_of_8-19-2011/mac_rom_archive_-_as_of_8-19-2011.zip&file=4D1F8172%20-%20MacPlus%20v3.ROM' > '4D1F8172 - MacPlus v3.ROM'
|
|
||||||
fi
|
|
||||||
make -C external/umac clean
|
|
||||||
make -C external/umac DISP_WIDTH=512 DISP_HEIGHT=342
|
|
||||||
./external/umac/main -r '4D1F8172 - MacPlus v3.ROM' -W rom.bin
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
xxd -i < rom.bin > incbin/umac-rom.h
|
|
||||||
|
|
||||||
if ! [ -f umac0ro.img ]; then
|
if ! [ -f umac0ro.img ]; then
|
||||||
curl -L 'https://archive.org/download/apple-mac-os-system-3.2-finder-5.3-system-tools-1.0-512-ke-jun-1986-3.5-800k.-7z/Apple%20Mac%20OS%20%28System%203.2%20Finder%205.3%29%20%28System%20Tools%201.0%20Mac%20128%2C%20512K%29%20%28Jun%201986%29%20%283.5-400k%29.7z' > 'Apple Mac OS (System 3.2 Finder 5.3) (System Tools 1.1 Mac Plus) (Jun 1986) (3.5-800k).7z'
|
curl -L 'https://archive.org/download/mac_MacOS_6.0.8/MacOS_6.0.8_System_Startup.img' > 'umac0ro.img'
|
||||||
7z x -so 'Apple Mac OS (System 3.2 Finder 5.3) (System Tools 1.1 Mac Plus) (Jun 1986) (3.5-800k).7z' 'Apple Mac OS (System 3.2 Finder 5.3) (System Tools 1.0 Mac 128, 512K) (Jun 1986) (3.5-400k)/System Installation.img' > umac0ro.img
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
xxd -i < umac0ro.img > incbin/umac-disc.h
|
sha256sum -c roms.sha256sum
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,15 @@ set -e
|
||||||
DISP_WIDTH=512
|
DISP_WIDTH=512
|
||||||
DISP_HEIGHT=342
|
DISP_HEIGHT=342
|
||||||
MEMSIZE=400
|
MEMSIZE=400
|
||||||
DISK_IMAGE=""
|
DISC_IMAGE=
|
||||||
CMAKE_ARGS=""
|
CMAKE_ARGS=""
|
||||||
|
OVERCLOCK=0
|
||||||
|
|
||||||
while getopts "hvd:m:" o; do
|
while getopts "hovd:m:" o; do
|
||||||
case "$o" in
|
case "$o" in
|
||||||
|
(o)
|
||||||
|
OVERCLOCK=1
|
||||||
|
;;
|
||||||
(v)
|
(v)
|
||||||
DISP_WIDTH=640
|
DISP_WIDTH=640
|
||||||
DISP_HEIGHT=480
|
DISP_HEIGHT=480
|
||||||
|
|
@ -24,14 +28,15 @@ while getopts "hvd:m:" o; do
|
||||||
MEMSIZE=$OPTARG
|
MEMSIZE=$OPTARG
|
||||||
;;
|
;;
|
||||||
(d)
|
(d)
|
||||||
DISK_IMAGE=$OPTARG
|
DISC_IMAGE=$OPTARG
|
||||||
;;
|
;;
|
||||||
(h|?)
|
(h|?)
|
||||||
echo "Usage: $0 [-v] [-m KiB] [-d diskimage]"
|
echo "Usage: $0 [-v] [-m KiB] [-d diskimage]"
|
||||||
echo ""
|
echo ""
|
||||||
echo " -v: Use framebuffer resolution 640x480 instead of 512x342"
|
echo " -v: Use framebuffer resolution 640x480 instead of 512x342"
|
||||||
echo " -m: Set memory size in KiB"
|
echo " -m: Set memory size in KiB (over 400kB requires psram)"
|
||||||
echo " -d: Specify disk image to include"
|
echo " -d: Specify disc image to include"
|
||||||
|
echo " -o: Overclock to 264MHz (known to be incompatible with psram)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "PSRAM is automatically set depending on memory & framebuffer details"
|
echo "PSRAM is automatically set depending on memory & framebuffer details"
|
||||||
exit
|
exit
|
||||||
|
|
@ -44,6 +49,9 @@ shift $((OPTIND-1))
|
||||||
TAG=fruitjam_${DISP_WIDTH}x${DISP_HEIGHT}_${MEMSIZE}k
|
TAG=fruitjam_${DISP_WIDTH}x${DISP_HEIGHT}_${MEMSIZE}k
|
||||||
PSRAM=$((MEMSIZE > 400))
|
PSRAM=$((MEMSIZE > 400))
|
||||||
if [ $PSRAM -ne 0 ] ; then
|
if [ $PSRAM -ne 0 ] ; then
|
||||||
|
if [ $OVERCLOCK -ne 0 ]; then
|
||||||
|
echo "*** Overclock + PSRAM is known not to work. You have been warned."
|
||||||
|
fi
|
||||||
TAG=${TAG}_psram
|
TAG=${TAG}_psram
|
||||||
CMAKE_ARGS="$CMAKE_ARGS -DUSE_PSRAM=1"
|
CMAKE_ARGS="$CMAKE_ARGS -DUSE_PSRAM=1"
|
||||||
fi
|
fi
|
||||||
|
|
@ -54,22 +62,18 @@ if [ "$MIRROR_FRAMEBUFFER" -eq 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Append disk name to build directory if disk image is specified
|
# Append disk name to build directory if disk image is specified
|
||||||
if [ -n "$DISK_IMAGE" ] && [ -f "$DISK_IMAGE" ]; then
|
if [ -n "$DISC_IMAGE" ] && [ -f "$DISC_IMAGE" ]; then
|
||||||
# Extract filename without extension
|
# Extract filename without extension
|
||||||
DISK_NAME=$(basename "$DISK_IMAGE" | sed 's/\.[^.]*$//')
|
CMAKE_ARGS="$CMAKE_ARGS -DDISC_IMAGE=${DISC_IMAGE}"
|
||||||
TAG=${TAG}_${DISK_NAME}
|
DISC_IMAGE=$(basename "$DISC_IMAGE" | sed 's/\.[^.]*$//')
|
||||||
|
TAG=${TAG}_${DISC_IMAGE}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $OVERCLOCK -ne 0 ]; then
|
||||||
|
TAG=${TAG}_overclock
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set -x
|
set -x
|
||||||
make -C external/umac clean
|
|
||||||
make -C external/umac DISP_WIDTH=${DISP_WIDTH} DISP_HEIGHT=${DISP_HEIGHT} MEMSIZE=${MEMSIZE}
|
|
||||||
rm -f rom.bin
|
|
||||||
./external/umac/main -r '4D1F8172 - MacPlus v3.ROM' -W rom.bin || true
|
|
||||||
[ -f rom.bin ]
|
|
||||||
xxd -i < rom.bin > incbin/umac-rom.h
|
|
||||||
if [ -n "$DISK_IMAGE" ] && [ -f "$DISK_IMAGE" ]; then
|
|
||||||
xxd -i < "$DISK_IMAGE" > incbin/umac-disc.h
|
|
||||||
fi
|
|
||||||
rm -rf build_${TAG}
|
rm -rf build_${TAG}
|
||||||
cmake -S . -B build_${TAG} \
|
cmake -S . -B build_${TAG} \
|
||||||
-DPICO_SDK_PATH=../pico-sdk \
|
-DPICO_SDK_PATH=../pico-sdk \
|
||||||
|
|
@ -80,5 +84,7 @@ cmake -S . -B build_${TAG} \
|
||||||
-DSD_TX=35 -DSD_RX=36 -DSD_SCK=34 -DSD_CS=39 -DUSE_SD=1 \
|
-DSD_TX=35 -DSD_RX=36 -DSD_SCK=34 -DSD_CS=39 -DUSE_SD=1 \
|
||||||
-DUART_TX=44 -DUART_RX=45 -DUART=0 \
|
-DUART_TX=44 -DUART_RX=45 -DUART=0 \
|
||||||
-DBOARD_FILE=boards/adafruit_fruit_jam.c \
|
-DBOARD_FILE=boards/adafruit_fruit_jam.c \
|
||||||
|
-DSD_MHZ=16 \
|
||||||
|
-DOVERCLOCK=${OVERCLOCK} \
|
||||||
${CMAKE_ARGS} "$@"
|
${CMAKE_ARGS} "$@"
|
||||||
make -C build_${TAG} -j$(nproc)
|
make -C build_${TAG} -j$(nproc)
|
||||||
|
|
|
||||||
11
include/clocking.h
Normal file
11
include/clocking.h
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum clk_sys_speed {
|
||||||
|
CLK_SYS_264MHZ = 2,
|
||||||
|
CLK_SYS_176MHZ = 3,
|
||||||
|
CLK_SYS_132MHZ = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void overclock(enum clk_sys_speed clk_sys_div, uint32_t bit_clk_hz);
|
||||||
2
roms.sha256sum
Normal file
2
roms.sha256sum
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
dd908e2b65772a6b1f0c859c24e9a0d3dcde17b1c6a24f4abd8955846d7895e7 *rom.bin
|
||||||
|
464ff9b8a55b0a8fe10d394b411f9c753fcd9d477c0d2f1118ce9927b19bee54 *umac0ro.img
|
||||||
180
src/clocking.c
Normal file
180
src/clocking.c
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
#include "clocking.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "pico.h"
|
||||||
|
#include "pico/stdio.h"
|
||||||
|
#include "hardware/clocks.h"
|
||||||
|
#include "hardware/pll.h"
|
||||||
|
#include "hardware/structs/ioqspi.h"
|
||||||
|
#include "hardware/structs/qmi.h"
|
||||||
|
#include "hardware/sync.h"
|
||||||
|
#include "hardware/vreg.h"
|
||||||
|
|
||||||
|
static void __no_inline_not_in_flash_func(set_qmi_timing)() {
|
||||||
|
// Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
|
||||||
|
while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS)
|
||||||
|
;
|
||||||
|
|
||||||
|
qmi_hw->m[0].timing = 0x40000202;
|
||||||
|
//qmi_hw->m[0].timing = 0x40000101;
|
||||||
|
// Force a read through XIP to ensure the timing is applied
|
||||||
|
volatile uint32_t* ptr = (volatile uint32_t*)0x14000000;
|
||||||
|
(void) *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef RP2350_PSRAM_MAX_SELECT_FS64
|
||||||
|
#define RP2350_PSRAM_MAX_SELECT_FS64 (125000000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RP2350_PSRAM_MIN_DESELECT_FS
|
||||||
|
#define RP2350_PSRAM_MIN_DESELECT_FS (50000000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RP2350_PSRAM_RX_DELAY_FS
|
||||||
|
#define RP2350_PSRAM_RX_DELAY_FS (3333333)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RP2350_PSRAM_MAX_SCK_HZ
|
||||||
|
#define RP2350_PSRAM_MAX_SCK_HZ (133000000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SEC_TO_FS 1000000000000000ll
|
||||||
|
|
||||||
|
static void __no_inline_not_in_flash_func(set_psram_timing)(void) {
|
||||||
|
// Get secs / cycle for the system clock - get before disabling interrupts.
|
||||||
|
uint32_t sysHz = (uint32_t)clock_get_hz(clk_sys);
|
||||||
|
|
||||||
|
// Calculate the clock divider - goal to get clock used for PSRAM <= what
|
||||||
|
// the PSRAM IC can handle - which is defined in RP2350_PSRAM_MAX_SCK_HZ
|
||||||
|
volatile uint8_t clockDivider = (sysHz + RP2350_PSRAM_MAX_SCK_HZ - 1) / RP2350_PSRAM_MAX_SCK_HZ;
|
||||||
|
|
||||||
|
uint32_t intr_stash = save_and_disable_interrupts();
|
||||||
|
|
||||||
|
// Get the clock femto seconds per cycle.
|
||||||
|
|
||||||
|
uint32_t fsPerCycle = SEC_TO_FS / sysHz;
|
||||||
|
uint32_t fsPerHalfCycle = fsPerCycle / 2;
|
||||||
|
|
||||||
|
// the maxSelect value is defined in units of 64 clock cycles
|
||||||
|
// So maxFS / (64 * fsPerCycle) = maxSelect = RP2350_PSRAM_MAX_SELECT_FS64/fsPerCycle
|
||||||
|
volatile uint8_t maxSelect = RP2350_PSRAM_MAX_SELECT_FS64 / fsPerCycle;
|
||||||
|
|
||||||
|
// minDeselect time - in system clock cycle
|
||||||
|
// Must be higher than 50ns (min deselect time for PSRAM) so add a fsPerCycle - 1 to round up
|
||||||
|
// So minFS/fsPerCycle = minDeselect = RP2350_PSRAM_MIN_DESELECT_FS/fsPerCycle
|
||||||
|
|
||||||
|
volatile uint8_t minDeselect = (RP2350_PSRAM_MIN_DESELECT_FS + fsPerCycle - 1) / fsPerCycle;
|
||||||
|
|
||||||
|
// RX delay (RP2350 datasheet 12.14.3.1) delay between between rising edge of SCK and
|
||||||
|
// the start of RX sampling. Expressed in 0.5 system clock cycles. Smallest value
|
||||||
|
// >= 3.3ns.
|
||||||
|
volatile uint8_t rxDelay = (RP2350_PSRAM_RX_DELAY_FS + fsPerHalfCycle - 1) / fsPerHalfCycle;
|
||||||
|
|
||||||
|
printf("syshz=%u\n", sysHz);
|
||||||
|
printf("Max Select: %d, Min Deselect: %d, RX delay: %d, clock divider: %d\n", maxSelect, minDeselect, rxDelay, clockDivider);
|
||||||
|
printf("PSRAM clock rate %.1fMHz\n", (float)sysHz / clockDivider / 1e6);
|
||||||
|
|
||||||
|
qmi_hw->m[1].timing = QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB | // Break between pages.
|
||||||
|
3 << QMI_M1_TIMING_SELECT_HOLD_LSB | // Delay releasing CS for 3 extra system cycles.
|
||||||
|
rxDelay << QMI_M1_TIMING_RXDELAY_LSB | // Delay between SCK and RX sampling
|
||||||
|
1 << QMI_M1_TIMING_COOLDOWN_LSB | 1 << QMI_M1_TIMING_RXDELAY_LSB |
|
||||||
|
maxSelect << QMI_M1_TIMING_MAX_SELECT_LSB | minDeselect << QMI_M1_TIMING_MIN_DESELECT_LSB |
|
||||||
|
clockDivider << QMI_M1_TIMING_CLKDIV_LSB;
|
||||||
|
|
||||||
|
restore_interrupts(intr_stash);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void __no_inline_not_in_flash_func(clock_init)(int sys_clk_div) {
|
||||||
|
uint32_t intr_stash = save_and_disable_interrupts();
|
||||||
|
|
||||||
|
// Before messing with clock speeds ensure QSPI clock is nice and slow
|
||||||
|
hw_write_masked(&qmi_hw->m[0].timing, 6, QMI_M0_TIMING_CLKDIV_BITS);
|
||||||
|
|
||||||
|
// We're going to go fast, boost the voltage a little
|
||||||
|
vreg_set_voltage(VREG_VOLTAGE_1_15);
|
||||||
|
|
||||||
|
// Force a read through XIP to ensure the timing is applied before raising the clock rate
|
||||||
|
volatile uint32_t* ptr = (volatile uint32_t*)0x14000000;
|
||||||
|
(void) *ptr;
|
||||||
|
|
||||||
|
// Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
|
||||||
|
hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS);
|
||||||
|
while (clocks_hw->clk[clk_sys].selected != 0x1)
|
||||||
|
tight_loop_contents();
|
||||||
|
hw_write_masked(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, CLOCKS_CLK_REF_CTRL_SRC_BITS);
|
||||||
|
while (clocks_hw->clk[clk_ref].selected != 0x4)
|
||||||
|
tight_loop_contents();
|
||||||
|
|
||||||
|
// Stop the other clocks so we don't worry about overspeed
|
||||||
|
clock_stop(clk_usb);
|
||||||
|
clock_stop(clk_adc);
|
||||||
|
clock_stop(clk_peri);
|
||||||
|
clock_stop(clk_hstx);
|
||||||
|
|
||||||
|
// Set USB PLL to 528MHz
|
||||||
|
pll_init(pll_usb, PLL_COMMON_REFDIV, 1584 * MHZ, 3, 1);
|
||||||
|
|
||||||
|
const uint32_t usb_pll_freq = 528 * MHZ;
|
||||||
|
|
||||||
|
// CLK SYS = PLL USB 528MHz / sys_clk_div = 264MHz, 176MHz, or 132MHz
|
||||||
|
clock_configure(clk_sys,
|
||||||
|
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
|
||||||
|
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
|
||||||
|
usb_pll_freq, usb_pll_freq / sys_clk_div);
|
||||||
|
|
||||||
|
// CLK PERI = PLL USB 528MHz / 4 = 132MHz
|
||||||
|
clock_configure(clk_peri,
|
||||||
|
0, // Only AUX mux on ADC
|
||||||
|
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
|
||||||
|
usb_pll_freq, usb_pll_freq / 4);
|
||||||
|
|
||||||
|
// CLK USB = PLL USB 528MHz / 11 = 48MHz
|
||||||
|
clock_configure(clk_usb,
|
||||||
|
0, // No GLMUX
|
||||||
|
CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
|
||||||
|
usb_pll_freq,
|
||||||
|
USB_CLK_KHZ * KHZ);
|
||||||
|
|
||||||
|
// CLK ADC = PLL USB 528MHz / 11 = 48MHz
|
||||||
|
clock_configure(clk_adc,
|
||||||
|
0, // No GLMUX
|
||||||
|
CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
|
||||||
|
usb_pll_freq,
|
||||||
|
USB_CLK_KHZ * KHZ);
|
||||||
|
|
||||||
|
// Now we are running fast set fast QSPI clock and read delay
|
||||||
|
set_qmi_timing();
|
||||||
|
|
||||||
|
restore_interrupts(intr_stash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void overclock(enum clk_sys_speed clk_sys_div, uint32_t bit_clk_khz) {
|
||||||
|
clock_init(clk_sys_div);
|
||||||
|
stdio_init_all();
|
||||||
|
set_psram_timing();
|
||||||
|
#define SHOW_CLK(i) printf("clk_get_hz(%s) -> %u\n", #i, clock_get_hz(i));
|
||||||
|
SHOW_CLK(clk_ref);
|
||||||
|
SHOW_CLK(clk_sys);
|
||||||
|
SHOW_CLK(clk_peri);
|
||||||
|
SHOW_CLK(clk_hstx);
|
||||||
|
SHOW_CLK(clk_usb);
|
||||||
|
SHOW_CLK(clk_adc);
|
||||||
|
|
||||||
|
|
||||||
|
const uint32_t dvi_clock_khz = bit_clk_khz >> 1;
|
||||||
|
printf("bit_clk_khz = %u dvi_clock_khz = %u\n", bit_clk_khz, dvi_clock_khz);
|
||||||
|
uint vco_freq, post_div1, post_div2;
|
||||||
|
if (!check_sys_clock_khz(dvi_clock_khz, &vco_freq, &post_div1, &post_div2))
|
||||||
|
panic("System clock of %u kHz cannot be exactly achieved", dvi_clock_khz);
|
||||||
|
const uint32_t freq = vco_freq / (post_div1 * post_div2);
|
||||||
|
|
||||||
|
// Set the sys PLL to the requested freq
|
||||||
|
pll_init(pll_sys, PLL_COMMON_REFDIV, vco_freq, post_div1, post_div2);
|
||||||
|
|
||||||
|
// CLK HSTX = Requested freq
|
||||||
|
clock_configure(clk_hstx,
|
||||||
|
0,
|
||||||
|
CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
|
||||||
|
freq, freq);
|
||||||
|
}
|
||||||
229
src/main.c
229
src/main.c
|
|
@ -45,6 +45,7 @@
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
|
||||||
#include "umac.h"
|
#include "umac.h"
|
||||||
|
#include "clocking.h"
|
||||||
|
|
||||||
#if USE_SD
|
#if USE_SD
|
||||||
#include "f_util.h"
|
#include "f_util.h"
|
||||||
|
|
@ -64,6 +65,8 @@
|
||||||
uint8_t *audio_base;
|
uint8_t *audio_base;
|
||||||
static void audio_setup();
|
static void audio_setup();
|
||||||
static bool audio_poll();
|
static bool audio_poll();
|
||||||
|
static void set_mute_state(bool new_state);
|
||||||
|
static absolute_time_t automute_time;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
@ -103,15 +106,11 @@ static void io_init()
|
||||||
|
|
||||||
static void poll_led_etc()
|
static void poll_led_etc()
|
||||||
{
|
{
|
||||||
static int led_on = 0;
|
|
||||||
static absolute_time_t last = 0;
|
static absolute_time_t last = 0;
|
||||||
absolute_time_t now = get_absolute_time();
|
absolute_time_t now = get_absolute_time();
|
||||||
|
|
||||||
if (absolute_time_diff_us(last, now) > 500*1000) {
|
if (absolute_time_diff_us(last, now) > 500*1000) {
|
||||||
last = now;
|
last = now;
|
||||||
|
|
||||||
//led_on ^= 1;
|
|
||||||
//gpio_put(GPIO_LED_PIN, led_on);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,6 +155,12 @@ static void poll_umac()
|
||||||
int64_t p_1hz = absolute_time_diff_us(last_1hz, now);
|
int64_t p_1hz = absolute_time_diff_us(last_1hz, now);
|
||||||
int64_t p_vsync = absolute_time_diff_us(last_vsync, now);
|
int64_t p_vsync = absolute_time_diff_us(last_vsync, now);
|
||||||
bool pending_vsync = p_vsync > 16667;
|
bool pending_vsync = p_vsync > 16667;
|
||||||
|
#if ENABLE_AUDIO
|
||||||
|
if (automute_time < now) {
|
||||||
|
automute_time = at_the_end_of_time;
|
||||||
|
set_mute_state(false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if ENABLE_AUDIO
|
#if ENABLE_AUDIO
|
||||||
pending_vsync |= audio_poll();
|
pending_vsync |= audio_poll();
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -204,10 +209,12 @@ static void poll_umac()
|
||||||
#if USE_SD
|
#if USE_SD
|
||||||
static int disc_do_read(void *ctx, uint8_t *data, unsigned int offset, unsigned int len)
|
static int disc_do_read(void *ctx, uint8_t *data, unsigned int offset, unsigned int len)
|
||||||
{
|
{
|
||||||
|
gpio_put(GPIO_LED_PIN, 1);
|
||||||
FIL *fp = (FIL *)ctx;
|
FIL *fp = (FIL *)ctx;
|
||||||
f_lseek(fp, offset);
|
f_lseek(fp, offset);
|
||||||
unsigned int did_read = 0;
|
unsigned int did_read = 0;
|
||||||
FRESULT fr = f_read(fp, data, len, &did_read);
|
FRESULT fr = f_read(fp, data, len, &did_read);
|
||||||
|
gpio_put(GPIO_LED_PIN, 0);
|
||||||
if (fr != FR_OK || len != did_read) {
|
if (fr != FR_OK || len != did_read) {
|
||||||
printf("disc: f_read returned %d, read %u (of %u)\n", fr, did_read, len);
|
printf("disc: f_read returned %d, read %u (of %u)\n", fr, did_read, len);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -217,10 +224,12 @@ static int disc_do_read(void *ctx, uint8_t *data, unsigned int offset, unsi
|
||||||
|
|
||||||
static int disc_do_write(void *ctx, uint8_t *data, unsigned int offset, unsigned int len)
|
static int disc_do_write(void *ctx, uint8_t *data, unsigned int offset, unsigned int len)
|
||||||
{
|
{
|
||||||
|
gpio_put(GPIO_LED_PIN, 1);
|
||||||
FIL *fp = (FIL *)ctx;
|
FIL *fp = (FIL *)ctx;
|
||||||
f_lseek(fp, offset);
|
f_lseek(fp, offset);
|
||||||
unsigned int did_write = 0;
|
unsigned int did_write = 0;
|
||||||
FRESULT fr = f_write(fp, data, len, &did_write);
|
FRESULT fr = f_write(fp, data, len, &did_write);
|
||||||
|
gpio_put(GPIO_LED_PIN, 0);
|
||||||
if (fr != FR_OK || len != did_write) {
|
if (fr != FR_OK || len != did_write) {
|
||||||
printf("disc: f_write returned %d, read %u (of %u)\n", fr, did_write, len);
|
printf("disc: f_write returned %d, read %u (of %u)\n", fr, did_write, len);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -394,14 +403,25 @@ static void __no_inline_not_in_flash_func(setup_psram)(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RESETEN, RESET and quad enable
|
// RESETEN, RESET and quad enable
|
||||||
for (uint8_t i = 0; i < 3; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
|
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
|
||||||
if (i == 0) {
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
// RESETEN
|
||||||
qmi_hw->direct_tx = 0x66;
|
qmi_hw->direct_tx = 0x66;
|
||||||
} else if (i == 1) {
|
break;
|
||||||
|
case 1:
|
||||||
|
// RESET
|
||||||
qmi_hw->direct_tx = 0x99;
|
qmi_hw->direct_tx = 0x99;
|
||||||
} else {
|
break;
|
||||||
|
case 2:
|
||||||
|
// Quad enable
|
||||||
qmi_hw->direct_tx = 0x35;
|
qmi_hw->direct_tx = 0x35;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// Toggle wrap boundary mode
|
||||||
|
qmi_hw->direct_tx = 0xc0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
|
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
|
||||||
}
|
}
|
||||||
|
|
@ -471,11 +491,39 @@ static void __no_inline_not_in_flash_func(setup_psram)(void) {
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// set_sys_clock_khz(250*1000, true);
|
|
||||||
|
|
||||||
setup_psram();
|
setup_psram();
|
||||||
|
#if OVERCLOCK
|
||||||
|
overclock(CLK_SYS_264MHZ, 252000);
|
||||||
|
#endif
|
||||||
stdio_init_all();
|
stdio_init_all();
|
||||||
|
|
||||||
|
printf("psram size %u\n", _psram_size);
|
||||||
|
|
||||||
|
#ifndef RAM_TEST
|
||||||
|
#define RAM_TEST (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if RAM_TEST
|
||||||
|
for(int pass=0; pass<10; pass++) {
|
||||||
|
uint8_t acc = 1;
|
||||||
|
for(int i=0; i < _psram_size; i++) {
|
||||||
|
umac_ram[i] = acc;
|
||||||
|
acc = acc * 13 + 21;
|
||||||
|
}
|
||||||
|
|
||||||
|
acc = 1;
|
||||||
|
for(int i=0; i < _psram_size; i++) {
|
||||||
|
uint8_t ri = umac_ram[i];
|
||||||
|
if(ri != acc) {
|
||||||
|
panic("[%08x] Stored %02x read %02x", i, acc, ri);
|
||||||
|
}
|
||||||
|
acc = acc * 13 + 21;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("ram test pass %d OK\n", pass);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
io_init();
|
io_init();
|
||||||
|
|
||||||
#if ENABLE_AUDIO
|
#if ENABLE_AUDIO
|
||||||
|
|
@ -525,7 +573,6 @@ void writeRegister(uint8_t reg, uint8_t value) {
|
||||||
printf("res=%d\n", res);
|
printf("res=%d\n", res);
|
||||||
panic("i2c_write_timeout failed: res=%d\n", res);
|
panic("i2c_write_timeout failed: res=%d\n", res);
|
||||||
}
|
}
|
||||||
printf("Write Reg: %d = 0x%x\n", reg, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t readRegister(uint8_t reg) {
|
uint8_t readRegister(uint8_t reg) {
|
||||||
|
|
@ -533,28 +580,23 @@ uint8_t readRegister(uint8_t reg) {
|
||||||
buf[0] = reg;
|
buf[0] = reg;
|
||||||
int res = i2c_write_timeout_us(i2c0, I2C_ADDR, buf, sizeof(buf), /* nostop */ true, 1000);
|
int res = i2c_write_timeout_us(i2c0, I2C_ADDR, buf, sizeof(buf), /* nostop */ true, 1000);
|
||||||
if (res != 1) {
|
if (res != 1) {
|
||||||
printf("res=%d\n", res);
|
|
||||||
panic("i2c_write_timeout failed: res=%d\n", res);
|
panic("i2c_write_timeout failed: res=%d\n", res);
|
||||||
}
|
}
|
||||||
res = i2c_read_timeout_us(i2c0, I2C_ADDR, buf, sizeof(buf), /* nostop */ false, 1000);
|
res = i2c_read_timeout_us(i2c0, I2C_ADDR, buf, sizeof(buf), /* nostop */ false, 1000);
|
||||||
if (res != 1) {
|
if (res != 1) {
|
||||||
printf("res=%d\n", res);
|
|
||||||
panic("i2c_read_timeout failed: res=%d\n", res);
|
panic("i2c_read_timeout failed: res=%d\n", res);
|
||||||
}
|
}
|
||||||
uint8_t value = buf[0];
|
uint8_t value = buf[0];
|
||||||
printf("Read Reg: %d = 0x%x\n", reg, value);
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void modifyRegister(uint8_t reg, uint8_t mask, uint8_t value) {
|
void modifyRegister(uint8_t reg, uint8_t mask, uint8_t value) {
|
||||||
uint8_t current = readRegister(reg);
|
uint8_t current = readRegister(reg);
|
||||||
printf("Modify Reg: %d = [Before: 0x%x] with mask 0x%x and value 0x%x\n", reg, current, mask, value);
|
|
||||||
uint8_t new_value = (current & ~mask) | (value & mask);
|
uint8_t new_value = (current & ~mask) | (value & mask);
|
||||||
writeRegister(reg, new_value);
|
writeRegister(reg, new_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPage(uint8_t page) {
|
void setPage(uint8_t page) {
|
||||||
printf("Set page %d\n", page);
|
|
||||||
writeRegister(0x00, page);
|
writeRegister(0x00, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -567,9 +609,9 @@ void Wire_begin() {
|
||||||
|
|
||||||
|
|
||||||
static void setup_i2s_dac() {
|
static void setup_i2s_dac() {
|
||||||
gpio_init(22);
|
gpio_init(22);
|
||||||
gpio_set_dir(22, true);
|
gpio_set_dir(22, true);
|
||||||
gpio_put(22, true); // allow i2s to come out of reset
|
gpio_put(22, true); // allow i2s to come out of reset
|
||||||
|
|
||||||
Wire_begin();
|
Wire_begin();
|
||||||
sleep_ms(1000);
|
sleep_ms(1000);
|
||||||
|
|
@ -612,9 +654,9 @@ gpio_put(22, true); // allow i2s to come out of reset
|
||||||
modifyRegister(0x05, 0x80, 0x80);
|
modifyRegister(0x05, 0x80, 0x80);
|
||||||
|
|
||||||
// Headset and GPIO Config
|
// Headset and GPIO Config
|
||||||
setPage(1);
|
setPage(1);
|
||||||
modifyRegister(0x2e, 0xFF, 0x0b);
|
modifyRegister(0x2e, 0xFF, 0x0b);
|
||||||
setPage(0);
|
setPage(0);
|
||||||
modifyRegister(0x43, 0x80, 0x80); // Headset Detect
|
modifyRegister(0x43, 0x80, 0x80); // Headset Detect
|
||||||
modifyRegister(0x30, 0x80, 0x80); // INT1 Control
|
modifyRegister(0x30, 0x80, 0x80); // INT1 Control
|
||||||
modifyRegister(0x33, 0x3C, 0x14); // GPIO1
|
modifyRegister(0x33, 0x3C, 0x14); // GPIO1
|
||||||
|
|
@ -631,113 +673,32 @@ setPage(0);
|
||||||
// DAC Volume Control
|
// DAC Volume Control
|
||||||
setPage(0);
|
setPage(0);
|
||||||
modifyRegister(0x40, 0x0C, 0x00);
|
modifyRegister(0x40, 0x0C, 0x00);
|
||||||
writeRegister(0x41, 0x28); // Left DAC Vol
|
writeRegister(0x41, 0x0); // Left DAC Vol, 0dB
|
||||||
writeRegister(0x42, 0x28); // Right DAC Vol
|
writeRegister(0x42, 0x0); // Right DAC Vol, 0dB
|
||||||
|
|
||||||
// ADC Setup
|
|
||||||
modifyRegister(0x51, 0x80, 0x80);
|
|
||||||
modifyRegister(0x52, 0x80, 0x00);
|
|
||||||
writeRegister(0x53, 0x68); // ADC Volume
|
|
||||||
|
|
||||||
// Headphone and Speaker Setup
|
// Headphone and Speaker Setup
|
||||||
setPage(1);
|
setPage(1);
|
||||||
modifyRegister(0x1F, 0xC0, 0xC0); // HP Driver
|
modifyRegister(0x1F, 0xC0, 0xC0); // HP Driver Powered
|
||||||
modifyRegister(0x28, 0x04, 0x04); // HP Left Gain
|
|
||||||
modifyRegister(0x29, 0x04, 0x04); // HP Right Gain
|
|
||||||
writeRegister(0x24, 0x0A); // Left Analog HP
|
|
||||||
writeRegister(0x25, 0x0A); // Right Analog HP
|
|
||||||
|
|
||||||
modifyRegister(0x28, 0x78, 0x40); // HP Left Gain
|
modifyRegister(0x28, 0x04, 0x04); // HP Left not muted
|
||||||
modifyRegister(0x29, 0x78, 0x40); // HP Right Gain
|
modifyRegister(0x29, 0x04, 0x04); // HP Right not muted
|
||||||
|
|
||||||
|
writeRegister(0x24, 50); // Left Analog HP, -26 dB
|
||||||
|
writeRegister(0x25, 50); // Right Analog HP, -26 dB
|
||||||
|
|
||||||
|
modifyRegister(0x28, 0x78, 0x00); // HP Left Gain, 0 db
|
||||||
|
modifyRegister(0x29, 0x78, 0x00); // HP Right Gain, 0 db
|
||||||
|
|
||||||
// Speaker Amp
|
// Speaker Amp
|
||||||
modifyRegister(0x20, 0x80, 0x80);
|
modifyRegister(0x20, 0x80, 0x80); // Amp enabled (0x80) disable with (0x00)
|
||||||
modifyRegister(0x2A, 0x04, 0x04);
|
modifyRegister(0x2A, 0x04, 0x04); // Not muted (0x04) mute with (0x00)
|
||||||
modifyRegister(0x2A, 0x18, 0x08);
|
modifyRegister(0x2A, 0x18, 0x08); // 0 dB gain
|
||||||
writeRegister(0x26, 0x0A);
|
writeRegister(0x26, 40); // amp gain, -20.1 dB
|
||||||
|
|
||||||
// Return to page 0
|
// Return to page 0
|
||||||
setPage(0);
|
setPage(0);
|
||||||
|
|
||||||
printf("Initialization complete!\n");
|
printf("Audio I2C Initialization complete!\n");
|
||||||
|
|
||||||
|
|
||||||
// Read all registers for verification
|
|
||||||
printf("Reading all registers for verification:\n");
|
|
||||||
|
|
||||||
setPage(0);
|
|
||||||
readRegister(0x00); // AIC31XX_PAGECTL
|
|
||||||
readRegister(0x01); // AIC31XX_RESET
|
|
||||||
readRegister(0x03); // AIC31XX_OT_FLAG
|
|
||||||
readRegister(0x04); // AIC31XX_CLKMUX
|
|
||||||
readRegister(0x05); // AIC31XX_PLLPR
|
|
||||||
readRegister(0x06); // AIC31XX_PLLJ
|
|
||||||
readRegister(0x07); // AIC31XX_PLLDMSB
|
|
||||||
readRegister(0x08); // AIC31XX_PLLDLSB
|
|
||||||
readRegister(0x0B); // AIC31XX_NDAC
|
|
||||||
readRegister(0x0C); // AIC31XX_MDAC
|
|
||||||
readRegister(0x0D); // AIC31XX_DOSRMSB
|
|
||||||
readRegister(0x0E); // AIC31XX_DOSRLSB
|
|
||||||
readRegister(0x10); // AIC31XX_MINI_DSP_INPOL
|
|
||||||
readRegister(0x12); // AIC31XX_NADC
|
|
||||||
readRegister(0x13); // AIC31XX_MADC
|
|
||||||
readRegister(0x14); // AIC31XX_AOSR
|
|
||||||
readRegister(0x19); // AIC31XX_CLKOUTMUX
|
|
||||||
readRegister(0x1A); // AIC31XX_CLKOUTMVAL
|
|
||||||
readRegister(0x1B); // AIC31XX_IFACE1
|
|
||||||
readRegister(0x1C); // AIC31XX_DATA_OFFSET
|
|
||||||
readRegister(0x1D); // AIC31XX_IFACE2
|
|
||||||
readRegister(0x1E); // AIC31XX_BCLKN
|
|
||||||
readRegister(0x1F); // AIC31XX_IFACESEC1
|
|
||||||
readRegister(0x20); // AIC31XX_IFACESEC2
|
|
||||||
readRegister(0x21); // AIC31XX_IFACESEC3
|
|
||||||
readRegister(0x22); // AIC31XX_I2C
|
|
||||||
readRegister(0x24); // AIC31XX_ADCFLAG
|
|
||||||
readRegister(0x25); // AIC31XX_DACFLAG1
|
|
||||||
readRegister(0x26); // AIC31XX_DACFLAG2
|
|
||||||
readRegister(0x27); // AIC31XX_OFFLAG
|
|
||||||
readRegister(0x2C); // AIC31XX_INTRDACFLAG
|
|
||||||
readRegister(0x2D); // AIC31XX_INTRADCFLAG
|
|
||||||
readRegister(0x2E); // AIC31XX_INTRDACFLAG2
|
|
||||||
readRegister(0x2F); // AIC31XX_INTRADCFLAG2
|
|
||||||
readRegister(0x30); // AIC31XX_INT1CTRL
|
|
||||||
readRegister(0x31); // AIC31XX_INT2CTRL
|
|
||||||
readRegister(0x33); // AIC31XX_GPIO1
|
|
||||||
readRegister(0x3C); // AIC31XX_DACPRB
|
|
||||||
readRegister(0x3D); // AIC31XX_ADCPRB
|
|
||||||
readRegister(0x3F); // AIC31XX_DACSETUP
|
|
||||||
readRegister(0x40); // AIC31XX_DACMUTE
|
|
||||||
readRegister(0x41); // AIC31XX_LDACVOL
|
|
||||||
readRegister(0x42); // AIC31XX_RDACVOL
|
|
||||||
readRegister(0x43); // AIC31XX_HSDETECT
|
|
||||||
readRegister(0x51); // AIC31XX_ADCSETUP
|
|
||||||
readRegister(0x52); // AIC31XX_ADCFGA
|
|
||||||
readRegister(0x53); // AIC31XX_ADCVOL
|
|
||||||
|
|
||||||
setPage(1);
|
|
||||||
readRegister(0x1F); // AIC31XX_HPDRIVER
|
|
||||||
readRegister(0x20); // AIC31XX_SPKAMP
|
|
||||||
readRegister(0x21); // AIC31XX_HPPOP
|
|
||||||
readRegister(0x22); // AIC31XX_SPPGARAMP
|
|
||||||
readRegister(0x23); // AIC31XX_DACMIXERROUTE
|
|
||||||
readRegister(0x24); // AIC31XX_LANALOGHPL
|
|
||||||
readRegister(0x25); // AIC31XX_RANALOGHPR
|
|
||||||
readRegister(0x26); // AIC31XX_LANALOGSPL
|
|
||||||
readRegister(0x27); // AIC31XX_RANALOGSPR
|
|
||||||
readRegister(0x28); // AIC31XX_HPLGAIN
|
|
||||||
readRegister(0x29); // AIC31XX_HPRGAIN
|
|
||||||
readRegister(0x2A); // AIC31XX_SPLGAIN
|
|
||||||
readRegister(0x2B); // AIC31XX_SPRGAIN
|
|
||||||
readRegister(0x2C); // AIC31XX_HPCONTROL
|
|
||||||
readRegister(0x2E); // AIC31XX_MICBIAS
|
|
||||||
readRegister(0x2F); // AIC31XX_MICPGA
|
|
||||||
readRegister(0x30); // AIC31XX_MICPGAPI
|
|
||||||
readRegister(0x31); // AIC31XX_MICPGAMI
|
|
||||||
readRegister(0x32); // AIC31XX_MICPGACM
|
|
||||||
|
|
||||||
setPage(3);
|
|
||||||
readRegister(0x10); // AIC31XX_TIMERDIVIDER
|
|
||||||
|
|
||||||
}
|
}
|
||||||
static int volscale;
|
static int volscale;
|
||||||
|
|
||||||
|
|
@ -746,9 +707,10 @@ static int volscale;
|
||||||
int16_t audio[SAMPLES_PER_BUFFER];
|
int16_t audio[SAMPLES_PER_BUFFER];
|
||||||
|
|
||||||
void umac_audio_trap() {
|
void umac_audio_trap() {
|
||||||
static int led_on;
|
set_mute_state(volscale != 0);
|
||||||
led_on ^= 1;
|
if(volscale) {
|
||||||
gpio_put(GPIO_LED_PIN, 1);
|
automute_time = make_timeout_time_ms(500);
|
||||||
|
}
|
||||||
int32_t offset = 128;
|
int32_t offset = 128;
|
||||||
uint16_t *audiodata = (uint16_t*)audio_base;
|
uint16_t *audiodata = (uint16_t*)audio_base;
|
||||||
int scale = volscale;
|
int scale = volscale;
|
||||||
|
|
@ -776,7 +738,6 @@ const struct audio_i2s_config config =
|
||||||
{
|
{
|
||||||
.data_pin = PICO_AUDIO_I2S_DATA_PIN,
|
.data_pin = PICO_AUDIO_I2S_DATA_PIN,
|
||||||
.clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE,
|
.clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE,
|
||||||
.clock_pin_swapped = true,
|
|
||||||
.pio_sm = 0,
|
.pio_sm = 0,
|
||||||
.dma_channel = 3
|
.dma_channel = 3
|
||||||
};
|
};
|
||||||
|
|
@ -787,6 +748,7 @@ static struct audio_buffer_format producer_format = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void audio_setup() {
|
static void audio_setup() {
|
||||||
|
setup_i2s_dac();
|
||||||
const struct audio_format *output_format = audio_i2s_setup(&audio_format, &config);
|
const struct audio_format *output_format = audio_i2s_setup(&audio_format, &config);
|
||||||
assert(output_format);
|
assert(output_format);
|
||||||
if (!output_format) {
|
if (!output_format) {
|
||||||
|
|
@ -802,14 +764,31 @@ static void audio_setup() {
|
||||||
static bool audio_poll() {
|
static bool audio_poll() {
|
||||||
audio_buffer_t *buffer = take_audio_buffer(producer_pool, false);
|
audio_buffer_t *buffer = take_audio_buffer(producer_pool, false);
|
||||||
if (!buffer) return false;
|
if (!buffer) return false;
|
||||||
gpio_put(GPIO_LED_PIN, 0);
|
|
||||||
memcpy(buffer->buffer->bytes, audio, sizeof(audio));
|
memcpy(buffer->buffer->bytes, audio, sizeof(audio));
|
||||||
buffer->sample_count = SAMPLES_PER_BUFFER;
|
buffer->sample_count = SAMPLES_PER_BUFFER;
|
||||||
give_audio_buffer(producer_pool, buffer);
|
give_audio_buffer(producer_pool, buffer);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool mute_state = false;
|
||||||
|
static void set_mute_state(bool new_state) {
|
||||||
|
if(mute_state == new_state) return;
|
||||||
|
mute_state = new_state;
|
||||||
|
|
||||||
|
setPage(1);
|
||||||
|
if(mute_state) {
|
||||||
|
modifyRegister(0x28, 0x04, 0x04); // HP Left not muted
|
||||||
|
modifyRegister(0x29, 0x04, 0x04); // HP Right not muted
|
||||||
|
modifyRegister(0x2A, 0x04, 0x04); // Speaker not muted
|
||||||
|
} else {
|
||||||
|
modifyRegister(0x28, 0x04, 0x0); // HP Left muted
|
||||||
|
modifyRegister(0x29, 0x04, 0x0); // HP Right muted
|
||||||
|
modifyRegister(0x2A, 0x04, 0x0); // Speaker muted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void umac_audio_cfg(int volume, int sndres) {
|
void umac_audio_cfg(int volume, int sndres) {
|
||||||
volscale = sndres ? 0 : 65536 * volume / 7;
|
volscale = sndres ? 0 : 65536 * volume / 7;
|
||||||
|
set_mute_state(volscale != 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue