Skip to content

Commit

Permalink
Merge #551 Preamp Rev4 support, firmware v1.7
Browse files Browse the repository at this point in the history
  • Loading branch information
Lohrer committed Dec 8, 2023
2 parents b7cb4dd + 63dde4e commit dd618f0
Show file tree
Hide file tree
Showing 96 changed files with 2,425 additions and 19,110 deletions.
2 changes: 2 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
BasedOnStyle: Google

ColumnLimit: 100

AlignConsecutiveAssignments: Consecutive
AlignConsecutiveBitFields: true
AlignConsecutiveDeclarations: Consecutive
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
branches: [ main, develop ]

jobs:
build:
Expand All @@ -15,8 +15,10 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get -y install gcc-arm-none-eabi
sudo apt-get -y install cmake
wget https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz
sudo tar -xf arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz -C /usr/share
sudo ln -s /usr/share/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-* /usr/bin
- name: Build Preamp
run: |
Expand Down
13 changes: 7 additions & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@
"**/.git/subtree-cache/**": true,
"**/venv/lib/**": true
},
"[cpp]": {
"[c][cpp]": {
"editor.formatOnSave": true,
"editor.rulers": [80]
},
"[c]": {
"editor.formatOnSave": true,
"editor.rulers": [80]
"editor.rulers": [100]
},
"C_Cpp.default.cStandard": "c23",
"files.associations": {
// The C/C++ extension treats *.h files as "non-standard" C files,
// and updates this list every time a .h file is opened.
Expand All @@ -35,5 +32,9 @@
"[python]": {
"editor.defaultFormatter": "ms-python.autopep8"
},
"markdownlint.config": {
"default": true,
"MD033": { "allowed_elements": ["table", "thead", "tbody", "th", "tr", "td", "b"] },
},
"python.formatting.provider": "none"
}
2 changes: 1 addition & 1 deletion amplipi/audiodetector/audiodetector.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* AmpliPi Home Audio
* Copyright (C) 2022 MicroNova LLC
* Copyright (C) 2023 MicroNova LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down
6 changes: 3 additions & 3 deletions amplipi/ctrl.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class Api:


# TODO: migrate to init setting instance vars to a disconnected state (API requests will throw Api.DisconnectedException() in this state
# with this reinit will be called connect and will attempt to load the configutation and connect to an AmpliPi (mocked or real)
# with this reinit will be called connect and will attempt to load the configuration and connect to an AmpliPi (mocked or real)
# returning a boolean on whether or not it was successful
def __init__(self, settings: models.AppSettings = models.AppSettings(), change_notifier: Optional[Callable[[models.Status], None]] = None):
self.reinit(settings, change_notifier)
Expand All @@ -113,7 +113,7 @@ def __init__(self, settings: models.AppSettings = models.AppSettings(), change_n
def reinit(self, settings: models.AppSettings = models.AppSettings(), change_notifier: Optional[Callable[[models.Status], None]] = None, config: Optional[models.Status] = None):
""" Initialize or Reinitialize the controller
Intitializes the system to to base configuration """
Initializes the system to to base configuration """
self._change_notifier = change_notifier
self._mock_hw = settings.mock_ctrl
self._mock_streams = settings.mock_streams
Expand All @@ -123,7 +123,7 @@ def reinit(self, settings: models.AppSettings = models.AppSettings(), change_not

# try to get a list of available boards to determine if we are a streamer
# the preamp hardware is not available on a streamer
# we need to know this before trying to intiialize the firmware
# we need to know this before trying to initialize the firmware
found_boards = []
try:
found_boards = EEPROM.get_available_devices(0)
Expand Down
21 changes: 18 additions & 3 deletions amplipi/eeprom.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,14 @@ class UnitType(Enum):
STREAMER = 2

class BoardType(Enum):
"""Board type"""
"""Matches the I2C address. The address is stored in the lowest 7 bits of the byte.
* For EEPROMs connected directly to the Pi's I2C bus, the MSB is 0.
* For EEPROMs connected to the Preamp's I2C bus, the MSB is 1.
The MC24C02's base I2C address is 0x50, with the 3 LSBs controlled by pins E[2:0].
"""
STREAMER_SUPPORT = 0x50
PREAMP = 0xD0

@dataclass
class BoardInfo:
Expand Down Expand Up @@ -123,7 +129,7 @@ def _read(self, address: int, length: int) -> List[int]:
raise EEPROMReadError("Read failed") from exception

def _write_number(self, bytes_len: int, addr: int, value: int) -> None:
"""Write number to EEPROM."""
"""Write number to EEPROM, big-endian."""
write_out = [0]*bytes_len
for i in range(0, bytes_len):
write_out[i] = value & 0xFF
Expand Down Expand Up @@ -223,7 +229,16 @@ def get_board_info(self) -> BoardInfo:
self.get_board_rev())

def write_board_info(self, board_info: BoardInfo) -> None:
"""Write board info to EEPROM."""
"""Write board info to EEPROM.
| Addr | Datatype | Data |
| ---- | -------- | :------------------------ |
| 0x00 | uint8 | EEPROM Data Format (0x00) |
| 0x01 | uint32 | Serial Number |
| 0x05 | uint8 | Unit Type |
| 0x06 | uint8 | Board Type |
| 0x07 | uint8 | Board Revision Number |
| 0x08 | char | Board Revision Letter |
"""
self._write_format()
self.write_serial(board_info.serial)
self.write_unit_type(board_info.unit_type)
Expand Down
44 changes: 22 additions & 22 deletions amplipi/hw.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ class FwVersion:
dirty: bool

def __init__(self, major: int = 0, minor: int = 0, git_hash: int = 0, dirty: bool = False):
if not 0 < major < 255 or not 0 < minor < 255:
raise ValueError('Major and minor version must be in the range [0,255]')
if not 0 < git_hash < 0xFFFFFFF:
if not 0 <= major <= 255 or not 0 <= minor <= 255:
raise ValueError(f'Major and minor version must be in the range [0,255]. Found: {major}.{minor}')
if not 0 <= git_hash <= 0xFFFFFFF:
raise ValueError('Hash must be an unsigned 28-bit value')
self.major = major
self.minor = minor
Expand Down Expand Up @@ -278,13 +278,12 @@ def _reset_master(self, bootloader: bool) -> None:
GPIO.output(self.Pin.BOOT0.value, bootloader)

# Hold the reset line low >300 ns
time.sleep(0.01)
time.sleep(0.001)
GPIO.output(self.Pin.NRST.value, 1)

# Each preamps' microcontroller takes ~3ms to startup after releasing
# Each preamps' microcontroller takes ~6ms to startup after releasing
# NRST. Just to be sure wait 10 ms before sending an I2C address.
# Further testing shows 6ms minimum
time.sleep(0.1)
time.sleep(0.01)
GPIO.cleanup()

def set_i2c_address(self, baud: int = 9600) -> bool:
Expand Down Expand Up @@ -334,20 +333,21 @@ def program(self, filepath: str, unit: int = 0, baud: int = 115200) -> bool:
print(f"Setting {self.unit_num_to_name(p)}'s UART as passthrough")
self.preamps[p].uart_passthrough(True)

# Before attempting programming, verify the unit even exists.
try:
subprocess.run([f'stm32flash -b {baud} {PI_SERIAL_PORT}'], shell=True,
check=True, stdout=subprocess.DEVNULL)
except subprocess.CalledProcessError:
# Failed to handshake with the bootloader. Assume unit not present.
print(f"Couldn't communicate with {self.unit_num_to_name(unit)}'s bootloader.")
plural = 's are' if unit != 1 else ' is'
print(f'Assuming only {unit} unit{plural} present and stopping programming')

# Reset all units to make sure the UART passthrough and
# bootloader modes are cleared.
self.reset()
return False
if unit > 1:
# Before attempting programming, verify the unit even exists.
try:
subprocess.run([f'stm32flash -b {baud} {PI_SERIAL_PORT}'], shell=True,
check=True, stdout=subprocess.DEVNULL)
except subprocess.CalledProcessError:
# Failed to handshake with the bootloader. Assume unit not present.
print(f"Couldn't communicate with {self.unit_num_to_name(unit)}'s bootloader.")
plural = 's are' if unit != 1 else ' is'
print(f'Assuming only {unit} unit{plural} present and stopping programming')

# Reset all units to make sure the UART passthrough and
# bootloader modes are cleared.
self.reset()
return False

prog_success = False
try:
Expand Down Expand Up @@ -401,7 +401,7 @@ def program_all(self, filepath: str, num_units: Optional[int] = None, baud: int
success = True
while success and unit < program_count:
print()
success = self.program(filepath, unit)
success = self.program(filepath, unit, baud)
if success:
unit += 1

Expand Down
10 changes: 5 additions & 5 deletions amplipi/rt.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,13 @@ def reset_preamps(self, bootloader: bool = False):
GPIO.output(4, 0) # Low pulse on the reset line (GPIO4)
GPIO.setup(5, GPIO.OUT)
GPIO.output(5, boot0) # Ensure BOOT0 is set (GPIO5)
time.sleep(0.1)
time.sleep(0.001) # Hold reset low for >20 us, but <10 ms.
GPIO.output(4, 1)

# Each box theoretically takes ~11ms to undergo a reset.
# Each box theoretically takes ~6 to undergo a reset.
# Estimating for six boxes, including some padding,
# wait 100ms for the resets to propagate down the line
time.sleep(0.1)
# wait 50ms for the resets to propagate down the line
time.sleep(0.05)

# Done with GPIO, they will default back to inputs with pullups
GPIO.cleanup()
Expand All @@ -187,7 +187,7 @@ def set_i2c_addr(self):
"""
# Setup serial connection via UART pins
with Serial('/dev/serial0', baudrate=9600) as ser:
ser.write((0x41, 0x10, 0x0D, 0x0A))
ser.write((0x41, 0x10, 0x0A))

# Delay to account for addresses being set
# Each box theoretically takes ~5ms to receive its address. Again, estimate for six boxes and include some padding
Expand Down
2 changes: 2 additions & 0 deletions config/asound.conf
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pcm.ch0 {
# U2/U4/U6/U8 op-amps. The audio signal is centered at 2.5 V and the
# op-amps cleanly pass down to ~1.4V, so max (2.5 - 1.4) / sqrt(2) = 0.78 Vrms.
# Nominal DAC output is ~1.76 Vrms. So 0.78 / 1.76 = 0.44
# Note: this is no longer true in Preamp Rev4 and greater, but
# we have to leave this here for backwards compatibility.
ttable.0.0 0.44
ttable.1.1 0.44

Expand Down
Binary file removed docs/imgs/PreampIDE_Options.jpg
Binary file not shown.
Binary file removed docs/imgs/debug_config1.jpg
Binary file not shown.
Binary file removed docs/imgs/debug_config2.jpg
Binary file not shown.
Binary file removed docs/imgs/debug_config3.jpg
Binary file not shown.
Binary file removed docs/imgs/debug_config4.jpg
Binary file not shown.
30 changes: 0 additions & 30 deletions docs/preamp_dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,3 @@ setting up VS Code remotes.
Debugging is available through the SWD debugger header on the Preamp board (J8). We typically connect a Nucleo-F030R8 development board to this port and use it as an ST-LINK/V2-1 debugger/programmer. Be sure to line up pin 1 from the debugger with pin 1 on the Preamp board debug port, noting that the cable is six pins wide, while the port on the Preamp board is five pins wide - the sixth pin is not needed. Some helpful links can be found below:
The Nucleo board can be purchased/researched [here.](http://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-eval-tools/stm32-mcu-eval-tools/stm32-mcu-nucleo/nucleo-f030r8.html)
The STM32 ST-LINK Utility provides the ability to easily erase the flash and to reset the board when other software will not connect, among other things. Find it [here.](http://www.st.com/content/st_com/en/products/embedded-software/development-tool-software/stsw-link004.html)

## System Workbench IDE/Debugging
As an alternative System Workbench for STM32 can also be used as an IDE.
It is based on Eclipse, and can be found
[here.](http://www.openstm32.org/System+Workbench+for+STM32)

Once you are in System Workbench,
set the debug configuration according to the pictures below:

![image](imgs/debug_config1.jpg)
![image](imgs/debug_config2.jpg)
![image](imgs/debug_config3.jpg)
![image](imgs/debug_config4.jpg)

For the debugger to work properly, YOU MUST MODIFY stm32f0x.cfg.
With System Workbench installed, it should be found in
```
(root)/Ac6/SystemWorkbench/plugins/fr.ac6.mcu.debug_XXXX/resources/openocd/scripts/target/stm32f0x.cfg
```
where (root) is the directory you installed it in.
Once this file is open, find this line: adapter_khz 1000
And change it to: adapter_khz 480

Once all of these steps are complete, the debugger should be functional!
Within the IDE, it is possible to build and debug the project.
The build and debug options can be found in the toolbar at the top of the
debugger, and they each come with multiple options.
Build should work if the project was set up properly, even without a debugger.

![image](imgs/PreampIDE_Options.jpg)
Binary file removed fw/bin/preamp_1.1.bin
Binary file not shown.
Binary file removed fw/bin/preamp_1.2.bin
Binary file not shown.
Binary file removed fw/bin/preamp_1.3.bin
Binary file not shown.
Binary file removed fw/bin/preamp_1.4.bin
Binary file not shown.
Binary file removed fw/bin/preamp_1.5.bin
Binary file not shown.
Binary file removed fw/bin/preamp_1.6.bin
Binary file not shown.
Binary file added fw/bin/preamp_1.7.bin
Binary file not shown.
Loading

0 comments on commit dd618f0

Please sign in to comment.