From 2593d665b05d01a575cb1bd5d5008c7cf880dd15 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 Mar 2025 08:34:27 -0500 Subject: [PATCH 01/12] Update README --- README.md | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index aa94369..05a8277 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,44 @@ # 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 * 512x342 & 640x480 digital output on HSTX * PIO USB * PSRAM support - * Some Sound support - * To enable that, some VIA timer 2 support + * Some Sound support on the onboard I2S DAC (speaker and headphones) -The two main variants offered are the "400kB" mac with a 640x480 resolution & a -4MB mac with 512x342 resolution (presented centered on a 640x480 display). - -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. +Several pre-compiled variants are offered: + * 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) + * overclocked or not (overclocked may run faster but may be less reliable) What works? * 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 * After Dark screensavers including audio + * Glider works, but without sound -What almost works - * Glider was working, but my sound changes made it boot with an error about missing coprocessor?? (appears linked to the timer2 implementation) +What doesn't work? + * 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 good Mac software: +Some of the software I tested with: * 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 +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 `` 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 From 1b3eb17cee801739ee75ae101633802eb702233b Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 Mar 2025 08:45:25 -0500 Subject: [PATCH 02/12] Update README.md --- README.md | 292 ++++++------------------------------------------------ 1 file changed, 30 insertions(+), 262 deletions(-) diff --git a/README.md b/README.md index 05a8277..8b952e5 100644 --- a/README.md +++ b/README.md @@ -3,29 +3,30 @@ v0.21-fruitjam 28 March 2025 I (@jepler) have run roughshod across the code, breaking things willy-nilly and adding - * 512x342 & 640x480 digital output on HSTX - * PIO USB - * PSRAM support - * Some Sound support on the onboard I2S DAC (speaker and headphones) + + * 512x342 & 640x480 digital output on HSTX + * PIO USB + * PSRAM support + * Some Sound support on the onboard I2S DAC (speaker and headphones) Several pre-compiled variants are offered: - * 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) - * overclocked or not (overclocked may run faster but may be less reliable) + * 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) + * overclocked or not (overclocked may run faster but may be less reliable) What works? - * System beep - * Dark Castle including audio - * After Dark screensavers including audio - * Glider works, but without sound + * System beep + * Dark Castle including audio + * After Dark screensavers including audio + * Glider works, but without sound What doesn't work? - * Hypercard "play" and some hypercard screen transitions + * Hypercard "play" and some hypercard screen transitions Some of the software I tested with: - * https://archive.org/details/HyperCardBootSystem7 - * https://archive.org/details/mac\_DarkCastle\_1\_2 - * https://archive.org/details/AfterDark2 + * https://archive.org/details/HyperCardBootSystem7 + * https://archive.org/details/mac\_DarkCastle\_1\_2 + * https://archive.org/details/AfterDark2 Plug mouse & keyboard into the USB ports of the fruit jam. @@ -79,98 +80,23 @@ couple of cheap components. * git submodules - Clone the repo with `--recursive`, or `git submodule update --init --recursive` * Install/set up the [Pico/RP2040 SDK](https://github.com/raspberrypi/pico-sdk) - -## 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 -``` + * Get a ROM & disc image with `sh fetch-rom-dsk.sh` (needs curl & 7z (debian package p7zip-full)) ## Build pico-umac -Do the initial Pico SDK `cmake` setup into an out-of-tree build dir, -providing config options if required. - -From the top-level `pico-umac` directory: - +Run the configure-and-build script: ``` -mkdir build -(cd build ; PICO_SDK_PATH=/path/to/sdk cmake .. ) +$ ./fruitjam-build.sh -h +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=` - - `-DSD_RX=` - - `-DSD_SCK=` - - `-DSD_CS=` - - `-DSD_MHZ=` - * `-DMEMSIZE=`: 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=`: 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 If you don't build SD support, an internal read-only disc image is @@ -195,147 +121,15 @@ into _one_ of the following files in the root of the card: * `umac0.img`: A normal read/write 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 -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. 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 -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 into `umac`. @@ -352,32 +146,6 @@ VT220 emulator!). The USB HID code is largely stolen from the TinyUSB example, but shows 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 `hid.c` and `tusb_config.h` are based on code from the TinyUSB From 34e184b5fe4faade4f144c6696806fc31d2d016a Mon Sep 17 00:00:00 2001 From: ladyada Date: Fri, 28 Mar 2025 10:08:21 -0400 Subject: [PATCH 03/12] tweak headset gain to not be so loud. --- src/main.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/main.c b/src/main.c index 8283058..8ef568e 100644 --- a/src/main.c +++ b/src/main.c @@ -578,9 +578,9 @@ void Wire_begin() { static void setup_i2s_dac() { -gpio_init(22); -gpio_set_dir(22, true); -gpio_put(22, true); // allow i2s to come out of reset + gpio_init(22); + gpio_set_dir(22, true); + gpio_put(22, true); // allow i2s to come out of reset Wire_begin(); sleep_ms(1000); @@ -623,9 +623,9 @@ gpio_put(22, true); // allow i2s to come out of reset modifyRegister(0x05, 0x80, 0x80); // Headset and GPIO Config -setPage(1); -modifyRegister(0x2e, 0xFF, 0x0b); -setPage(0); + setPage(1); + modifyRegister(0x2e, 0xFF, 0x0b); + setPage(0); modifyRegister(0x43, 0x80, 0x80); // Headset Detect modifyRegister(0x30, 0x80, 0x80); // INT1 Control modifyRegister(0x33, 0x3C, 0x14); // GPIO1 @@ -642,27 +642,24 @@ setPage(0); // DAC Volume Control setPage(0); modifyRegister(0x40, 0x0C, 0x00); - writeRegister(0x41, 0x28); // Left DAC Vol - writeRegister(0x42, 0x28); // Right DAC Vol - - // ADC Setup - modifyRegister(0x51, 0x80, 0x80); - modifyRegister(0x52, 0x80, 0x00); - writeRegister(0x53, 0x68); // ADC Volume + writeRegister(0x41, 0x0); // Left DAC Vol, 0dB + writeRegister(0x42, 0x0); // Right DAC Vol, 0dB // Headphone and Speaker Setup setPage(1); - modifyRegister(0x1F, 0xC0, 0xC0); // HP Driver - 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(0x1F, 0xC0, 0xC0); // HP Driver Powered + + modifyRegister(0x28, 0x04, 0x04); // HP Left not muted + modifyRegister(0x29, 0x04, 0x04); // HP Right not muted + + writeRegister(0x24, 50); // Left Analog HP, -14dB + writeRegister(0x25, 50); // Right Analog HP, -14dB - modifyRegister(0x28, 0x78, 0x40); // HP Left Gain - modifyRegister(0x29, 0x78, 0x40); // HP Right Gain + modifyRegister(0x28, 0x78, 0x00); // HP Left Gain, 0 db + modifyRegister(0x29, 0x78, 0x00); // HP Right Gain, 0 db // Speaker Amp - modifyRegister(0x20, 0x80, 0x80); + modifyRegister(0x20, 0x80, 0x00); // powered down for now, set to 0x80 to power up! modifyRegister(0x2A, 0x04, 0x04); modifyRegister(0x2A, 0x18, 0x08); writeRegister(0x26, 0x0A); From 05d4c11f56f7c164dd4f86ae87c9993b66872a06 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 Mar 2025 09:11:44 -0500 Subject: [PATCH 04/12] Fix specifying disc images --- fruitjam-build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fruitjam-build.sh b/fruitjam-build.sh index 1d140db..06d3b13 100755 --- a/fruitjam-build.sh +++ b/fruitjam-build.sh @@ -64,8 +64,8 @@ fi # Append disk name to build directory if disk image is specified if [ -n "$DISC_IMAGE" ] && [ -f "$DISC_IMAGE" ]; then # Extract filename without extension + CMAKE_ARGS="$CMAKE_ARGS -DDISC_IMAGE=${DISC_IMAGE}" DISC_IMAGE=$(basename "$DISC_IMAGE" | sed 's/\.[^.]*$//') - CMAKE_ARGS="$CMAKE_ARGS -DDISK_IMAGE=${DISC_IMAGE}" TAG=${TAG}_${DISC_IMAGE} fi From d41ccd1082b18acfb219596ebdc898370be9c817 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 Mar 2025 09:12:28 -0500 Subject: [PATCH 05/12] ignore built files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c81c4eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.img +*.bin +os.7z +picotool From 41a8e0e9b6721c6fdc5be0fe5f1f52257799be79 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 Mar 2025 09:18:00 -0500 Subject: [PATCH 06/12] Mute the i2s dac when sound output is inactive --- src/main.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main.c b/src/main.c index 8ef568e..0ce89ac 100644 --- a/src/main.c +++ b/src/main.c @@ -739,7 +739,23 @@ static bool audio_poll() { 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 + } else { + modifyRegister(0x28, 0x04, 0x0); // HP Left muted + modifyRegister(0x29, 0x04, 0x0); // HP Right muted + } +} + void umac_audio_cfg(int volume, int sndres) { volscale = sndres ? 0 : 65536 * volume / 7; + set_mute_state(volscale != 0); } #endif From b1c20d06eaeed5fd2947e357465f38f99771aae4 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 Mar 2025 09:35:02 -0500 Subject: [PATCH 07/12] Change the LED to a SD activity light --- src/main.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main.c b/src/main.c index 0ce89ac..a48007c 100644 --- a/src/main.c +++ b/src/main.c @@ -104,15 +104,11 @@ static void io_init() static void poll_led_etc() { - static int led_on = 0; static absolute_time_t last = 0; absolute_time_t now = get_absolute_time(); if (absolute_time_diff_us(last, now) > 500*1000) { last = now; - - //led_on ^= 1; - //gpio_put(GPIO_LED_PIN, led_on); } } @@ -205,10 +201,12 @@ static void poll_umac() #if USE_SD 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; f_lseek(fp, offset); unsigned int did_read = 0; FRESULT fr = f_read(fp, data, len, &did_read); + gpio_put(GPIO_LED_PIN, 0); if (fr != FR_OK || len != did_read) { printf("disc: f_read returned %d, read %u (of %u)\n", fr, did_read, len); return -1; @@ -218,10 +216,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) { + gpio_put(GPIO_LED_PIN, 1); FIL *fp = (FIL *)ctx; f_lseek(fp, offset); unsigned int did_write = 0; FRESULT fr = f_write(fp, data, len, &did_write); + gpio_put(GPIO_LED_PIN, 0); if (fr != FR_OK || len != did_write) { printf("disc: f_write returned %d, read %u (of %u)\n", fr, did_write, len); return -1; @@ -676,9 +676,6 @@ static int volscale; int16_t audio[SAMPLES_PER_BUFFER]; void umac_audio_trap() { -static int led_on; - led_on ^= 1; - gpio_put(GPIO_LED_PIN, 1); int32_t offset = 128; uint16_t *audiodata = (uint16_t*)audio_base; int scale = volscale; @@ -732,7 +729,6 @@ setup_i2s_dac(); static bool audio_poll() { audio_buffer_t *buffer = take_audio_buffer(producer_pool, false); if (!buffer) return false; - gpio_put(GPIO_LED_PIN, 0); memcpy(buffer->buffer->bytes, audio, sizeof(audio)); buffer->sample_count = SAMPLES_PER_BUFFER; give_audio_buffer(producer_pool, buffer); From ad1c9265bebf40ae6af892788183d26321eef830 Mon Sep 17 00:00:00 2001 From: ladyada Date: Fri, 28 Mar 2025 10:49:12 -0400 Subject: [PATCH 08/12] headphone gain is good now --- src/main.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.c b/src/main.c index 8ef568e..37226e3 100644 --- a/src/main.c +++ b/src/main.c @@ -652,22 +652,22 @@ static void setup_i2s_dac() { modifyRegister(0x28, 0x04, 0x04); // HP Left not muted modifyRegister(0x29, 0x04, 0x04); // HP Right not muted - writeRegister(0x24, 50); // Left Analog HP, -14dB - writeRegister(0x25, 50); // Right Analog HP, -14dB + 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 - modifyRegister(0x20, 0x80, 0x00); // powered down for now, set to 0x80 to power up! - modifyRegister(0x2A, 0x04, 0x04); - modifyRegister(0x2A, 0x18, 0x08); - writeRegister(0x26, 0x0A); + modifyRegister(0x20, 0x80, 0x80); // Amp enabled (0x80) disable with (0x00) + modifyRegister(0x2A, 0x04, 0x00); // Not muted (0x04) mute with (0x00) + modifyRegister(0x2A, 0x18, 0x08); // 0 dB gain + writeRegister(0x26, 0x0 | 60); // amp gain, -26 dB // Return to page 0 setPage(0); - printf("Initialization complete!\n"); + printf("Audio I2C Initialization complete!\n"); } static int volscale; From baef470b95f36fc9b9575185fe4d7ddf4efa5aa3 Mon Sep 17 00:00:00 2001 From: ladyada Date: Fri, 28 Mar 2025 10:50:52 -0400 Subject: [PATCH 09/12] turn off speaker for now --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 37226e3..2d96bed 100644 --- a/src/main.c +++ b/src/main.c @@ -659,7 +659,7 @@ static void setup_i2s_dac() { modifyRegister(0x29, 0x78, 0x00); // HP Right Gain, 0 db // Speaker Amp - modifyRegister(0x20, 0x80, 0x80); // Amp enabled (0x80) disable with (0x00) + modifyRegister(0x20, 0x80, 0x00); // Amp enabled (0x80) disable with (0x00) modifyRegister(0x2A, 0x04, 0x00); // Not muted (0x04) mute with (0x00) modifyRegister(0x2A, 0x18, 0x08); // 0 dB gain writeRegister(0x26, 0x0 | 60); // amp gain, -26 dB From 0e464348437800b26c62c50250f236aa83a02a04 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 Mar 2025 11:05:46 -0500 Subject: [PATCH 10/12] mute headphone when not actively playing audio --- src/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.c b/src/main.c index 780830b..5abd937 100644 --- a/src/main.c +++ b/src/main.c @@ -744,9 +744,11 @@ static void set_mute_state(bool new_state) { if(mute_state) { modifyRegister(0x28, 0x04, 0x04); // HP Left not muted modifyRegister(0x29, 0x04, 0x04); // HP Right not muted + modifyRegister(0x2A, 0x04, 0x00); // Speaker not muted } else { modifyRegister(0x28, 0x04, 0x0); // HP Left muted modifyRegister(0x29, 0x04, 0x0); // HP Right muted + modifyRegister(0x2A, 0x04, 0x0); // Speaker muted } } From eebab0afa451efae2aa22d7a59d2dd6582a753d3 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 Mar 2025 11:26:14 -0500 Subject: [PATCH 11/12] mute speaker too --- src/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.c b/src/main.c index 5abd937..0c31c6d 100644 --- a/src/main.c +++ b/src/main.c @@ -659,10 +659,10 @@ static void setup_i2s_dac() { modifyRegister(0x29, 0x78, 0x00); // HP Right Gain, 0 db // Speaker Amp - modifyRegister(0x20, 0x80, 0x00); // Amp enabled (0x80) disable with (0x00) - modifyRegister(0x2A, 0x04, 0x00); // Not muted (0x04) mute with (0x00) + modifyRegister(0x20, 0x80, 0x80); // Amp enabled (0x80) disable with (0x00) + modifyRegister(0x2A, 0x04, 0x04); // Not muted (0x04) mute with (0x00) modifyRegister(0x2A, 0x18, 0x08); // 0 dB gain - writeRegister(0x26, 0x0 | 60); // amp gain, -26 dB + writeRegister(0x26, 40); // amp gain, -20.1 dB // Return to page 0 setPage(0); @@ -739,12 +739,12 @@ 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, 0x00); // Speaker not muted + modifyRegister(0x2A, 0x04, 0x04); // Speaker not muted } else { modifyRegister(0x28, 0x04, 0x0); // HP Left muted modifyRegister(0x29, 0x04, 0x0); // HP Right muted From 1c71dd765ae86b2501cb3db477f54ea9ded9bc2e Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 Mar 2025 11:48:04 -0500 Subject: [PATCH 12/12] Automute 1/2s after audio finishes playing --- src/main.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 0c31c6d..e8e2c30 100644 --- a/src/main.c +++ b/src/main.c @@ -65,6 +65,8 @@ uint8_t *audio_base; static void audio_setup(); static bool audio_poll(); +static void set_mute_state(bool new_state); +static absolute_time_t automute_time; #endif //////////////////////////////////////////////////////////////////////////////// @@ -153,6 +155,12 @@ static void poll_umac() int64_t p_1hz = absolute_time_diff_us(last_1hz, now); int64_t p_vsync = absolute_time_diff_us(last_vsync, now); 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 pending_vsync |= audio_poll(); #endif @@ -565,7 +573,6 @@ void modifyRegister(uint8_t reg, uint8_t mask, uint8_t value) { } void setPage(uint8_t page) { - printf("Set page %d\n", page); writeRegister(0x00, page); } @@ -676,6 +683,10 @@ static int volscale; int16_t audio[SAMPLES_PER_BUFFER]; void umac_audio_trap() { + set_mute_state(volscale != 0); + if(volscale) { + automute_time = make_timeout_time_ms(500); + } int32_t offset = 128; uint16_t *audiodata = (uint16_t*)audio_base; int scale = volscale;