Compare commits
11 Commits
pcb-rev1
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f9bdb2517 | |||
| 502805e2e8 | |||
| 4a15b2c221 | |||
| d3674e46aa | |||
| 1fa3b3c887 | |||
| 4d295501eb | |||
| c9150eb21a | |||
| da90228ab5 | |||
| bd17197fef | |||
| d39157ba0a | |||
| 09c8f522b8 |
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: Build RPi Pico firmware image
|
||||
on:
|
||||
"on":
|
||||
push:
|
||||
|
||||
jobs:
|
||||
@@ -22,4 +22,4 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: firmware-RPi-Pico-W-with-fs
|
||||
path: software/lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/firmware-filesystem.uf2
|
||||
path: software/lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/firmware-filesystem-*.uf2
|
||||
|
||||
@@ -341,15 +341,15 @@
|
||||
(uuid "b2d6f12f-bf69-4a9b-a3e9-164231e4f483")
|
||||
)
|
||||
(embedded_fonts no)
|
||||
(model "${KICAD9_3DMODEL_DIR}/Connector_PinSocket_2.54mm.3dshapes/PinSocket_1x07_P2.54mm_Vertical.step"
|
||||
(model "${KICAD9_3DMODEL_DIR}/Connector_PinSocket_2.54mm.3dshapes/PinSocket_1x08_P2.54mm_Vertical.step"
|
||||
(offset
|
||||
(xyz 0 0 0)
|
||||
(xyz 0 1.25 0)
|
||||
)
|
||||
(scale
|
||||
(xyz 1 1 1)
|
||||
)
|
||||
(rotate
|
||||
(xyz 0 0 0)
|
||||
(xyz -0 -0 -0)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -79,14 +79,6 @@
|
||||
"items_not_allowed|148720706|136551802|f2c1e20e-a272-42d8-a551-3e9074b96921|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"lib_footprint_mismatch|117348000|41148000|9810a3ba-e3fe-4afe-955a-bea6c56712c1|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"lib_footprint_mismatch|210176000|131198000|bf46a807-edd1-4c31-a613-56c8fe7b1e6b|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
],
|
||||
[
|
||||
"nonmirrored_text_on_back_layer|210811000|131073000|e87ca627-5327-4f7e-ac1a-c1eadd662c0a|00000000-0000-0000-0000-000000000000",
|
||||
""
|
||||
@@ -177,7 +169,7 @@
|
||||
"min_track_width": 0.2032,
|
||||
"min_via_annular_width": 0.15,
|
||||
"min_via_diameter": 0.5,
|
||||
"solder_mask_to_copper_clearance": 0.0,
|
||||
"solder_mask_to_copper_clearance": 0.005,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"teardrop_options": [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,9 +54,9 @@ add_subdirectory(modules/audiocore)
|
||||
|
||||
add_custom_target(check-format
|
||||
find . -iname '*.[ch]' -exec clang-format -Werror --dry-run {} +
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/modules
|
||||
)
|
||||
add_custom_target(clang-format
|
||||
find . -iname '*.[ch]' -exec clang-format -i {} +
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/modules
|
||||
)
|
||||
|
||||
@@ -27,12 +27,19 @@ fi
|
||||
BUILDDIR=lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/
|
||||
FS_STAGE_DIR=$(mktemp -d)
|
||||
trap 'rm -rf $FS_STAGE_DIR' EXIT
|
||||
find src/ -iname '*.py' | cpio -pdm "$FS_STAGE_DIR"
|
||||
tools/mklittlefs/mklittlefs -p 256 -s 868352 -c "$FS_STAGE_DIR"/src $BUILDDIR/filesystem.bin
|
||||
truncate -s 2M $BUILDDIR/firmware-filesystem.bin
|
||||
dd if=$BUILDDIR/firmware.bin of=$BUILDDIR/firmware-filesystem.bin bs=1k
|
||||
dd if=$BUILDDIR/filesystem.bin of=$BUILDDIR/firmware-filesystem.bin bs=1k seek=1200
|
||||
$PICOTOOL uf2 convert $BUILDDIR/firmware-filesystem.bin $BUILDDIR/firmware-filesystem.uf2
|
||||
for hwconfig in src/hwconfig_*.py; do
|
||||
hwconfig_base=$(basename "$hwconfig")
|
||||
hwname=${hwconfig_base##hwconfig_}
|
||||
hwname=${hwname%%.py}
|
||||
find src/ -iname '*.py' \! -iname 'hwconfig_*.py' | cpio -pdm "$FS_STAGE_DIR"
|
||||
cp "$hwconfig" "$FS_STAGE_DIR"/src/hwconfig.py
|
||||
tools/mklittlefs/mklittlefs -p 256 -s 868352 -c "$FS_STAGE_DIR"/src $BUILDDIR/filesystem.bin
|
||||
truncate -s 2M $BUILDDIR/firmware-filesystem.bin
|
||||
dd if=$BUILDDIR/firmware.bin of=$BUILDDIR/firmware-filesystem.bin bs=1k
|
||||
dd if=$BUILDDIR/filesystem.bin of=$BUILDDIR/firmware-filesystem.bin bs=1k seek=1200
|
||||
$PICOTOOL uf2 convert $BUILDDIR/firmware-filesystem.bin $BUILDDIR/firmware-filesystem-"$hwname".uf2
|
||||
rm -r "${FS_STAGE_DIR:?}"/*
|
||||
done
|
||||
|
||||
echo "Output in $BUILDDIR/firmware.uf2"
|
||||
echo "Image with filesystem in $BUILDDIR/firmware-filesystem.uf2"
|
||||
echo "Images with filesystem in" ${BUILDDIR}firmware-filesystem-*.uf2
|
||||
|
||||
@@ -16,14 +16,15 @@ check_command lsusb
|
||||
check_command picotool
|
||||
|
||||
DEVICEPATH=/dev/disk/by-label/RPI-RP2
|
||||
IMAGEPATH=lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/firmware.uf2
|
||||
IMAGEPATH=lib/micropython/ports/rp2/build-TONBERRY_RPI_PICO_W/
|
||||
REVISION=Rev1
|
||||
|
||||
flash_via_mountpoint()
|
||||
{
|
||||
while [ ! -e "$DEVICEPATH" ] ; do sleep 1; echo 'Waiting for RP2...'; done
|
||||
|
||||
udisksctl mount -b "$DEVICEPATH"
|
||||
cp "$IMAGEPATH" "$(findmnt "$DEVICEPATH" -n -o TARGET)"
|
||||
cp "$IMAGEFILE" "$(findmnt "$DEVICEPATH" -n -o TARGET)"
|
||||
}
|
||||
|
||||
PID="2e8a"
|
||||
@@ -40,7 +41,7 @@ flash_via_picotool()
|
||||
local device="${bus_device[1]//[!0-9]/}"
|
||||
echo "Found RP2 with serial $serial on Bus $bus Device $device"
|
||||
|
||||
picotool load --bus "$bus" --address "$device" "$IMAGEPATH"
|
||||
picotool load --bus "$bus" --address "$device" "$IMAGEFILE"
|
||||
}
|
||||
|
||||
FLASH_VIA_MOUNTPOINT=0
|
||||
@@ -52,11 +53,12 @@ usage()
|
||||
echo
|
||||
echo " -m, --via-mountpoint Mount first found RP2 and flash image by"
|
||||
echo " copying to mountpoint."
|
||||
echo " -r, --revision <rev> Hardware revision to flash. Default is Rev1"
|
||||
echo " -h, --help Print this text and exit."
|
||||
exit 2
|
||||
}
|
||||
|
||||
PARSED_ARGUMENTS=$(getopt -a -n "$0" -o mh --long via-mountpoint,help -- "$@")
|
||||
PARSED_ARGUMENTS=$(getopt -a -n "$0" -o mhr: --long via-mountpoint,revision:,help -- "$@")
|
||||
# shellcheck disable=SC2181
|
||||
# Indirect getopt return value checking is okay here
|
||||
if [ "$?" != "0" ]; then
|
||||
@@ -68,6 +70,7 @@ while :
|
||||
do
|
||||
case "$1" in
|
||||
-m | --via-mountpoint) FLASH_VIA_MOUNTPOINT=1 ; shift ;;
|
||||
-r | --revision) REVISION=$2 ; shift 2 ;;
|
||||
-h | --help) usage ;;
|
||||
--) shift; break ;;
|
||||
*) echo "Unexpected option: $1"
|
||||
@@ -80,6 +83,8 @@ if [ $# -gt 0 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
IMAGEFILE="$IMAGEPATH"/firmware-filesystem-$REVISION.uf2
|
||||
|
||||
if [ "$FLASH_VIA_MOUNTPOINT" -eq 0 ]; then
|
||||
flash_via_picotool
|
||||
else
|
||||
|
||||
@@ -40,7 +40,7 @@ void __time_critical_func(core1_main)(void)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
bool running = true, playing = false;
|
||||
if (!i2s_init(shared_context.out_pin, shared_context.sideset_base)) {
|
||||
if (!i2s_init(shared_context.out_pin, shared_context.sideset_base, shared_context.sideset_dclk_first)) {
|
||||
ret = MP_EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ struct audiocore_shared_context {
|
||||
|
||||
// Set by module.c before core1 is launched and then never changed, can be read without lock
|
||||
int out_pin, sideset_base, samplerate;
|
||||
bool sideset_dclk_first;
|
||||
|
||||
// Must hold lock. The indices 0..MP3_BUFFER_PREAREA-1 may only be read and written on core1 (no
|
||||
// lock needed) The buffer is aligned to, and MP3_BUFFER_PREAREA is a multiple of, the machine
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
import _audiocore
|
||||
from asyncio import ThreadSafeFlag
|
||||
from utils import get_pin_index
|
||||
|
||||
|
||||
class Audiocore:
|
||||
def __init__(self, pin, sideset):
|
||||
def __init__(self, din, dclk, lrclk):
|
||||
# PIO requires sideset pins to be adjacent
|
||||
assert get_pin_index(lrclk) == get_pin_index(dclk)+1 or get_pin_index(lrclk) == get_pin_index(dclk)-1
|
||||
self.notify = ThreadSafeFlag()
|
||||
self.pin = pin
|
||||
self.sideset = sideset
|
||||
self._audiocore = _audiocore.Audiocore(self.pin, self.sideset, self._interrupt)
|
||||
self._audiocore = _audiocore.Audiocore(din, dclk, lrclk, self._interrupt)
|
||||
|
||||
def deinit(self):
|
||||
self._audiocore.deinit()
|
||||
@@ -40,12 +44,13 @@ class Audiocore:
|
||||
|
||||
|
||||
class AudioContext:
|
||||
def __init__(self, pin, sideset):
|
||||
self.pin = pin
|
||||
self.sideset = sideset
|
||||
def __init__(self, din, dclk, lrclk):
|
||||
self.din = din
|
||||
self.dclk = dclk
|
||||
self.lrclk = lrclk
|
||||
|
||||
def __enter__(self):
|
||||
self._audiocore = Audiocore(self.pin, self.sideset)
|
||||
self._audiocore = Audiocore(self.din, self.dclk, self.lrclk)
|
||||
return self._audiocore
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
|
||||
@@ -113,17 +113,18 @@ void i2s_stop(void)
|
||||
pio_sm_clear_fifos(audiocore_pio, i2s_context.pio_sm);
|
||||
}
|
||||
|
||||
bool i2s_init(int out_pin, int sideset_base)
|
||||
bool i2s_init(int out_pin, int sideset_base, bool dclk_first)
|
||||
{
|
||||
memset(i2s_context.dma_buf, 0, sizeof(i2s_context.dma_buf[0][0]) * AUDIO_BUFS * I2S_DMA_BUF_SIZE);
|
||||
if (!pio_can_add_program(audiocore_pio, (const pio_program_t *)&i2s_max98357_program))
|
||||
const pio_program_t *program = dclk_first ? &i2s_max98357_program : &i2s_max98357_lrclk_program;
|
||||
if (!pio_can_add_program(audiocore_pio, program))
|
||||
return false;
|
||||
i2s_context.pio_sm = pio_claim_unused_sm(audiocore_pio, false);
|
||||
i2s_context.out_pin = out_pin;
|
||||
i2s_context.sideset_base = sideset_base;
|
||||
if (i2s_context.pio_sm == -1)
|
||||
return false;
|
||||
i2s_context.pio_program_offset = pio_add_program(audiocore_pio, (const pio_program_t *)&i2s_max98357_program);
|
||||
i2s_context.pio_program_offset = pio_add_program(audiocore_pio, program);
|
||||
|
||||
i2s_context.dma_ch = dma_claim_unused_channel(false);
|
||||
if (i2s_context.dma_ch == -1)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#define I2S_DMA_BUF_SIZE (1152)
|
||||
|
||||
bool i2s_init(int out_pin, int sideset_base);
|
||||
bool i2s_init(int out_pin, int sideset_base, bool dclk_first);
|
||||
void i2s_deinit(void);
|
||||
|
||||
void i2s_play(int samplerate);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
.lang_opt python autopull = True
|
||||
|
||||
// data - DOUT
|
||||
// sideset - 2-BCLK, 1-LRCLK
|
||||
// sideset - 2-LRCLK, 1-BCLK
|
||||
|
||||
set x,15 side 0
|
||||
nop side 1
|
||||
@@ -33,6 +33,38 @@ right_loop:
|
||||
set x, 14 side 1
|
||||
.wrap
|
||||
|
||||
.program i2s_max98357_lrclk
|
||||
.side_set 2
|
||||
|
||||
.lang_opt python sideset_init = pico.PIO.OUT_LOW
|
||||
.lang_opt python out_init = pico.PIO.OUT_LOW
|
||||
.lang_opt python out_shiftdir = pcio.PIO.SHIFT_LEFT
|
||||
.lang_opt python autopull = True
|
||||
|
||||
// data - DOUT
|
||||
// sideset - 2-BCLK, 1-LRCLK
|
||||
|
||||
set x,15 side 0
|
||||
nop side 2
|
||||
startup_loop:
|
||||
nop side 1
|
||||
jmp x-- startup_loop side 3
|
||||
nop side 0
|
||||
set x, 14 side 2
|
||||
|
||||
left_loop:
|
||||
.wrap_target
|
||||
out pins, 1 side 0
|
||||
jmp x-- left_loop side 2
|
||||
out pins, 1 side 1
|
||||
set x, 14 side 3
|
||||
right_loop:
|
||||
out pins, 1 side 1
|
||||
jmp x-- right_loop side 3
|
||||
out pins, 1 side 0
|
||||
set x, 14 side 2
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
|
||||
@@ -170,10 +170,11 @@ static MP_DEFINE_CONST_FUN_OBJ_2(audiocore_set_volume_obj, audiocore_set_volume)
|
||||
*/
|
||||
static void audiocore_init(struct audiocore_obj *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
||||
{
|
||||
enum { ARG_pin, ARG_sideset, ARG_handler };
|
||||
enum { ARG_pin, ARG_dclk, ARG_lrclk, ARG_handler };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
{MP_QSTR_sideset, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
{MP_QSTR_dclk, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
{MP_QSTR_lrclk, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
{MP_QSTR_handler, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
};
|
||||
if (initialized)
|
||||
@@ -188,7 +189,8 @@ static void audiocore_init(struct audiocore_obj *obj, size_t n_args, const mp_ob
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
const mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(args[ARG_pin].u_obj);
|
||||
const mp_hal_pin_obj_t sideset_pin = mp_hal_get_pin_obj(args[ARG_sideset].u_obj);
|
||||
const mp_hal_pin_obj_t dclk_pin = mp_hal_get_pin_obj(args[ARG_dclk].u_obj);
|
||||
const mp_hal_pin_obj_t lrclk_pin = mp_hal_get_pin_obj(args[ARG_lrclk].u_obj);
|
||||
if (args[ARG_handler].u_obj != MP_OBJ_NULL) {
|
||||
obj->irq_obj = mp_irq_new(&audiocore_irq_methods, MP_OBJ_FROM_PTR(obj));
|
||||
obj->irq_obj->handler = args[ARG_handler].u_obj;
|
||||
@@ -203,7 +205,14 @@ static void audiocore_init(struct audiocore_obj *obj, size_t n_args, const mp_ob
|
||||
memset(shared_context.mp3_buffer, 0, MP3_BUFFER_PREAREA + MP3_BUFFER_SIZE);
|
||||
multicore_reset_core1();
|
||||
shared_context.out_pin = pin;
|
||||
shared_context.sideset_base = sideset_pin;
|
||||
// PIO requires sideset pins to be adjacent, but we support both dclk first and lrclk first
|
||||
if (lrclk_pin == dclk_pin + 1) {
|
||||
shared_context.sideset_base = dclk_pin;
|
||||
shared_context.sideset_dclk_first = true;
|
||||
} else {
|
||||
shared_context.sideset_base = lrclk_pin;
|
||||
shared_context.sideset_dclk_first = false;
|
||||
}
|
||||
initialized = true;
|
||||
multicore_launch_core1(&core1_main);
|
||||
uint32_t result = get_fifo_read_value_blocking(obj);
|
||||
|
||||
@@ -15,7 +15,7 @@ static unsigned multicore_fifo_push_last;
|
||||
|
||||
static unsigned (*multicore_fifo_pop_blocking_cb)(void);
|
||||
|
||||
bool i2s_init(int out_pin, int sideset_base)
|
||||
bool i2s_init(int out_pin, int sideset_base, bool dclk_first)
|
||||
{
|
||||
TEST_ASSERT_FALSE(i2s_initialized);
|
||||
if (i2s_init_return)
|
||||
|
||||
@@ -23,7 +23,7 @@ static bool sd_acmd(const uint8_t cmd, const uint32_t arg, unsigned resplen, uin
|
||||
static bool sd_early_init(void)
|
||||
{
|
||||
uint8_t buf;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
if (sd_cmd(0, 0, 1, &buf)) {
|
||||
#ifdef SD_DEBUG
|
||||
printf("CMD0 resp %02hhx\n", buf);
|
||||
@@ -63,7 +63,7 @@ static bool sd_send_op_cond(void)
|
||||
{
|
||||
uint8_t buf;
|
||||
bool use_acmd = true;
|
||||
for (int timeout = 0; timeout < 500; ++timeout) {
|
||||
for (int timeout = 0; timeout < 50000; ++timeout) {
|
||||
bool result = false;
|
||||
if (use_acmd)
|
||||
result = sd_acmd(41, 0x40000000, 1, &buf);
|
||||
@@ -74,6 +74,7 @@ static bool sd_send_op_cond(void)
|
||||
#ifdef SD_DEBUG
|
||||
printf("sd_init: card does not understand ACMD41, try CMD1...\n");
|
||||
#endif
|
||||
use_acmd = false;
|
||||
continue;
|
||||
} else if (buf != 0x01) {
|
||||
printf("sd_init: send_op_cond failed\n");
|
||||
@@ -174,6 +175,7 @@ static bool sd_read_csd(struct sd_context *sd_context)
|
||||
}
|
||||
}
|
||||
sd_context->blocks = blocks;
|
||||
sd_context->blocksize = blocksize;
|
||||
#ifdef SD_DEBUG
|
||||
printf("CSD version %u.0, blocksize %u, blocks %u, capacity %llu MiB\n", version, blocksize, blocks,
|
||||
((uint64_t)blocksize * blocks) / (1024 * 1024));
|
||||
@@ -225,6 +227,26 @@ bool sd_init(struct sd_context *sd_context, int mosi, int miso, int sck, int ss,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sd_context->blocksize != SD_SECTOR_SIZE) {
|
||||
if (sd_context->blocksize != 1024 && sd_context->blocksize != 2048) {
|
||||
printf("sd_init: Unsupported block size %u\n", sd_context->blocksize);
|
||||
return false;
|
||||
}
|
||||
// Attempt SET_BLOCKLEN command
|
||||
uint8_t resp[1];
|
||||
if (!sd_cmd(16, SD_SECTOR_SIZE, 1, resp)) {
|
||||
printf("sd_init: SET_BLOCKLEN failed\n");
|
||||
return false;
|
||||
}
|
||||
// Successfully set blocksize to SD_SECTOR_SIZE, adjust context
|
||||
sd_context->blocks *= sd_context->blocksize / SD_SECTOR_SIZE;
|
||||
#ifdef SD_DEBUG
|
||||
printf("Adjusted blocksize from %u to 512, card now has %u blocks\n", sd_context->blocksize,
|
||||
sd_context->blocks);
|
||||
#endif
|
||||
sd_context->blocksize = SD_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
#ifdef SD_DEBUG
|
||||
sd_dump_cid();
|
||||
#endif
|
||||
@@ -246,14 +268,24 @@ bool sd_readblock(struct sd_context *sd_context, size_t sector_num, uint8_t buff
|
||||
if (!sd_context->initialized || sector_num >= sd_context->blocks)
|
||||
return false;
|
||||
|
||||
return sd_cmd_read(17, sector_num, SD_SECTOR_SIZE, buffer);
|
||||
uint32_t addr = sector_num;
|
||||
if (!sd_context->sdhc_sdxc) {
|
||||
// SDSC cards used byte addressing
|
||||
addr *= SD_SECTOR_SIZE;
|
||||
}
|
||||
return sd_cmd_read(17, addr, SD_SECTOR_SIZE, buffer);
|
||||
}
|
||||
|
||||
bool sd_readblock_start(struct sd_context *sd_context, size_t sector_num, uint8_t buffer[static SD_SECTOR_SIZE])
|
||||
{
|
||||
if (!sd_context->initialized || sector_num >= sd_context->blocks)
|
||||
return false;
|
||||
return sd_cmd_read_start(17, sector_num, SD_SECTOR_SIZE, buffer);
|
||||
uint32_t addr = sector_num;
|
||||
if (!sd_context->sdhc_sdxc) {
|
||||
// SDSC cards used byte addressing
|
||||
addr *= SD_SECTOR_SIZE;
|
||||
}
|
||||
return sd_cmd_read_start(17, addr, SD_SECTOR_SIZE, buffer);
|
||||
}
|
||||
|
||||
bool sd_readblock_complete(struct sd_context *sd_context)
|
||||
@@ -271,5 +303,10 @@ bool sd_writeblock(struct sd_context *sd_context, size_t sector_num, uint8_t buf
|
||||
if (!sd_context->initialized || sector_num >= sd_context->blocks)
|
||||
return false;
|
||||
|
||||
return sd_cmd_write(24, sector_num, SD_SECTOR_SIZE, buffer);
|
||||
uint32_t addr = sector_num;
|
||||
if (!sd_context->sdhc_sdxc) {
|
||||
// SDSC cards used byte addressing
|
||||
addr *= SD_SECTOR_SIZE;
|
||||
}
|
||||
return sd_cmd_write(24, addr, SD_SECTOR_SIZE, buffer);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
struct sd_context {
|
||||
size_t blocks;
|
||||
size_t blocksize;
|
||||
bool initialized;
|
||||
bool old_card;
|
||||
bool sdhc_sdxc;
|
||||
|
||||
65
software/src/hwconfig_Rev1.py
Normal file
65
software/src/hwconfig_Rev1.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
import machine
|
||||
from machine import Pin
|
||||
|
||||
# SD Card SPI
|
||||
SD_DI = Pin.board.GP3
|
||||
SD_DO = Pin.board.GP4
|
||||
SD_SCK = Pin.board.GP2
|
||||
SD_CS = Pin.board.GP5
|
||||
SD_CLOCKRATE = 16000000
|
||||
|
||||
# MAX98357
|
||||
I2S_LRCLK = Pin.board.GP6
|
||||
I2S_DCLK = Pin.board.GP7
|
||||
I2S_DIN = Pin.board.GP8
|
||||
I2S_SD = Pin.board.GP9
|
||||
|
||||
# RC522
|
||||
RC522_SPIID = 1
|
||||
RC522_RST = Pin.board.GP14
|
||||
RC522_IRQ = Pin.board.GP15
|
||||
RC522_MOSI = Pin.board.GP11
|
||||
RC522_MISO = Pin.board.GP12
|
||||
RC522_SCK = Pin.board.GP10
|
||||
RC522_SS = Pin.board.GP13
|
||||
|
||||
# WS2812
|
||||
LED_DIN = Pin.board.GP16
|
||||
LED_COUNT = 1
|
||||
|
||||
# Buttons
|
||||
BUTTON_VOLUP = Pin.board.GP17
|
||||
BUTTON_VOLDOWN = Pin.board.GP19
|
||||
BUTTON_NEXT = Pin.board.GP18
|
||||
BUTTON_POWER = Pin.board.GP21
|
||||
|
||||
# Power
|
||||
POWER_EN = Pin.board.GP22
|
||||
VBAT_ADC = Pin.board.GP26
|
||||
|
||||
|
||||
def board_init():
|
||||
# Keep power turned on
|
||||
# TODO: Implement soft power off
|
||||
POWER_EN.init(mode=Pin.OUT)
|
||||
POWER_EN.value(1)
|
||||
|
||||
# SD_DO / MISO input doesn't need any special configuration
|
||||
# Set 8 mA drive strength for SCK and MOSI
|
||||
machine.mem32[0x4001c004 + 2*4] = 0x71 # SCK
|
||||
machine.mem32[0x4001c004 + 3*4] = 0x71 # MOSI
|
||||
# SD_CS doesn't need any special configuration
|
||||
|
||||
# Permanently enable amplifier
|
||||
# TODO: Implement amplifier power management
|
||||
I2S_SD.init(mode=Pin.OPEN_DRAIN)
|
||||
I2S_SD.value(1)
|
||||
|
||||
|
||||
def get_battery_voltage():
|
||||
adc = machine.ADC(VBAT_ADC) # create ADC object on ADC pin
|
||||
battv = adc.read_u16()/65535.0*3.3*2
|
||||
return battv
|
||||
49
software/src/hwconfig_breadboard.py
Normal file
49
software/src/hwconfig_breadboard.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
from machine import Pin
|
||||
|
||||
# SD Card SPI
|
||||
SD_DI = Pin.board.GP3
|
||||
SD_DO = Pin.board.GP4
|
||||
SD_SCK = Pin.board.GP2
|
||||
SD_CS = Pin.board.GP5
|
||||
SD_CLOCKRATE = 15000000
|
||||
|
||||
# MAX98357
|
||||
I2S_LRCLK = Pin.board.GP7
|
||||
I2S_DCLK = Pin.board.GP6
|
||||
I2S_DIN = Pin.board.GP8
|
||||
I2S_SD = None
|
||||
|
||||
# RC522
|
||||
RC522_SPIID = 1
|
||||
RC522_RST = Pin.board.GP9
|
||||
RC522_IRQ = Pin.board.GP14
|
||||
RC522_MOSI = Pin.board.GP11
|
||||
RC522_MISO = Pin.board.GP12
|
||||
RC522_SCK = Pin.board.GP10
|
||||
RC522_SS = Pin.board.GP13
|
||||
|
||||
# WS2812
|
||||
LED_DIN = Pin.board.GP16
|
||||
LED_COUNT = 1
|
||||
|
||||
# Buttons
|
||||
BUTTON_VOLUP = Pin.board.GP17
|
||||
BUTTON_VOLDOWN = Pin.board.GP19
|
||||
BUTTON_NEXT = Pin.board.GP18
|
||||
BUTTON_POWER = None
|
||||
|
||||
# Power
|
||||
POWER_EN = None
|
||||
VBAT_ADC = Pin.board.GP26
|
||||
|
||||
|
||||
def board_init():
|
||||
pass
|
||||
|
||||
|
||||
def get_battery_voltage():
|
||||
# Not supported on breadboard
|
||||
return None
|
||||
@@ -6,7 +6,6 @@ import asyncio
|
||||
import machine
|
||||
import micropython
|
||||
import time
|
||||
from machine import Pin
|
||||
from math import pi, sin, pow
|
||||
|
||||
# Own modules
|
||||
@@ -18,14 +17,23 @@ from nfc import Nfc
|
||||
from rp2_neopixel import NeoPixel
|
||||
from utils import Buttons, SDContext, TimerManager
|
||||
|
||||
try:
|
||||
import hwconfig
|
||||
except ImportError:
|
||||
print("Fatal: No hwconfig.py found")
|
||||
raise
|
||||
|
||||
micropython.alloc_emergency_exception_buf(100)
|
||||
|
||||
# Machine setup
|
||||
hwconfig.board_init()
|
||||
|
||||
|
||||
async def rainbow(np, period=10):
|
||||
def gamma(value, X=2.2):
|
||||
return min(max(int(brightness * pow(value / 255.0, X) * 255.0 + 0.5), 0), 255)
|
||||
|
||||
brightness = 0.5
|
||||
brightness = 0.05
|
||||
count = 0.0
|
||||
leds = len(np)
|
||||
while True:
|
||||
@@ -34,7 +42,7 @@ async def rainbow(np, period=10):
|
||||
np[i] = (gamma((sin(ofs / leds * 2 * pi) + 1) * 127),
|
||||
gamma((sin(ofs / leds * 2 * pi + 2/3*pi) + 1) * 127),
|
||||
gamma((sin(ofs / leds * 2 * pi + 4/3*pi) + 1) * 127))
|
||||
count += 0.2
|
||||
count += 0.02 * leds
|
||||
before = time.ticks_ms()
|
||||
await np.async_write()
|
||||
now = time.ticks_ms()
|
||||
@@ -42,12 +50,6 @@ async def rainbow(np, period=10):
|
||||
await asyncio.sleep_ms(20 - (now - before))
|
||||
|
||||
|
||||
# Machine setup
|
||||
|
||||
# Set 8 mA drive strength and fast slew rate
|
||||
machine.mem32[0x4001c004 + 6*4] = 0x67
|
||||
machine.mem32[0x4001c004 + 7*4] = 0x67
|
||||
machine.mem32[0x4001c004 + 8*4] = 0x67
|
||||
# high prio for proc 1
|
||||
machine.mem32[0x40030000 + 0x00] = 0x10
|
||||
|
||||
@@ -55,21 +57,24 @@ machine.mem32[0x40030000 + 0x00] = 0x10
|
||||
def run():
|
||||
asyncio.new_event_loop()
|
||||
# Setup LEDs
|
||||
pin = Pin.board.GP16
|
||||
np = NeoPixel(pin, 10, sm=1)
|
||||
np = NeoPixel(hwconfig.LED_DIN, hwconfig.LED_COUNT, sm=1)
|
||||
asyncio.create_task(rainbow(np))
|
||||
|
||||
# Setup MP3 player
|
||||
with SDContext(mosi=Pin(3), miso=Pin(4), sck=Pin(2), ss=Pin(5), baudrate=15000000), \
|
||||
AudioContext(Pin(8), Pin(6)) as audioctx:
|
||||
with SDContext(mosi=hwconfig.SD_DI, miso=hwconfig.SD_DO, sck=hwconfig.SD_SCK, ss=hwconfig.SD_CS,
|
||||
baudrate=hwconfig.SD_CLOCKRATE), \
|
||||
AudioContext(hwconfig.I2S_DIN, hwconfig.I2S_DCLK, hwconfig.I2S_LRCLK) as audioctx:
|
||||
|
||||
# Setup NFC
|
||||
reader = MFRC522(spi_id=1, sck=10, miso=12, mosi=11, cs=13, rst=9, tocard_retries=20)
|
||||
reader = MFRC522(spi_id=hwconfig.RC522_SPIID, sck=hwconfig.RC522_SCK, miso=hwconfig.RC522_MISO,
|
||||
mosi=hwconfig.RC522_MOSI, cs=hwconfig.RC522_SS, rst=hwconfig.RC522_RST, tocard_retries=20)
|
||||
|
||||
# Setup app
|
||||
deps = app.Dependencies(mp3player=lambda the_app: MP3Player(audioctx, the_app),
|
||||
nfcreader=lambda the_app: Nfc(reader, the_app),
|
||||
buttons=lambda the_app: Buttons(the_app))
|
||||
buttons=lambda the_app: Buttons(the_app, pin_volup=hwconfig.BUTTON_VOLUP,
|
||||
pin_voldown=hwconfig.BUTTON_VOLDOWN,
|
||||
pin_next=hwconfig.BUTTON_NEXT))
|
||||
the_app = app.PlayerApp(deps)
|
||||
|
||||
# Start
|
||||
@@ -80,5 +85,5 @@ def run():
|
||||
|
||||
if __name__ == '__main__':
|
||||
if machine.Pin(17, machine.Pin.IN, machine.Pin.PULL_UP).value() != 0:
|
||||
time.sleep(5)
|
||||
time.sleep(1)
|
||||
run()
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
from utils.buttons import Buttons
|
||||
from utils.mbrpartition import MBRPartition
|
||||
from utils.pinindex import get_pin_index
|
||||
from utils.sdcontext import SDContext
|
||||
from utils.timer import TimerManager
|
||||
|
||||
__all__ = ["Buttons", "MBRPartition", "SDContext", "TimerManager"]
|
||||
__all__ = ["Buttons", "get_pin_index", "MBRPartition", "SDContext", "TimerManager"]
|
||||
|
||||
43
software/src/utils/pinindex.py
Normal file
43
software/src/utils/pinindex.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
from machine import Pin
|
||||
|
||||
pins = [Pin.board.GP0,
|
||||
Pin.board.GP1,
|
||||
Pin.board.GP2,
|
||||
Pin.board.GP3,
|
||||
Pin.board.GP4,
|
||||
Pin.board.GP5,
|
||||
Pin.board.GP6,
|
||||
Pin.board.GP7,
|
||||
Pin.board.GP8,
|
||||
Pin.board.GP9,
|
||||
Pin.board.GP10,
|
||||
Pin.board.GP11,
|
||||
Pin.board.GP12,
|
||||
Pin.board.GP13,
|
||||
Pin.board.GP14,
|
||||
Pin.board.GP15,
|
||||
Pin.board.GP16,
|
||||
Pin.board.GP17,
|
||||
Pin.board.GP18,
|
||||
Pin.board.GP19,
|
||||
Pin.board.GP20,
|
||||
Pin.board.GP21,
|
||||
Pin.board.GP22,
|
||||
None, # 23
|
||||
None, # 24
|
||||
None, # 25
|
||||
Pin.board.GP26,
|
||||
Pin.board.GP27,
|
||||
Pin.board.GP28,
|
||||
]
|
||||
|
||||
|
||||
def get_pin_index(pin: Pin) -> int:
|
||||
"""
|
||||
Get the pin index back from a pin object.
|
||||
Unfortunately, micropython has no built-in function for this.
|
||||
"""
|
||||
return pins.index(pin)
|
||||
@@ -1,2 +1,40 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
|
||||
|
||||
class Pin:
|
||||
def __init__(self, idx):
|
||||
self.idx = idx
|
||||
|
||||
board = None
|
||||
|
||||
|
||||
class Board:
|
||||
GP0 = Pin(0)
|
||||
GP1 = Pin(1)
|
||||
GP2 = Pin(2)
|
||||
GP3 = Pin(3)
|
||||
GP4 = Pin(4)
|
||||
GP5 = Pin(5)
|
||||
GP6 = Pin(6)
|
||||
GP7 = Pin(7)
|
||||
GP8 = Pin(8)
|
||||
GP9 = Pin(9)
|
||||
GP10 = Pin(10)
|
||||
GP11 = Pin(11)
|
||||
GP12 = Pin(12)
|
||||
GP13 = Pin(13)
|
||||
GP14 = Pin(14)
|
||||
GP15 = Pin(15)
|
||||
GP16 = Pin(16)
|
||||
GP17 = Pin(17)
|
||||
GP18 = Pin(18)
|
||||
GP19 = Pin(19)
|
||||
GP20 = Pin(20)
|
||||
GP21 = Pin(21)
|
||||
GP22 = Pin(22)
|
||||
GP26 = Pin(26)
|
||||
GP27 = Pin(27)
|
||||
GP28 = Pin(28)
|
||||
|
||||
|
||||
Pin.board = Board
|
||||
|
||||
Reference in New Issue
Block a user