Merge pull request #10369 from wee-noise-makers/noise-nugget-support

ports/raspberrypi: first version of the Noise Nugget board support
This commit is contained in:
Dan Halbert 2025-07-10 17:38:47 -04:00 committed by GitHub
commit e80c92cfaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 1169 additions and 0 deletions

View file

@ -8,3 +8,4 @@ i1Qb$TE"rl
ZEN = "the zen of python beautiful is better than ugly explicit is better than implicit simple is better than complex complex is better than complicated flat is better than nested sparse is better than dense readability counts special cases arent special enough to break the rules although practicality beats purity errors should never pass silently unless explicitly silenced in the face of ambiguity refuse the temptation to guess there should be one and preferably only one obvious way to do it although that way may not be obvious at first unless youre dutch now is better than never although never is often better than right now if the implementation is hard to explain its a bad idea if the implementation is easy to explain it may be a good idea namespaces are one honking great idea lets do more of those"
"arent",
"youre",
USB_MANUFACTURER = "Wee Noise Makers"

View file

@ -0,0 +1,564 @@
from adafruit_bus_device import i2c_device
import time
# Page select register
AIC3X_PAGE_SELECT = 0
# Software reset register
AIC3X_RESET = 1
# Codec Sample rate select register
AIC3X_SAMPLE_RATE_SEL_REG = 2
# PLL progrramming register A
AIC3X_PLL_PROGA_REG = 3
# PLL progrramming register B
AIC3X_PLL_PROGB_REG = 4
# PLL progrramming register C
AIC3X_PLL_PROGC_REG = 5
# PLL progrramming register D
AIC3X_PLL_PROGD_REG = 6
# Codec datapath setup register
AIC3X_CODEC_DATAPATH_REG = 7
# Audio serial data interface control register A
AIC3X_ASD_INTF_CTRLA = 8
# Audio serial data interface control register B
AIC3X_ASD_INTF_CTRLB = 9
# Audio serial data interface control register C
AIC3X_ASD_INTF_CTRLC = 10
# Audio overflow status and PLL R value programming register
AIC3X_OVRF_STATUS_AND_PLLR_REG = 11
# Audio codec digital filter control register
AIC3X_CODEC_DFILT_CTRL = 12
# Headset/button press detection register
AIC3X_HEADSET_DETECT_CTRL_A = 13
AIC3X_HEADSET_DETECT_CTRL_B = 14
# ADC PGA Gain control registers
LADC_VOL = 15
RADC_VOL = 16
# MIC3 control registers
MIC3LR_2_LADC_CTRL = 17
MIC3LR_2_RADC_CTRL = 18
# Line1 Input control registers
LINE1L_2_LADC_CTRL = 19
LINE1R_2_LADC_CTRL = 21
LINE1R_2_RADC_CTRL = 22
LINE1L_2_RADC_CTRL = 24
# Line2 Input control registers
LINE2L_2_LADC_CTRL = 20
LINE2R_2_RADC_CTRL = 23
# MICBIAS Control Register
MICBIAS_CTRL = 25
# AGC Control Registers A, B, C
LAGC_CTRL_A = 26
LAGC_CTRL_B = 27
LAGC_CTRL_C = 28
RAGC_CTRL_A = 29
RAGC_CTRL_B = 30
RAGC_CTRL_C = 31
# DAC Power and Left High Power Output control registers
DAC_PWR = 37
HPLCOM_CFG = 37
# Right High Power Output control registers
HPRCOM_CFG = 38
# High Power Output Stage Control Register
HPOUT_SC = 40
# DAC Output Switching control registers
DAC_LINE_MUX = 41
# High Power Output Driver Pop Reduction registers
HPOUT_POP_REDUCTION = 42
# DAC Digital control registers
LDAC_VOL = 43
RDAC_VOL = 44
# Left High Power Output control registers
LINE2L_2_HPLOUT_VOL = 45
PGAL_2_HPLOUT_VOL = 46
DACL1_2_HPLOUT_VOL = 47
LINE2R_2_HPLOUT_VOL = 48
PGAR_2_HPLOUT_VOL = 49
DACR1_2_HPLOUT_VOL = 50
HPLOUT_CTRL = 51
# Left High Power COM control registers
LINE2L_2_HPLCOM_VOL = 52
PGAL_2_HPLCOM_VOL = 53
DACL1_2_HPLCOM_VOL = 54
LINE2R_2_HPLCOM_VOL = 55
PGAR_2_HPLCOM_VOL = 56
DACR1_2_HPLCOM_VOL = 57
HPLCOM_CTRL = 58
# Right High Power Output control registers
LINE2L_2_HPROUT_VOL = 59
PGAL_2_HPROUT_VOL = 60
DACL1_2_HPROUT_VOL = 61
LINE2R_2_HPROUT_VOL = 62
PGAR_2_HPROUT_VOL = 63
DACR1_2_HPROUT_VOL = 64
HPROUT_CTRL = 65
# Right High Power COM control registers
LINE2L_2_HPRCOM_VOL = 66
PGAL_2_HPRCOM_VOL = 67
DACL1_2_HPRCOM_VOL = 68
LINE2R_2_HPRCOM_VOL = 69
PGAR_2_HPRCOM_VOL = 70
DACR1_2_HPRCOM_VOL = 71
HPRCOM_CTRL = 72
# Mono Line Output Plus/Minus control registers
LINE2L_2_MONOLOPM_VOL = 73
PGAL_2_MONOLOPM_VOL = 74
DACL1_2_MONOLOPM_VOL = 75
LINE2R_2_MONOLOPM_VOL = 76
PGAR_2_MONOLOPM_VOL = 77
DACR1_2_MONOLOPM_VOL = 78
MONOLOPM_CTRL = 79
# Left Line Output Plus/Minus control registers
LINE2L_2_LLOPM_VOL = 80
PGAL_2_LLOPM_VOL = 81
DACL1_2_LLOPM_VOL = 82
LINE2R_2_LLOPM_VOL = 83
PGAR_2_LLOPM_VOL = 84
DACR1_2_LLOPM_VOL = 85
LLOPM_CTRL = 86
# Right Line Output Plus/Minus control registers
LINE2L_2_RLOPM_VOL = 87
PGAL_2_RLOPM_VOL = 88
DACL1_2_RLOPM_VOL = 89
LINE2R_2_RLOPM_VOL = 90
PGAR_2_RLOPM_VOL = 91
DACR1_2_RLOPM_VOL = 92
RLOPM_CTRL = 93
MODULE_POWER_STATUS = 94
# GPIO/IRQ registers
AIC3X_STICKY_IRQ_FLAGS_REG = 96
AIC3X_RT_IRQ_FLAGS_REG = 97
AIC3X_CLOCK_REG = 101
# Clock generation control register
AIC3X_CLKGEN_CTRL_REG = 102
# New AGC registers
LAGCN_ATTACK = 103
LAGCN_DECAY = 104
RAGCN_ATTACK = 105
RAGCN_DECAY = 106
# New Programmable ADC Digital Path and I2C Bus Condition Register
NEW_ADC_DIGITALPATH = 107
# Passive Analog Signal Bypass Selection During Powerdown Register
PASSIVE_BYPASS = 108
# DAC Quiescent Current Adjustment Register
DAC_ICC_ADJ = 109
# My kingdom for enums, and strong typing...
LINE2_L = 0
PGA_L = 1
DAC_L1 = 2
LINE2_R = 3
PGA_R = 4
DAC_R1 = 5
# Don't use the same values for the sink and source "enums", this way a runtime
# error will tell us when we mix the two.
HP_L_OUT = 10
HP_L_COM = 11
HP_R_OUT = 12
HP_R_COM = 13
LINE_OUT_L = 14
LINE_OUT_R = 15
class AIC3105:
def __init__(self, i2c_bus, sample_rate, address=0x18):
self.i2c_device = i2c_device.I2CDevice(i2c_bus, address)
self._buf = bytearray(2)
self._sample_rate = sample_rate
self._registers_copy = [
0b00000000, # 0
0b00000000, # 1
0b00000000, # 2
0b00010000, # 3
0b00000010, # 4
0b00000000, # 5
0b00000000, # 6
0b00000000, # 7
0b00000000, # 8
0b00000000, # 9
0b00000000, # 10
0b00000001, # 11
0b00000000, # 12
0b00000000, # 13
0b00000000, # 14
0b10000000, # 15
0b10000000, # 16
0b11111111, # 17
0b11111111, # 18
0b01111000, # 19
0b01111000, # 20
0b01111000, # 21
0b01111000, # 22
0b01111000, # 23
0b01111000, # 24
0b00000000, # 25
0b00000000, # 26
0b11111110, # 27
0b00000000, # 28
0b00000000, # 29
0b11111110, # 30
0b00000000, # 31
0b00000000, # 32
0b00000000, # 33
0b00000000, # 34
0b00000000, # 35
0b00000000, # 36
0b00000000, # 37
0b00000000, # 38
0b00000000, # 39
0b00000000, # 40
0b00000000, # 41
0b00000000, # 42
0b10000000, # 43
0b10000000, # 44
0b00000000, # 45
0b00000000, # 46
0b00000000, # 47
0b00000000, # 48
0b00000000, # 49
0b00000000, # 50
0b00000110, # 51
0b00000000, # 52
0b00000000, # 53
0b00000000, # 54
0b00000000, # 55
0b00000000, # 56
0b00000000, # 57
0b00000110, # 58
0b00000000, # 59
0b00000000, # 60
0b00000000, # 61
0b00000000, # 62
0b00000000, # 63
0b00000000, # 64
0b00000010, # 65
0b00000000, # 66
0b00000000, # 67
0b00000000, # 68
0b00000000, # 69
0b00000000, # 70
0b00000000, # 71
0b00000010, # 72
0b00000000, # 73
0b00000000, # 74
0b00000000, # 75
0b00000000, # 76
0b00000000, # 77
0b00000000, # 78
0b00000000, # 79
0b00000000, # 80
0b00000000, # 81
0b00000000, # 82
0b00000000, # 83
0b00000000, # 84
0b00000000, # 85
0b00000010, # 86
0b00000000, # 87
0b00000000, # 88
0b00000000, # 89
0b00000000, # 90
0b00000000, # 91
0b00000000, # 92
0b00000010, # 93
0b00000000, # 94
0b00000000, # 95
0b00000000, # 96
0b00000000, # 97
0b00000000, # 98
0b00000000, # 99
0b00000000, # 100
0b00000000, # 101
0b00000010, # 102
0b00000000, # 103
0b00000000, # 104
0b00000000, # 105
0b00000000, # 106
0b00000000, # 107
0b00000000, # 108
0b00000000,
] # 109
def _write_register(self, reg, value):
self._buf[0] = reg & 0xFF
self._buf[1] = value & 0xFF
# print("_write_register reg:0x{:02x} value:0x{:02x}".format(reg, value))
with self.i2c_device as i2c:
i2c.write(self._buf)
self._registers_copy[reg] = value
def _write_bit(self, reg, pos, value):
current = self._registers_copy[reg]
mask = 1 << pos
if value == 0:
new = current & (~mask)
else:
new = current | mask
self._write_register(reg, new)
def _write_multi(self, reg, msb, lsb, value):
new = self._registers_copy[reg]
# Clear bits for the range we case about
for x in range(lsb, msb + 1):
new = new & (~(1 << x))
new = new | (value << lsb)
self._write_register(reg, new)
def sink_base_register(self, sink):
if sink == HP_L_OUT:
return 45
if sink == HP_L_COM:
return 52
if sink == HP_R_OUT:
return 59
if sink == HP_R_COM:
return 66
if sink == LINE_OUT_L:
return 80
if sink == LINE_OUT_R:
return 87
raise RuntimeError("invalid sink")
def source_register_offset(self, source):
if source == LINE2_L:
return 0
if source == PGA_L:
return 1
if source == DAC_L1:
return 2
if source == LINE2_R:
return 3
if source == PGA_R:
return 4
if source == DAC_R1:
return 5
raise RuntimeError("invalid source")
def power_on(self, sink):
reg = self.sink_base_register(sink) + 6
self._write_bit(reg, 0, 1)
def power_off(self, sink):
reg = self.sink_base_register(sink) + 6
self._write_bit(reg, 0, 0)
def mute(self, sink):
reg = self.sink_base_register(sink) + 6
self._write_bit(reg, 3, 0)
def unmute(self, sink):
reg = self.sink_base_register(sink) + 6
self._write_bit(reg, 3, 1)
def route(self, source, sink):
reg = self.sink_base_register(sink) + self.source_register_offset(source)
self._write_bit(reg, 7, 1)
def unroute(self, source, sink):
reg = self.sink_base_register(sink) + self.source_register_offset(source)
self._write_bit(reg, 7, 0)
def volume_convert(self, volume, min, max, mute_value):
if volume == 0.0 or volume < 0.0:
return mute_value
elif volume > 1.0:
volume = 1.0
if max > min:
amplitude = max - min
val = volume * amplitude
return int(min + val)
else:
amplitude = min - max
val = (1.0 - volume) * amplitude
return int(max + val)
def PGA_volume(self, volume):
return self.volume_convert(volume, 0, 0b1111111, 0)
def output_stage_volume(self, volume):
return self.volume_convert(volume, 117, 0, 118)
def set_volume(self, source, sink, volume):
vol = self.output_stage_volume(volume)
reg = self.sink_base_register(sink) + self.source_register_offset(source)
self._write_multi(reg, 6, 0, vol)
def set_HP_volume(self, left, right):
self.set_volume(DAC_L1, HP_L_OUT, left)
self.set_volume(DAC_R1, HP_R_OUT, right)
def enable_line_out(self, left, right):
if left:
self.power_on(LINE_OUT_L)
self.route(DAC_L1, LINE_OUT_L)
self.route(DAC_L1, LINE_OUT_R)
self.unmute(LINE_OUT_L)
else:
self.power_off(LINE_OUT_L)
self.unroute(DAC_L1, LINE_OUT_L)
self.unroute(DAC_L1, LINE_OUT_R)
self.mute(LINE_OUT_L)
if right:
self.power_on(LINE_OUT_R)
self.route(DAC_R1, LINE_OUT_L)
self.route(DAC_R1, LINE_OUT_R)
self.unmute(LINE_OUT_R)
else:
self.power_off(LINE_OUT_R)
self.unroute(DAC_R1, LINE_OUT_L)
self.unroute(DAC_R1, LINE_OUT_R)
self.mute(LINE_OUT_R)
def set_line_out_volume(
self, left_to_left, right_to_right, left_to_right=0.0, right_to_left=0.0
):
self.set_volume(DAC_L1, LINE_OUT_L, left_to_left)
self.set_volume(DAC_L1, LINE_OUT_R, left_to_right)
self.set_volume(DAC_R1, LINE_OUT_R, right_to_right)
self.set_volume(DAC_R1, LINE_OUT_L, right_to_left)
def enable_mic_bias(self):
self._write_multi(MICBIAS_CTRL, 7, 6, 0b10)
def start_i2s_out(self):
if self._sample_rate == 8000:
cfg = [5, 4613, 1, 8]
elif self._sample_rate == 16000:
cfg = [5, 4613, 1, 4]
elif self._sample_rate == 22050:
cfg = [7, 5264, 1, 4]
elif self._sample_rate == 32000:
cfg = [5, 4613, 1, 2]
elif self._sample_rate == 44100:
cfg = [7, 5264, 1, 2]
elif self._sample_rate == 48000:
cfg = [8, 1920, 1, 2]
else:
raise RuntimeError("invalid sample rate: " + str(self._sample_rate))
J = cfg[0]
D = cfg[1]
R = cfg[2]
P = cfg[3]
# Select Page 0
self._write_bit(AIC3X_PAGE_SELECT, 0, 0)
# Soft reset
self._write_bit(AIC3X_RESET, 7, 1)
# Let's start with clock configuration.
# PLL P = 2
self._write_multi(AIC3X_PLL_PROGA_REG, 2, 0, P)
# PLL R = 1
self._write_multi(AIC3X_OVRF_STATUS_AND_PLLR_REG, 3, 0, R)
# PLL J = 7
self._write_multi(AIC3X_PLL_PROGB_REG, 7, 2, J)
# PLL D = 5264
PLL_D = D
REG_C = PLL_D >> 6
REG_D = PLL_D & 0x3F
self._write_multi(AIC3X_PLL_PROGC_REG, 7, 0, REG_C)
self._write_multi(AIC3X_PLL_PROGD_REG, 7, 2, REG_D)
# Select the PLLCLK_IN source. 0: MCLK, 1: GPIO2, 2: BCLK
self._write_multi(AIC3X_CLKGEN_CTRL_REG, 5, 4, 2)
# Select the CLKDIV_IN source. 0: MCLK, 1: GPIO2, 2: BCLK
#
# Note: When PLL is used CLKDIV_IN still needs some kind of clock
# signal. So if there's no MCLK, BCLK should be used here as well
self._write_multi(AIC3X_CLKGEN_CTRL_REG, 6, 7, 0)
# Enable PLL
self._write_bit(AIC3X_PLL_PROGA_REG, 7, 1)
# Set FS(ref) value for AGC time constants to 44.1KHz
self._write_bit(AIC3X_CODEC_DATAPATH_REG, 7, 1)
# CODEC_CLKIN Source Selection. 0: PLLDIV_OUT. 1: CLKDIV_OUT
self._write_bit(AIC3X_CLOCK_REG, 0, 0)
# Note: We leave the ADC Sample Rate Select and DAC Sample Rate Select
# at the default value: fs(ref) / 1
# Audio Serial Data Interface at the default settings: I2S
# mode, 16bits words,
self._write_multi(AIC3X_ASD_INTF_CTRLB, 7, 6, 0b00)
# Output Driver Power-On Delay Control
self._write_multi(HPOUT_POP_REDUCTION, 7, 4, 0b1000)
# Driver Ramp-Up Step Timing Control
self._write_multi(HPOUT_POP_REDUCTION, 3, 2, 0b01)
# Power outputs
self.power_on(HP_L_OUT)
self.power_on(HP_R_OUT)
# L and R DACs Power On
self._write_multi(DAC_PWR, 7, 6, 0b11)
# Left DAC plays left input data
self._write_multi(AIC3X_CODEC_DATAPATH_REG, 4, 3, 0b01)
# Right DAC plays right input data
self._write_multi(AIC3X_CODEC_DATAPATH_REG, 2, 1, 0b01)
# Unmute L DAC
self._write_bit(LDAC_VOL, 7, 0)
# Unmute R DAC
self._write_bit(RDAC_VOL, 7, 0)
# Left-DAC output selects DAC_L1 path.
self._write_multi(DAC_LINE_MUX, 7, 6, 0)
# Right-DAC output selects DAC_R1 path.
self._write_multi(DAC_LINE_MUX, 5, 4, 0)
# DAC to HP
self.route(DAC_L1, HP_L_OUT)
self.route(DAC_R1, HP_R_OUT)
# DAC to Line-Out
self.route(DAC_L1, LINE_OUT_L)
self.route(DAC_R1, LINE_OUT_R)
# Enable Left ADC
self._write_bit(LINE1L_2_LADC_CTRL, 2, 1)
# Enable Right ADC
self._write_bit(LINE1R_2_RADC_CTRL, 2, 1)
# Unmute L ADC PGA
self._write_bit(LADC_VOL, 7, 0)
# Unmute R ADC PGA
self._write_bit(RADC_VOL, 7, 0)
# Programs high-power outputs for ac-coupled driver configuration
self._write_bit(AIC3X_HEADSET_DETECT_CTRL_B, 7, 1)
# HPLCOM configured as independent single-ended output
self._write_multi(HPLCOM_CFG, 5, 4, 2)
# HPRCOM configured as independent single-ended output
self._write_multi(HPRCOM_CFG, 5, 3, 1)
# Unmute outputs
self.unmute(HP_L_OUT)
self.unmute(HP_R_OUT)

View file

@ -0,0 +1,65 @@
from adafruit_bus_device import i2c_device
IO_EXP_SPK_Enable_L_Mask = 0b00000001
IO_EXP_SPK_Enable_R_Mask = 0b00000010
IO_EXP_SPK_Gain_0_Mask = 0b00000100
IO_EXP_SPK_Gain_1_Mask = 0b00001000
IO_EXP_DAC_Not_Reset_Mask = 0b00010000
IO_EXP_Jack_Detect_Mask = 0b00100000
IO_EXP_INPUT_REG = 0
IO_EXP_OUTPUT_REG = 1
IO_EXP_CONFIG_REG = 3
IO_EXP_CONFIG_REG_INIT = IO_EXP_Jack_Detect_Mask
IO_EXP_OUTPUT_REG_INIT = 0b00000000
class TCA6408:
def __init__(self, i2c_bus, address=0x20):
self._i2c_device = i2c_device.I2CDevice(i2c_bus, address)
self._buf = bytearray(2)
self._reg_state = IO_EXP_OUTPUT_REG_INIT
self._write_register(IO_EXP_OUTPUT_REG, self._reg_state)
self._write_register(IO_EXP_CONFIG_REG, IO_EXP_CONFIG_REG_INIT)
def _write_register(self, reg, value):
self._buf[0] = reg & 0xFF
self._buf[1] = value & 0xFF
with self._i2c_device as i2c:
i2c.write(self._buf)
def _set_out(self, new_state):
self._write_register(IO_EXP_OUTPUT_REG, new_state)
self._reg_state = new_state
def enable_codec(self):
self._set_out(self._reg_state | IO_EXP_DAC_Not_Reset_Mask)
def enable_speakers(self, left, right):
new_state = self._reg_state
if left:
new_state = new_state | IO_EXP_SPK_Enable_L_Mask
else:
new_state = new_state & ~IO_EXP_SPK_Enable_L_Mask
if right:
new_state = new_state | IO_EXP_SPK_Enable_R_Mask
else:
new_state = new_state & ~IO_EXP_SPK_Enable_R_Mask
self._set_out(new_state)
def set_speakers_gain(self, g0, g1):
new_state = self._reg_state
if g0:
new_state = new_state | IO_EXP_SPK_Gain_0_Mask
else:
new_state = new_state & ~IO_EXP_SPK_Gain_0_Mask
if g1:
new_state = new_state | IO_EXP_SPK_Gain_1_Mask
else:
new_state = new_state & ~IO_EXP_SPK_Gain_1_Mask

View file

@ -0,0 +1,29 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "supervisor/board.h"
// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.

View file

@ -0,0 +1,2 @@
#define MICROPY_HW_BOARD_NAME "Noise Nugget 2040"
#define MICROPY_HW_MCU_NAME "rp2040"

View file

@ -0,0 +1,18 @@
USB_VID = 0x2E8A
USB_PID = 0x1090
USB_PRODUCT = "Noise Nugget 2040"
USB_MANUFACTURER = "Wee Noise Makers"
CHIP_VARIANT = RP2040
CHIP_FAMILY = rp2
EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ"
CIRCUITPY__EVE = 1
FROZEN_MPY_DIRS += $(TOP)/ports/raspberrypi/boards/weenoisemakers_noisenugget
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ImageLoad
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MIDI

View file

@ -0,0 +1,115 @@
from board import *
import audiobusio
import busio
import time
import _aic3105
import _tca6408
_AUDIO = None
_DAC = None
_I2C = None
_IO_EXP = None
_SAMPLE_RATE = 22050
def io_exp():
global _IO_EXP
if not _IO_EXP:
_IO_EXP = _tca6408.TCA6408(i2c())
return _IO_EXP
def i2c():
global _I2C
if not _I2C:
SCL = GP7
SDA = GP6
_I2C = busio.I2C(SCL, SDA)
return _I2C
def _init_dac(sample_rate):
global _DAC
global _SAMPLE_RATE
if not _DAC:
_SAMPLE_RATE = sample_rate
_DAC = _aic3105.AIC3105(i2c(), sample_rate)
_DAC.start_i2s_out()
return _DAC
def audio(sample_rate=22050):
global _AUDIO
global _DAC
if not _AUDIO:
i2s_out = GP1
i2s_lrclk = GP2
i2s_bclk = GP3
io_exp().enable_codec()
time.sleep(0.1)
# Must init the dac here
_init_dac(sample_rate)
_DAC.set_HP_volume(0.5, 0.5)
_AUDIO = audiobusio.I2SOut(i2s_bclk, i2s_lrclk, i2s_out, left_justified=False)
return _AUDIO
def sample_rate():
return _SAMPLE_RATE
def _check_dac_init():
global _DAC
if not _DAC:
raise RuntimeError("audio not initialized. Call noise_nugget.audio() first.")
def set_HP_volume(left, right):
global _DAC
_check_dac_init()
_DAC.set_HP_volume(left, right)
def enable_speakers(left, right):
global _DAC
_check_dac_init()
_DAC.enable_line_out(left, right)
io_exp().enable_speakers(left, right)
def set_speakers_volume(left_to_left, right_to_right, left_to_right=0.0, right_to_left=0.0):
global _DAC
_check_dac_init()
_DAC.set_line_out_volume(left_to_left, right_to_right, left_to_right, right_to_left)
def set_speakers_gain(gain):
if gain == 0:
io_exp().set_speakers_gain(False, False)
elif gain == 1:
io_exp().set_speakers_gain(True, False)
elif gain == 2:
io_exp().set_speakers_gain(False, True)
elif gain == 3:
io_exp().set_speakers_gain(True, True)
else:
raise RuntimeError("invalid speaker gain " + str(gain) + " (must be between 0 and 3)")

View file

@ -0,0 +1,320 @@
from board import *
import busio
import displayio
import adafruit_displayio_ssd1306
from fourwire import FourWire
from neopixel import NeoPixel
import pwmio
from digitalio import DigitalInOut, Direction, Pull
_DISPLAY = None
_LEDS = None
_KEYBOARD_STATE = 0
_KEYBOARD_PREV_STATE = 0
VOLTAGE_MONITOR = A2
BATTERY = A2
K_TRACK = 0x10000000
K_STEP = 0x08000000
K_PLAY = 0x02000000
K_REC = 0x04000000
K_ALT = 0x00040000
K_PATT = 0x00001000
K_SONG = 0x00000040
K_MENU = 0x00000020
K_UP = 0x00020000
K_DOWN = 0x00800000
K_RIGHT = 0x00000800
K_LEFT = 0x20000000
K_A = 0x01000000
K_B = 0x00000001
K_1 = 0x00400000
K_2 = 0x00010000
K_3 = 0x00000400
K_4 = 0x00000010
K_5 = 0x00000002
K_6 = 0x00000080
K_7 = 0x00002000
K_8 = 0x00080000
K_9 = 0x00200000
K_10 = 0x00008000
K_11 = 0x00000200
K_12 = 0x00000008
K_13 = 0x00000004
K_14 = 0x00000100
K_15 = 0x00004000
K_16 = 0x00100000
KEY_LIST = [
K_TRACK,
K_STEP,
K_PLAY,
K_REC,
K_ALT,
K_PATT,
K_SONG,
K_MENU,
K_UP,
K_DOWN,
K_RIGHT,
K_LEFT,
K_A,
K_B,
K_1,
K_2,
K_3,
K_4,
K_5,
K_6,
K_7,
K_8,
K_9,
K_10,
K_11,
K_12,
K_13,
K_14,
K_15,
K_16,
]
KEY_NAME = {
K_TRACK: "Track",
K_STEP: "Step",
K_PLAY: "Play",
K_REC: "Rec",
K_ALT: "Alt",
K_PATT: "Pattern",
K_SONG: "Song",
K_MENU: "Menu",
K_UP: "Up",
K_DOWN: "Down",
K_RIGHT: "Right",
K_LEFT: "Left",
K_A: "A",
K_B: "B",
K_1: "1",
K_2: "2",
K_3: "3",
K_4: "4",
K_5: "5",
K_6: "6",
K_7: "7",
K_8: "8",
K_9: "9",
K_10: "10",
K_11: "11",
K_12: "12",
K_13: "13",
K_14: "14",
K_15: "15",
K_16: "16",
}
LED_MENU = 0
LED_SONG = 1
LED_PATT = 2
LED_ALT = 3
LED_TRACK = 4
LED_K_1 = 5
LED_K_2 = 6
LED_K_3 = 7
LED_K_4 = 8
LED_K_5 = 9
LED_K_6 = 10
LED_K_7 = 11
LED_K_8 = 12
LED_PLAY = 13
LED_STEP = 14
LED_K_9 = 15
LED_K_10 = 16
LED_K_11 = 17
LED_K_12 = 18
LED_K_13 = 19
LED_K_14 = 20
LED_K_15 = 21
LED_K_16 = 22
LED_REC = 23
_KEY_COL = [
DigitalInOut(GP21),
DigitalInOut(GP22),
DigitalInOut(GP26),
DigitalInOut(GP23),
DigitalInOut(GP29),
]
_KEY_ROW = [
DigitalInOut(GP20),
DigitalInOut(GP18),
DigitalInOut(GP19),
DigitalInOut(GP24),
DigitalInOut(GP25),
DigitalInOut(GP27),
]
for pin in _KEY_COL:
pin.direction = Direction.OUTPUT
pin.value = False
for pin in _KEY_ROW:
pin.direction = Direction.INPUT
pin.pull = Pull.DOWN
def scan_keyboard():
global _KEYBOARD_STATE
global _KEYBOARD_PREV_STATE
val = 0
for col in _KEY_COL:
col.value = True
for row in _KEY_ROW:
val = val << 1
if row.value:
val = val | 1
col.value = False
_KEYBOARD_PREV_STATE = _KEYBOARD_STATE
_KEYBOARD_STATE = val
def pressed(keys):
global _KEYBOARD_STATE
return (_KEYBOARD_STATE & keys) != 0
def falling(keys):
global _KEYBOARD_STATE
global _KEYBOARD_PREV_STATE
all_falling_keys = _KEYBOARD_STATE & ~_KEYBOARD_PREV_STATE
return (all_falling_keys & keys) != 0
def raising(keys):
global _KEYBOARD_STATE
global _KEYBOARD_PREV_STATE
all_raising_keys = ~_KEYBOARD_STATE & _KEYBOARD_PREV_STATE
return (all_raising_keys & keys) != 0
def display(spi_frequency=1000000):
global _DISPLAY
if not _DISPLAY:
displayio.release_displays()
spi = busio.SPI(GP10, GP11)
cs = None
dc = GP12
reset = GP13
display_bus = FourWire(
spi, command=dc, chip_select=cs, reset=reset, baudrate=spi_frequency
)
_DISPLAY = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=64)
return _DISPLAY
def neopixel(brightness=0.1):
global _LEDS
if not _LEDS:
_LEDS = NeoPixel(GP14, 24, brightness=brightness)
return _LEDS
def key_to_led(key):
map = {
K_TRACK: LED_TRACK,
K_STEP: LED_STEP,
K_PLAY: LED_PLAY,
K_REC: LED_REC,
K_ALT: LED_ALT,
K_PATT: LED_PATT,
K_SONG: LED_SONG,
K_MENU: LED_MENU,
K_1: LED_K_1,
K_2: LED_K_2,
K_3: LED_K_3,
K_4: LED_K_4,
K_5: LED_K_5,
K_6: LED_K_6,
K_7: LED_K_7,
K_8: LED_K_8,
K_9: LED_K_9,
K_10: LED_K_10,
K_11: LED_K_11,
K_12: LED_K_12,
K_13: LED_K_13,
K_14: LED_K_14,
K_15: LED_K_15,
K_16: LED_K_16,
}
return map[key]
def simple_synth(synth, verbose=False):
base_note = 60
leds = neopixel()
keys = [
(K_9, 0),
(K_2, 1),
(K_10, 2),
(K_3, 3),
(K_11, 4),
(K_12, 5),
(K_5, 6),
(K_13, 7),
(K_6, 8),
(K_14, 9),
(K_7, 10),
(K_15, 11),
(K_16, 12),
]
for key, _ in keys:
leds[key_to_led(key)] = (0, 0, 255)
leds[key_to_led(K_1)] = (255, 255, 0)
leds[key_to_led(K_8)] = (255, 255, 0)
leds.show()
while True:
scan_keyboard()
# Lower base note 1 octave down
if falling(K_1) and base_note > 12:
# Turn off any potentially on notes
for _, offset in keys:
synth.release(base_note + offset)
base_note -= 12
if verbose:
print("Octave down")
# Raise base note 1 octave up
if falling(K_8) and base_note <= 96:
# Turn off any potentially on notes
for _, offset in keys:
synth.release(base_note + offset)
base_note += 12
if verbose:
print("Octave up")
for key, offset in keys:
note = base_note + offset
if falling(key):
if verbose:
print("On " + str(note))
synth.press(note)
if raising(key):
if verbose:
print("Off " + str(note))
synth.release(note)

View file

@ -0,0 +1 @@
// Put board-specific pico-sdk definitions here. This file must exist.

View file

@ -0,0 +1,54 @@
#include "shared-bindings/board/__init__.h"
const mcu_pin_obj_t pin_GPIO_fake;
static const mp_rom_map_elem_t board_module_globals_table[] = {
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
{ MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) },
{ MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) },
{ MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) },
{ MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) },
{ MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) },
{ MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) },
{ MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) },
{ MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) },
{ MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) },
{ MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) },
{ MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) },
{ MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) },
{ MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) },
{ MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) },
{ MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) },
{ MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) },
{ MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) },
{ MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) },
{ MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) },
{ MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) },
{ MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) },
{ MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) },
{ MP_ROM_QSTR(MP_QSTR_GP23), MP_ROM_PTR(&pin_GPIO23) },
{ MP_ROM_QSTR(MP_QSTR_GP24), MP_ROM_PTR(&pin_GPIO24) },
{ MP_ROM_QSTR(MP_QSTR_GP25), MP_ROM_PTR(&pin_GPIO25) },
{ MP_ROM_QSTR(MP_QSTR_GP26_A0), MP_ROM_PTR(&pin_GPIO26) },
{ MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) },
{ MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) },
{ MP_ROM_QSTR(MP_QSTR_GP27_A1), MP_ROM_PTR(&pin_GPIO27) },
{ MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) },
{ MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) },
{ MP_ROM_QSTR(MP_QSTR_GP28_A2), MP_ROM_PTR(&pin_GPIO28) },
{ MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) },
{ MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) },
{ MP_ROM_QSTR(MP_QSTR_GP29_A3), MP_ROM_PTR(&pin_GPIO29) },
{ MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) },
{ MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) },
};
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);