Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc7ab44c23 | |||
| 0ab1ba2285 |
6 changed files with 336 additions and 128 deletions
5
.github/workflows/build.yaml
vendored
5
.github/workflows/build.yaml
vendored
|
|
@ -23,9 +23,6 @@ jobs:
|
|||
with:
|
||||
release: '13.2.Rel1'
|
||||
|
||||
- name: install sdl
|
||||
run: sudo apt-get update && sudo apt-get install -y eatmydata && sudo eatmydata apt-get install -y libsdl2-dev
|
||||
|
||||
- name: get submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
|
@ -37,6 +34,8 @@ jobs:
|
|||
./fetch-rom-dsk.sh
|
||||
./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 -o
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,4 +0,0 @@
|
|||
*.img
|
||||
*.bin
|
||||
os.7z
|
||||
picotool
|
||||
309
README.md
309
README.md
|
|
@ -1,44 +1,36 @@
|
|||
# Pico Micro Mac (pico-umac)
|
||||
|
||||
v0.21-fruitjam 28 March 2025
|
||||
v0.21-fruitjam 22 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
|
||||
|
||||
* 512x342 & 640x480 digital output on HSTX
|
||||
* PIO USB
|
||||
* PSRAM 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).
|
||||
|
||||
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)
|
||||
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.
|
||||
|
||||
What works?
|
||||
* System beep
|
||||
* Dark Castle including audio
|
||||
* After Dark screensavers including audio
|
||||
* Glider works, but without sound
|
||||
* 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
|
||||
|
||||
What doesn't work?
|
||||
* Hypercard "play" and some hypercard screen transitions
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
There are artifacts that you can grab from the latest Actions build, at least until they expire.
|
||||
|
||||
|
||||
**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.
|
||||
Some good Mac software:
|
||||
* https://archive.org/details/HyperCardBootSystem7
|
||||
* https://archive.org/details/mac_DarkCastle_1_2
|
||||
* https://archive.org/details/AfterDark2
|
||||
|
||||
v0.21 20 December 2024
|
||||
|
||||
|
|
@ -80,22 +72,97 @@ 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)
|
||||
* 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
|
||||
|
||||
Run the configure-and-build script:
|
||||
```
|
||||
$ ./fruitjam-build.sh -h
|
||||
Usage: ./fruitjam-build.sh [-v] [-m KiB] [-d diskimage]
|
||||
Do the initial Pico SDK `cmake` setup into an out-of-tree build dir,
|
||||
providing config options if required.
|
||||
|
||||
-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)
|
||||
From the top-level `pico-umac` directory:
|
||||
|
||||
PSRAM is automatically set depending on memory & framebuffer details
|
||||
```
|
||||
mkdir build
|
||||
(cd build ; PICO_SDK_PATH=/path/to/sdk cmake .. <options>)
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -121,15 +188,147 @@ 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 optionally overclocked (blush) to 264MHz so that
|
||||
Both CPU cores are used, and are overclocked (blush) to 250MHz 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, or to a mirrored region in SRAM depending
|
||||
on the configuration.
|
||||
framebuffer in the Mac's RAM.
|
||||
|
||||
Other than that, it's just a main loop in `main.c` shuffling things
|
||||
into `umac`.
|
||||
|
|
@ -146,6 +345,32 @@ 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ static void __no_inline_not_in_flash_func(set_qmi_timing)() {
|
|||
#endif
|
||||
|
||||
#ifndef RP2350_PSRAM_MAX_SCK_HZ
|
||||
#define RP2350_PSRAM_MAX_SCK_HZ (109000000)
|
||||
#define RP2350_PSRAM_MAX_SCK_HZ (66777777)
|
||||
#endif
|
||||
|
||||
#define SEC_TO_FS 1000000000000000ll
|
||||
|
|
@ -62,6 +62,7 @@ static void __no_inline_not_in_flash_func(set_psram_timing)(void) {
|
|||
|
||||
printf("syshz=%u\n", sysHz);
|
||||
printf("Max Select: %d, Min Deselect: %d, clock divider: %d\n", maxSelect, minDeselect, 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.
|
||||
|
|
@ -142,10 +143,6 @@ void overclock(enum clk_sys_speed clk_sys_div, uint32_t bit_clk_khz) {
|
|||
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_gpout0);
|
||||
SHOW_CLK(clk_gpout1);
|
||||
SHOW_CLK(clk_gpout2);
|
||||
SHOW_CLK(clk_gpout3);
|
||||
SHOW_CLK(clk_ref);
|
||||
SHOW_CLK(clk_sys);
|
||||
SHOW_CLK(clk_peri);
|
||||
|
|
|
|||
137
src/main.c
137
src/main.c
|
|
@ -65,8 +65,6 @@
|
|||
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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -106,11 +104,15 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,12 +157,6 @@ 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
|
||||
|
|
@ -209,12 +205,10 @@ 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;
|
||||
|
|
@ -224,12 +218,10 @@ 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;
|
||||
|
|
@ -480,27 +472,40 @@ static void __no_inline_not_in_flash_func(setup_psram)(void) {
|
|||
|
||||
int main()
|
||||
{
|
||||
#if defined(OVERCLOCK) && OVERCLOCK+0
|
||||
setup_psram();
|
||||
#if OVERCLOCK
|
||||
overclock(CLK_SYS_264MHZ, 252000);
|
||||
#endif
|
||||
// set_sys_clock_khz(250*1000, true);
|
||||
|
||||
setup_psram();
|
||||
|
||||
stdio_init_all();
|
||||
io_init();
|
||||
|
||||
#define SHOW_CLK(i) printf("clk_get_hz(%s) -> %u\n", #i, clock_get_hz(i));
|
||||
SHOW_CLK(clk_gpout0);
|
||||
SHOW_CLK(clk_gpout1);
|
||||
SHOW_CLK(clk_gpout2);
|
||||
SHOW_CLK(clk_gpout3);
|
||||
SHOW_CLK(clk_ref);
|
||||
SHOW_CLK(clk_sys);
|
||||
SHOW_CLK(clk_peri);
|
||||
SHOW_CLK(clk_hstx);
|
||||
SHOW_CLK(clk_usb);
|
||||
SHOW_CLK(clk_adc);
|
||||
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();
|
||||
|
||||
#if ENABLE_AUDIO
|
||||
audio_setup();
|
||||
|
|
@ -573,6 +578,7 @@ 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);
|
||||
}
|
||||
|
||||
|
|
@ -585,9 +591,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);
|
||||
|
|
@ -630,9 +636,9 @@ static void setup_i2s_dac() {
|
|||
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
|
||||
|
|
@ -649,32 +655,35 @@ static void setup_i2s_dac() {
|
|||
// DAC Volume Control
|
||||
setPage(0);
|
||||
modifyRegister(0x40, 0x0C, 0x00);
|
||||
writeRegister(0x41, 0x0); // Left DAC Vol, 0dB
|
||||
writeRegister(0x42, 0x0); // Right DAC Vol, 0dB
|
||||
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
|
||||
|
||||
// Headphone and Speaker Setup
|
||||
setPage(1);
|
||||
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, -26 dB
|
||||
writeRegister(0x25, 50); // Right Analog HP, -26 dB
|
||||
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(0x28, 0x78, 0x00); // HP Left Gain, 0 db
|
||||
modifyRegister(0x29, 0x78, 0x00); // HP Right Gain, 0 db
|
||||
modifyRegister(0x28, 0x78, 0x40); // HP Left Gain
|
||||
modifyRegister(0x29, 0x78, 0x40); // HP Right Gain
|
||||
|
||||
// Speaker Amp
|
||||
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, 40); // amp gain, -20.1 dB
|
||||
modifyRegister(0x20, 0x80, 0x80);
|
||||
modifyRegister(0x2A, 0x04, 0x04);
|
||||
modifyRegister(0x2A, 0x18, 0x08);
|
||||
writeRegister(0x26, 0x0A);
|
||||
|
||||
// Return to page 0
|
||||
setPage(0);
|
||||
|
||||
printf("Audio I2C Initialization complete!\n");
|
||||
printf("Initialization complete!\n");
|
||||
}
|
||||
static int volscale;
|
||||
|
||||
|
|
@ -683,10 +692,9 @@ 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);
|
||||
}
|
||||
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;
|
||||
|
|
@ -740,31 +748,14 @@ 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);
|
||||
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) {
|
||||
volscale = sndres ? 0 : 65536 * volume / 7;
|
||||
set_mute_state(volscale != 0);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue