Compare commits

..

26 commits

Author SHA1 Message Date
grvvy
e1dc052815 Jenkinsfile: ensure usb hub commands are wrapped in retry blocks
Some checks failed
Check code style / clang-format-2 (push) Failing after 19s
Check code style / clang-format-3 (push) Failing after 7s
Check includes / includes-check (HACKRF_ONE, 3.10.0) (push) Failing after 6s
Check includes / includes-check (HACKRF_ONE, latest) (push) Failing after 5s
Check includes / includes-check (JAWBREAKER, 3.10.0) (push) Failing after 5s
Check includes / includes-check (JAWBREAKER, latest) (push) Failing after 3s
Check includes / includes-check (PRALINE, 3.10.0) (push) Failing after 4s
Check includes / includes-check (PRALINE, latest) (push) Failing after 3s
Check includes / includes-check (RAD1O, 3.10.0) (push) Failing after 3s
Check includes / includes-check (RAD1O, latest) (push) Failing after 3s
Check includes / includes-check (UNIVERSAL, 3.10.0) (push) Failing after 4s
Check includes / includes-check (UNIVERSAL, latest) (push) Failing after 3s
Build / host (3.10.0, map[os:macos shell:bash]) (push) Has been cancelled
Build / host (3.16.0, map[os:macos shell:bash]) (push) Has been cancelled
Build / host (3.21.0, -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake --install-prefix=$env:GITHUB_WORKSPACE/install, map[os:windows shell:pwsh]) (push) Has been cancelled
Build / host (3.21.0, map[os:macos shell:bash]) (push) Has been cancelled
Build / host (4.0.0, -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake --install-prefix=$env:GITHUB_WORKSPACE/install, map[os:windows shell:pwsh]) (push) Has been cancelled
Build / host (4.0.0, map[os:macos shell:bash]) (push) Has been cancelled
Build / host (latest, --install-prefix=/usr/local, map[os:windows shell:msys2]) (push) Has been cancelled
Build / host (latest, -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake --install-prefix=$env:GITHUB_WORKSPACE/install, map[os:windows shell:pwsh]) (push) Has been cancelled
Build / host (latest, map[os:macos shell:bash]) (push) Has been cancelled
Build / firmware (HACKRF_ONE, latest, windows) (push) Has been cancelled
Build / firmware (JAWBREAKER, latest, windows) (push) Has been cancelled
Build / firmware (PRALINE, 3.10.0, macos) (push) Has been cancelled
Build / firmware (PRALINE, latest, macos) (push) Has been cancelled
Build / firmware (PRALINE, latest, windows) (push) Has been cancelled
Build / firmware (RAD1O, latest, windows) (push) Has been cancelled
Build / firmware (UNIVERSAL, 3.10.0, macos) (push) Has been cancelled
Build / firmware (UNIVERSAL, latest, macos) (push) Has been cancelled
Build / firmware (UNIVERSAL, latest, windows) (push) Has been cancelled
2026-04-15 23:22:21 -06:00
grvvy
f8c3856d7c Add temporary HIL CI fix - sleep 4s after programming the EUT 2026-04-15 23:19:05 -06:00
Martin Ling
9e1126d952 Use IS macros for all conditional compilation. 2026-04-15 22:53:42 +01:00
Martin Ling
8ce78cbc8f firmware: Separate compile-time and runtime platform checks. 2026-04-15 22:53:42 +01:00
Martin Ling
7938f93295 firmware: Use macros to simplify platform-specific code. 2026-04-15 22:53:41 +01:00
Martin Ling
7fa88d13e0 max283x: Add macros to simplify variant-specific dispatch. 2026-04-15 22:51:54 +01:00
Martin Ling
77569163a3 max283x: Move platform switch inside max283x_setup(). 2026-04-15 22:51:54 +01:00
Martin Ling
7fd2d4b86f Jenkinsfile: add tests with BOARD=UNIVERSAL. 2026-04-15 22:23:03 +01:00
Martin Ling
9d11652d62 Add UNIVERSAL firmware build to GitHub Actions. 2026-04-15 22:23:03 +01:00
Martin Ling
509249fe74 Add UNIVERSAL board target. 2026-04-15 22:23:03 +01:00
Martin Ling
e34393a1dd Universalize: firmware/hackrf_usb
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:23:03 +01:00
Martin Ling
d862545ce3 Universalize: hackrf_core.{c,h}, selftest.h
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:23:03 +01:00
Martin Ling
a14c4c85f8 Universalize: radio.c
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
7c78654afc Universalize: sgpio.{c,h}
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
85ada5ba48 Universalize: w25q80bv.c
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
547aa38294 Universalize: operacake_sctimer.c
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
8587964d32 Universalize: hackrf_ui.c
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
0b3ed0d43c Universalize: tuning.{c,h}, tune_config.h
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
52c9b4ac0a Universalize: mixer.{c,h}, usb_api_register.c
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
2a89ed7207 Universalize: rf_path.{c,h}
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
ccc144ad48 Universalize: si5351.c
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
dafa1349cc Universalize: rffc5071.{c,h}
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
25918e154a Universalize: max283x.{c,h}
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
bf7d47abf1 Universalize: cpld_jtag.{c,h}
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
7e03135d49 Universalize: platform_scu.{c,h}
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
Martin Ling
697d10fa53 Universalize: platform_gpio.{c,h}
Co-authored-by: Antoine van Gelder <antoine@greatscottgadgets.com>
2026-04-15 22:22:04 +01:00
136 changed files with 2797 additions and 5316 deletions

View file

@ -17,127 +17,70 @@ jobs:
host:
strategy:
matrix:
os: ['macos', 'ubuntu', 'windows']
cmake: ['3.10.0', '3.21.7', '4.2.6', 'latest']
compiler: ['gcc', 'msvc']
sys:
- {os: 'macos', shell: 'bash'}
- {os: 'ubuntu', shell: 'bash'}
- {os: 'windows', shell: 'pwsh'}
- {os: 'windows', shell: 'msys2'}
cmake: ['3.10.0', '3.16.0', '3.21.0', '4.0.0', 'latest']
exclude:
# MSVC only runs on Windows.
- os: 'macos'
compiler: 'msvc'
- os: 'ubuntu'
compiler: 'msvc'
# We can't use the oldest CMake versions on Windows.
- os: 'windows'
# GitHub runners use Visual Studio 2022. Support added in CMake 3.21.
- sys: {os: 'windows', shell: 'pwsh'}
cmake: '3.10.0'
# The in-between CMake versions aren't so intersting with GCC.
- compiler: 'gcc'
cmake: '3.21.7'
- compiler: 'gcc'
cmake: '4.2.6'
- sys: {os: 'windows', shell: 'pwsh'}
cmake: '3.16.0'
# MSYS2 always supplies the latest cmake.
- sys: {os: 'windows', shell: 'msys2'}
cmake: '3.10.0'
- sys: {os: 'windows', shell: 'msys2'}
cmake: '3.16.0'
- sys: {os: 'windows', shell: 'msys2'}
cmake: '3.21.0'
- sys: {os: 'windows', shell: 'msys2'}
cmake: '4.0.0'
include:
# Shell selection
- os: 'macos'
shell: 'bash'
- os: 'ubuntu'
shell: 'bash'
- os: 'windows'
compiler: 'msvc'
shell: 'pwsh'
- os: 'windows'
compiler: 'gcc'
shell: 'msys2'
# MSVC version selection
- cmake: '3.21.7'
msvc: '17.14'
- cmake: '4.2.6'
msvc: 'latest'
- cmake: 'latest'
msvc: 'latest'
# CMake generator selection
- os: 'macos'
generator: 'Unix Makefiles'
- os: 'ubuntu'
generator: 'Unix Makefiles'
- compiler: 'msvc'
cmake: '3.21.7'
generator: 'Visual Studio 17 2022'
- compiler: 'msvc'
cmake: '4.2.6'
generator: 'Visual Studio 18 2026'
- compiler: 'msvc'
cmake: 'latest'
generator: 'Visual Studio 18 2026'
- os: 'windows'
compiler: 'gcc'
generator: 'MSYS Makefiles'
# Additional CMake arguments
- os: 'windows'
compiler: 'msvc'
- sys: {os: 'windows', shell: 'pwsh'}
cmake_args: >-
-DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe
-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
--install-prefix=$env:GITHUB_WORKSPACE/install
- os: 'windows'
compiler: 'gcc'
- sys: {os: 'windows', shell: 'msys2'}
cmake_args: >-
--install-prefix=/usr/local
# CMake 3.10 uses --build with --target install
- cmake: '3.10.0'
install_cmd: --build
install_args: --target install
# Later versions just use --install
- cmake: '3.21.7'
install_cmd: --install
- cmake: '4.2.6'
install_cmd: --install
- cmake: 'latest'
install_cmd: --install
# We need sudo on MacOS and Ubuntu only
- os: macos
sudo: sudo
- os: ubuntu
sudo: sudo
# Don't cancel all builds when one fails
fail-fast: false
runs-on: ${{ matrix.os }}-latest
runs-on: ${{ matrix.sys.os }}-latest
defaults:
run:
shell: '${{ matrix.shell }} {0}'
shell: '${{ matrix.sys.shell }} {0}'
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Setup cmake
uses: lukka/get-cmake@latest
with:
cmakeVersion: ${{ matrix.cmake }}
if: matrix.shell != 'msys2'
- name: Install MSVC (Windows)
uses: k3DW/setup-msvc@v1
with:
vs-version: ${{ matrix.msvc }}
if: matrix.compiler == 'msvc' && matrix.msvc != 'latest'
if: matrix.sys.shell != 'msys2'
- name: Install dependencies (macOS)
run: brew install fftw
if: matrix.os == 'macos'
if: matrix.sys.os == 'macos'
- name: Install dependencies (Ubuntu)
run: |
sudo apt update
sudo apt install libfftw3-dev libusb-1.0-0-dev
if: matrix.os == 'ubuntu'
if: matrix.sys.os == 'ubuntu'
- name: Install dependencies (Windows)
run: vcpkg install --triplet=x64-windows libusb fftw3 pthreads pkgconf
if: matrix.os == 'windows' && matrix.shell != 'msys2'
if: matrix.sys.os == 'windows' && matrix.sys.shell != 'msys2'
- name: Setup MSYS (Windows)
if: matrix.os == 'windows' && matrix.shell == 'msys2'
if: matrix.sys.os == 'windows' && matrix.sys.shell == 'msys2'
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
@ -155,7 +98,7 @@ jobs:
run: |
cmake -E make_directory host/build
cd host/build
cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}}
cmake .. -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}}
cmake --build . --config Release
# Build libhackrf ONLY
@ -164,12 +107,22 @@ jobs:
run: |
cmake -E make_directory host/libhackrf/build
cd host/libhackrf/build
cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake --build . --config Release
- name: Install (libhackrf)
run: |
${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/libhackrf/build ${{ matrix.install_args }} --config Release
sudo cmake --install host/libhackrf/build --config Release
if: matrix.sys.os != 'windows' && matrix.cmake != '3.10.0'
- name: Install (libhackrf, CMake 3.10)
run: |
sudo cmake --build host/libhackrf/build --target install --config Release
if: matrix.sys.os != 'windows' && matrix.cmake == '3.10.0'
- name: Install (libhackrf, Windows)
run: cmake --install host/libhackrf/build --config Release
if: matrix.sys.os == 'windows'
# Build hackrf-tools ONLY
@ -177,12 +130,22 @@ jobs:
run: |
cmake -E make_directory host/hackrf-tools/build
cd host/hackrf-tools/build
cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake --build . --config Release
- name: Install (hackrf-tools)
run: |
${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/hackrf-tools/build ${{ matrix.install_args }}
sudo cmake --install host/hackrf-tools/build --config Release
if: matrix.sys.os != 'windows' && matrix.cmake != '3.10.0'
- name: Install (hackrf-tools, CMake 3.10)
run: |
sudo cmake --build host/hackrf-tools/build --target install --config Release
if: matrix.sys.os != 'windows' && matrix.cmake == '3.10.0'
- name: Install (hackrf-tools, Windows)
run: cmake --install host/hackrf-tools/build --config Release
if: matrix.sys.os == 'windows'
# Publish the contents of install/bin (which should be the combination libhackrf and host-tools) for Windows
- name: Publish Artifacts (Windows)
@ -190,17 +153,17 @@ jobs:
with:
name: hackrf-tools-windows
path: ${{github.workspace}}/install/bin
if: matrix.os == 'windows' && matrix.cmake == 'latest' && matrix.shell == 'pwsh'
if: matrix.sys.os == 'windows' && matrix.cmake == 'latest' && matrix.sys.shell == 'pwsh'
firmware:
strategy:
matrix:
os: ['macos', 'ubuntu', 'windows']
board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE', 'UNIVERSAL']
cmake: ['3.12.0', 'latest']
cmake: ['3.10.0', 'latest']
exclude:
- os: 'windows'
cmake: '3.12.0'
cmake: '3.10.0'
# GitHub doesn't give us many macOS runners, so don't build for every board.
# We just need to know that building firmware works on macOS. Use PRALINE
# since that's the most complicated one due to FPGA bitstreams.
@ -216,7 +179,7 @@ jobs:
runs-on: ${{ matrix.os }}-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
submodules: true

View file

@ -16,7 +16,7 @@ jobs:
- check: 'firmware/hackrf_usb'
exclude: ''
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Run clang-format-action
uses: jidicula/clang-format-action@v4.6.2
with:

View file

@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE', 'UNIVERSAL']
cmake: ['3.12.0', 'latest']
cmake: ['3.10.0', 'latest']
# Don't cancel all builds when one fails
fail-fast: false

135
Jenkinsfile vendored
View file

@ -39,15 +39,31 @@ pipeline {
timeout(time: 20, unit: 'MINUTES')
}
steps {
runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES')
runCommand("Build HackRF One Firmware", './ci-scripts/build_firmware.sh HACKRF_ONE', 3, 1, 'MINUTES')
lock('HIL_hubs') {
script {
allOff()
runTest("Check Host", 'h1_eut', './ci-scripts/test_host.sh')
runTest("HackRF One HIL Test", 'h1_tester h1_eut', h1_test)
runTest("SGPIO Debug Test", 'h1_eut', 'python3 ci-scripts/test_sgpio_debug.py')
}
sh './ci-scripts/install_host.sh'
sh './ci-scripts/build_firmware.sh HACKRF_ONE'
script {
allOff()
reset('h1_eut')
}
sh 'sleep 1s'
retry(3) {
sh './ci-scripts/test_host.sh'
}
script {
reset('h1_tester h1_eut')
}
sh 'sleep 1s'
script {
// Allow up to 3 retries 5 minutes each for the HIL test
runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test)
}
script {
allOff()
reset('h1_eut')
}
sh 'sleep 1s'
retry(3) {
sh 'python3 ci-scripts/test_sgpio_debug.py'
}
}
}
@ -63,17 +79,32 @@ pipeline {
timeout(time: 20, unit: 'MINUTES')
}
steps {
runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES')
runCommand("Build Universal Firmware", './ci-scripts/build_firmware.sh UNIVERSAL', 3, 1, 'MINUTES')
lock('HIL_hubs') {
script {
allOff()
runTest("Check Host", 'h1_eut', './ci-scripts/test_host.sh')
runTest("HackRF One HIL Test", 'h1_tester h1_eut', h1_test)
runTest("SGPIO Debug Test", 'h1_eut', 'python3 ci-scripts/test_sgpio_debug.py')
}
sh './ci-scripts/install_host.sh'
sh './ci-scripts/build_firmware.sh UNIVERSAL'
script {
allOff()
reset('h1_eut')
}
sh 'sleep 1s'
retry(3) {
sh './ci-scripts/test_host.sh'
}
script {
reset('h1_tester h1_eut')
}
sh 'sleep 1s'
script {
// Allow up to 3 retries 5 minutes each for the HIL test
runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test)
}
script {
allOff()
reset('h1_eut')
}
sh 'sleep 1s'
retry(3) {
sh 'python3 ci-scripts/test_sgpio_debug.py'
}
}
}
stage('Test HackRF Pro with BOARD=PRALINE') {
@ -88,14 +119,23 @@ pipeline {
timeout(time: 20, unit: 'MINUTES')
}
steps {
runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES')
runCommand("Build Praline Firmware", './ci-scripts/build_firmware.sh PRALINE', 3, 1, 'MINUTES')
lock('HIL_hubs') {
script {
allOff()
runTest("Check Host", 'hpro_eut', './ci-scripts/test_host.sh')
runTest("HackRF Pro HIL Test", 'hpro_tester hpro_eut', hpro_test)
}
sh './ci-scripts/install_host.sh'
sh './ci-scripts/build_firmware.sh PRALINE'
script {
allOff()
reset('hpro_eut')
}
sh 'sleep 1s'
retry(3) {
sh './ci-scripts/test_host.sh'
}
script {
reset('hpro_tester hpro_eut')
}
sh 'sleep 1s'
script {
// Allow up to 3 retries 5 minutes each for the HIL test
runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test)
}
}
}
@ -111,14 +151,23 @@ pipeline {
timeout(time: 20, unit: 'MINUTES')
}
steps {
runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES')
runCommand("Build Universal Firmware", './ci-scripts/build_firmware.sh UNIVERSAL', 3, 1, 'MINUTES')
lock('HIL_hubs') {
script {
allOff()
runTest("Check Host", 'hpro_eut', './ci-scripts/test_host.sh')
runTest("HackRF Pro HIL Test", 'hpro_tester hpro_eut', hpro_test)
}
sh './ci-scripts/install_host.sh'
sh './ci-scripts/build_firmware.sh UNIVERSAL'
script {
allOff()
reset('hpro_eut')
}
sh 'sleep 1s'
retry(3) {
sh './ci-scripts/test_host.sh'
}
script {
reset('hpro_tester hpro_eut')
}
sh 'sleep 1s'
script {
// Allow up to 3 retries 5 minutes each for the HIL test
runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test)
}
}
}
@ -135,15 +184,15 @@ pipeline {
def allOff() {
// Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately
runCommand('USB hub port power server command', "hubs all off", 3, 20, 'SECONDS')
runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs all off")
}
def reset(devices) {
// Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately
runCommand('USB hub port power server command', "hubs ${devices} reset", 3, 20, 'SECONDS')
runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs ${devices} reset")
}
def runCommand(title, cmd, retries, time, unit) {
def runCommand(retries, time, unit, title, cmd) {
retry(retries) {
try {
timeout(time: time, unit: unit) {
@ -165,13 +214,3 @@ def runCommand(title, cmd, retries, time, unit) {
}
}
}
def runTest(title, devices, cmd) {
retry(3) {
// reset() retains it's own internal retries
reset(devices)
sh 'sleep 1s'
// run the test with 0 internal retries and 3 external retries to ensure resets between runs
runCommand(title, cmd, 0, 5, 'MINUTES')
}
}

View file

@ -663,6 +663,9 @@ class HackRF:
self.rf_test_cases = praline_r110_rf_test_cases
def clkin(self):
if self.name == "EUT" and self.id == "5":
debug = subprocess.run([self.bin_dir + "/hackrf_debug", "-d", self.serial,
"-C", "0"])
command = subprocess.run([self.bin_dir + "/hackrf_clock", "-i", "-d",
self.serial], capture_output=True, encoding="utf-8",
timeout=TIMEOUT)
@ -1410,11 +1413,11 @@ def program(bin_dir, fw_dir, serial, unattended=False):
if spiflash.returncode != 0:
log(spiflash.stdout + spiflash.stderr)
fail(70)
time.sleep(3)
then = time.time()
device_found = False
while time.time() < (then + 5):
time.sleep(1)
flash_info = subprocess.run([bin_dir + "/hackrf_info"], capture_output=True,
timeout=TIMEOUT)
if serial == "RunningFromRAM" and not unattended:
@ -1430,7 +1433,7 @@ def program(bin_dir, fw_dir, serial, unattended=False):
if serial in flash_info.stdout.decode('utf-8', errors='ignore'):
device_found = True
break
time.sleep(1)
time.sleep(0.1)
if not device_found:
fail(75)
@ -1591,6 +1594,7 @@ def main():
# TODO: check if the provided firmware directory exists
if args.fwupdate:
program(eut_host_dir, args.fwupdate, eut_sn, args.unattended)
time.sleep(4)
eut_sn = find_sn("EUT", eut_host_dir, args.factory, [tester_sn])
if not args.rev:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 34 KiB

View file

@ -9,18 +9,12 @@ HackRF Pro
HackRF Pro has two configurable SMA ports, P1 and P2. By default, P1 is configured as CLKIN and P2 as CLKOUT. The default behaviour of these signals is as described for HackRF One below.
A second CLKIN signal is available on header P22 pin 2. Unlike HackRF One, HackRF Pro's P22_CLKIN is a separate signal from P1_CLKIN. To enable P22_CLKIN instead of P1_CLKIN use ``hackrf_clock -c p22``.
Various internal signals can be connected to P1 or P2 instead of the default CLKIN and CLKOUT signals. Use ``hackrf_clock -1`` or ``hackrf_clock -2`` to select a different signal.
HackRF One
~~~~~~~~~~
HackRF One produces a 10 MHz clock signal on the CLKOUT SMA port. The signal is a 3.3 V, 10 MHz square wave intended for a high impedance load.
HackRF One produces a 10 MHz clock signal on CLKOUT. The signal is a 3.3 V, 10 MHz square wave intended for a high impedance load.
The CLKIN SMA port on HackRF One is a high impedance input that expects 3.3 V square wave at 10 MHz. Do not exceed 3.3 V or drop below 0 V on this input. Do not connect a clock signal at a frequency other than 10 MHz (unless you modify the firmware to support this). You may directly connect the CLKOUT port of one HackRF One to the CLKIN port of another HackRF.
The CLKIN signal is also connected to header P22 pin 2. Unlike HackRF Pro, HackRF One has only one CLKIN signal shared between P22 pin 2 and the CLKIN port. Do not connect input signals to both CLKIN and P22 pin 2 simultaneously.
The CLKIN port on HackRF One is a high impedance input that expects 3.3 V square wave at 10 MHz. Do not exceed 3.3 V or drop below 0 V on this input. Do not connect a clock signal at a frequency other than 10 MHz (unless you modify the firmware to support this). You may directly connect the CLKOUT port of one HackRF One to the CLKIN port of another HackRF One.
HackRF One uses CLKIN instead of the internal crystal when a clock signal is detected on CLKIN. The switch to or from CLKIN only happens when a transmit or receive operation begins.

View file

@ -1,118 +0,0 @@
================================================
Gateware
================================================
One of the significant hardware changes in :ref:`HackRF Pro <hackrf_pro>` is the replacement of the CPLD with a FPGA. While the older CPLD primarily provided glue logic between the MCU and RF front end, the FPGA in HackRF Pro introduces more logic and DSP capability. This enables offloading digital signal processing tasks from the MCU.
FPGAs are highly flexible devices whose behavior is defined by *gateware*: hardware descriptions that configure the internal logic fabric. HackRF Pro gateware is written in `Amaranth HDL <https://amaranth-lang.org/>`__, a Python-based hardware description language.
The specific FPGA device used in HackRF Pro is the Lattice iCE40UP5K, which features
5280 LUT4s and 8 dedicated DSP (multiply-accumulate) blocks. We rely on the `open-source iCE40 FPGA toolchain <https://github.com/YosysHQ/icestorm>`__ to build the required bitstreams that are bundled in the firmware.
All gateware source code lives under `firmware/fpga/` in the HackRF repository. Top-level designs reside in `firmware/fpga/top/` and are the primary entry points for different operational modes.
By default, a standard gateware configuration is loaded at boot. However, the firmware can dynamically reconfigure the FPGA at runtime to switch between different gateware variants.
Standard gateware
~~~~~~~~~~~~~~~~~
The standard gateware is used by default when the firmware has not requested an alternative bitstream.
The standard gateware provides a balanced configuration optimized for general-purpose operation. It implements configurable digital signal processing paths for the reception and transmission paths, capable of (limited) frequency translation and supporting a wide range of sample rates.
Block diagram
^^^^^^^^^^^^^
.. image:: ../images/gateware-basic-block-diagram.png
:align: center
.. image:: ../images/gateware-standard-dsp-chain.png
:align: center
:width: 75%
Features
^^^^^^^^
* 8-bit I, 8-bit Q data format
* Receiver signal chain:
* Optional DC offset removal (DC blocker)
* Configurable fs/4 shifter (quarter sample rate): bypass, shift up or shift down
* Configurable decimation rates: 1x, 2x, 4x, 8x, 16x, 32x
* Transmitter signal chain:
* Configurable interpolation rates: 1x, 2x, 4x, 8x, 16x, 32x
* SPI control interface for register configuration
* Double data rate (DDR) interface to RF transceiver
* Interface to MCU (SGPIO)
Half-precision gateware
~~~~~~~~~~~~~~~~~~~~~~~
The half-precision gateware reduces sample width to 4 bits per I/Q component, enabling higher throughput within the constraints of the USB interface (up to 40 Msps).
This configuration is intended for applications where bandwidth is more critical than dynamic range, such as wideband spectrum monitoring.
Block diagram
^^^^^^^^^^^^^
.. image:: ../images/gateware-basic-block-diagram.png
:align: center
.. image:: ../images/gateware-halfprec-dsp-chain.png
:align: center
:width: 75%
Features
^^^^^^^^
* 4-bit I, 4-bit Q data format
* Receiver signal chain:
* Optional DC offset removal (DC blocker)
* Round to 4-bit I/Q
* Transmitter signal chain:
* Extend width to 8-bit I/Q
* SPI control interface for register configuration
* Double data rate (DDR) interface to RF transceiver
* Interface to MCU (SGPIO)
Extended-precision gateware (RX and TX)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The extended-precision gateware increases internal signal processing precision and output sample width to improve signal quality. The main drawback is that the minimum decimation or interpolation factor is 16x. Due to increased logic requirements, this gateware is split in two top-level designs (RX and TX).
Samples are 16-bit I/Q, while the effective number of bits (ENOB) depends on the selected configuration and typically ranges between 9 and 11 bits.
The increased dynamic range of the output makes it particularly useful for weak and/or narrowband signals.
Block diagram (RX)
^^^^^^^^^^^^^^^^^^
.. image:: ../images/gateware-rx-only-basic-block-diagram.png
:align: center
.. image:: ../images/gateware-ext-prec-rx-dsp-chain.png
:align: center
:width: 75%
Block diagram (TX)
^^^^^^^^^^^^^^^^^^
.. image:: ../images/gateware-tx-only-basic-block-diagram.png
:align: center
.. image:: ../images/gateware-ext-prec-tx-dsp-chain.png
:align: center
:width: 75%
Features
^^^^^^^^
* 16-bit I, 16-bit Q data format
* Receiver signal chain (RX extended-precision gateware):
* Optional DC offset removal (DC blocker)
* Configurable mixer (in fs/128 steps)
* Configurable decimation rates: 16x, 32x, 64x, 128x
* Transmitter signal chain (TX extended-precision gateware):
* Configurable interpolation rates: 16x, 32x, 64x, 128x
* SPI control interface for register configuration
* Double data rate (DDR) interface to RF transceiver
* Interface to MCU (SGPIO)

View file

@ -2,8 +2,6 @@
HackRF Pro
================================================
.. _hackrf_pro:
.. image:: ../images/hackrf-pro-preliminary-photo.jpg
:alt: HackRF Pro

View file

@ -57,13 +57,6 @@ Welcome to HackRF's documentation!
setting_gain
virtual_machines
.. toctree::
:maxdepth: 2
:caption: Gateware
gateware
.. toctree::
:maxdepth: 2
:caption: Opera Cake Documentation

View file

@ -43,11 +43,6 @@ Software That Has Direct Support For HackRF
* `https://xakcop.com/aprs-sdr <https://xakcop.com/aprs-sdr/>`__
* SigDigger (Windows/Linux/macOS)
* `https://github.com/BatchDrake/SigDigger <https://github.com/BatchDrake/SigDigger>`__
* Supports HackRF through SoapySDR / SoapyHackRF
Software That Can Use Data From HackRF
@ -79,4 +74,4 @@ Troubleshooting Recommendations
Many of these tools require libhackrf and at times HackRF Tools. It may help you to have updated libhackrf and HackRF Tools when troubleshooting these applications.
It is also strongly suggested, and usually required, that your HackRF Tools and HackRF firmware match.
It is also strongly suggested, and usually required, that your HackRF Tools and HackRF firmware match.

View file

@ -23,7 +23,7 @@
option(CHECK_INCLUDES
"Check firmware sources for unused includes and transitive dependencies. (Requires iwyu)" OFF)
cmake_minimum_required(VERSION 3.12.0)
cmake_minimum_required(VERSION 3.10.0)
set(CMAKE_TOOLCHAIN_FILE toolchain-arm-cortex-m.cmake)
project (hackrf_firmware_all C)

View file

@ -19,16 +19,14 @@
* Boston, MA 02110-1301, USA.
*/
#include "delay.h"
#include "leds.h"
#include "pins.h"
#include "hackrf_core.h"
#include "platform_detect.h"
#include "power.h"
#include "delay.h"
int main(void)
{
detect_hardware_platform();
pins_setup();
pin_setup();
#ifndef PRALINE
/* enable 1V8 power supply so that the 1V8 LED lights up */
@ -39,19 +37,19 @@ int main(void)
#endif
/* Blink LED1/2/3 on the board. */
while (1)
while (1)
{
led_on(LED1);
led_on(LED2);
led_on(LED3);
delay_ms(150);
delay(2000000);
led_off(LED1);
led_off(LED2);
led_off(LED3);
delay_ms(150);
delay(2000000);
}
return 0;

View file

@ -26,8 +26,7 @@ MEMORY
{
/* rom is really the shadow region that points to SPI flash or elsewhere */
rom (rx) : ORIGIN = 0x00000000, LENGTH = 1M
ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 64K
ram_usb (rw) : ORIGIN = 0x10010000, LENGTH = 32K
ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 96K
ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 32K
ram_sleep (rwx) : ORIGIN = 0x10088000, LENGTH = 8K
}

View file

@ -26,8 +26,7 @@ MEMORY
{
/* rom is really the shadow region that points to SPI flash or elsewhere */
rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K
ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 96K
ram_usb(rw) : ORIGIN = 0x10018000, LENGTH = 32K
ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 128K
ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 64K
ram_sleep (rwx) : ORIGIN = 0x10090000, LENGTH = 8K
}

View file

@ -35,12 +35,11 @@ MEMORY
* to get performance benefit of having two USB buffers addressable
* simultaneously (on two different buses of the AHB multilayer matrix)
*/
ram_samp (rwx) : ORIGIN = 0x20008000, LENGTH = 32K
ram_usb (rwx) : ORIGIN = 0x20008000, LENGTH = 32K
}
usb_bulk_buffer = ORIGIN(ram_usb);
lz4_in_buf = ORIGIN(ram_lz4_in);
lz4_out_buf = ORIGIN(ram_lz4_out);
usb_samp_buffer = ORIGIN(ram_samp);
usb_bulk_buffer = ORIGIN(ram_usb);
m0_state = ORIGIN(ram_shared);
PROVIDE(__ram_m0_start__ = ORIGIN(ram_m0));

View file

@ -1,5 +1,5 @@
/*
* Copyright 2022-2026 Great Scott Gadgets
* Copyright 2022 Great Scott Gadgets
*
* This file is part of HackRF.
*
@ -19,9 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#include "clock_io.h"
#include "clkin.h"
#include <stdbool.h>
#include <stdint.h>
#include <libopencm3/lpc43xx/timer.h>
@ -31,15 +30,6 @@
#include <libopencm3/lpc43xx/creg.h>
#include "gpdma.h"
#include "gpio.h"
#include "platform_detect.h"
#ifdef IS_NOT_PRALINE
#include "sgpio.h"
#endif
#ifdef IS_PRALINE
#include "fpga.h"
#include "platform_gpio.h"
#endif
#define CLOCK_CYCLES_1_MS (204000)
#define MEASUREMENT_WINDOW_MS (50)
@ -124,47 +114,4 @@ void clkin_detect_init(void)
uint32_t clkin_frequency(void)
{
return TIMER2_CR3 * (1000 / MEASUREMENT_WINDOW_MS);
}
void trigger_enable(const bool enable)
{
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
gpio_write(sgpio_config.gpio_trigger_enable, enable);
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
fpga_set_trigger_enable(&fpga, enable);
}
#endif
}
#ifdef IS_PRALINE
void clkin_ctrl_set(const clkin_signal_t signal)
{
gpio_write(platform_gpio()->clkin_ctrl, signal & 1);
}
void p1_ctrl_set(const p1_ctrl_signal_t signal)
{
const platform_gpio_t* gpio = platform_gpio();
gpio_write(gpio->p1_ctrl0, signal & 1);
gpio_write(gpio->p1_ctrl1, (signal >> 1) & 1);
gpio_write(gpio->p1_ctrl2, (signal >> 2) & 1);
}
void p2_ctrl_set(const p2_ctrl_signal_t signal)
{
const platform_gpio_t* gpio = platform_gpio();
gpio_write(gpio->p2_ctrl0, signal & 1);
gpio_write(gpio->p2_ctrl1, (signal >> 1) & 1);
}
void pps_out_set(const uint8_t value)
{
gpio_write(platform_gpio()->pps_out, value & 1);
}
#endif
};

View file

@ -1,5 +1,5 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2022 Great Scott Gadgets
*
* This file is part of HackRF.
*
@ -23,10 +23,5 @@
#include <stdint.h>
typedef struct {
uint64_t hi;
uint64_t lo;
} u128;
u128 u128_multiply(uint64_t a, uint64_t b);
u128 u128_divide(u128 dvnd, u128 dvsr);
void clkin_detect_init(void);
uint32_t clkin_frequency(void);

View file

@ -1,420 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "clock_gen.h"
#include <stdint.h>
#include "hackrf_ui.h"
#include "platform_detect.h"
#include "sgpio.h"
#include "si5351c.h"
#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
#include "delay.h"
#include "portapack.h"
#endif
void clock_gen_init(void)
{
si5351c_init(&si5351c);
si5351c_disable_all_outputs(&si5351c);
si5351c_disable_oeb_pin_control(&si5351c);
si5351c_power_down_all_clocks(&si5351c);
si5351c_set_crystal_configuration(&si5351c);
si5351c_enable_xo_and_ms_fanout(&si5351c);
/*
* Clocks on HackRF One r9:
* CLK0 -> MAX5864/CPLD/SGPIO (sample clocks)
* CLK1 -> RFFC5072/MAX2839
* CLK2 -> External Clock Output/LPC43xx (power down at boot)
*
* Clocks on other platforms:
* CLK0 -> MAX5864/CPLD
* CLK1 -> CPLD
* CLK2 -> SGPIO
* CLK3 -> External Clock Output (power down at boot)
* CLK4 -> RFFC5072 (MAX2837 on rad1o)
* CLK5 -> MAX2837 (MAX2871 on rad1o)
* CLK6 -> none
* CLK7 -> LPC43xx (uses a 12MHz crystal by default)
*
* Clocks on Praline:
* CLK0 -> AFE_CLK (MAX5864/FPGA)
* CLK1 -> SCT_CLK
* CLK2 -> MCU_CLK (uses a 12MHz crystal by default)
* CLK3 -> External Clock Output (power down at boot)
* CLK4 -> XCVR_CLK (MAX2837)
* CLK5 -> MIX_CLK (RFFC5072)
* CLK6 -> AUX_CLK1
* CLK7 -> AUX_CLK2
*/
#ifdef IS_H1_R9
if (IS_H1_R9) {
/* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */
si5351c_configure_multisynth(
&si5351c,
0,
20 * 128 - 512,
0,
1,
0); /* 800/20 = 40MHz */
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
/* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */
si5351c_configure_multisynth(
&si5351c,
4,
20 * 128 - 512,
0,
1,
0); /* 800/20 = 40MHz */
/* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */
si5351c_configure_multisynth(
&si5351c,
5,
20 * 128 - 512,
0,
1,
0); /* 800/20 = 40MHz */
}
#endif
/* MS6/CLK6 is unused. */
/* MS7/CLK7 is unused. */
/* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */
sample_rate_set(SR_FP_MHZ(10), true);
si5351c_configure_clock_control(&si5351c);
si5351c_change_input(&si5351c, SI5351C_INPUT_XTAL);
// soft reset
si5351c_reset_plls(&si5351c, SI5351C_PLL_MASK_BOTH);
si5351c_enable_clock_outputs(&si5351c);
}
void clock_gen_shutdown(void)
{
si5351c_disable_all_outputs(&si5351c);
si5351c_disable_oeb_pin_control(&si5351c);
si5351c_power_down_all_clocks(&si5351c);
}
clock_source_t activate_best_clock_source(void)
{
#ifdef IS_EXPANSION_COMPATIBLE
if (IS_EXPANSION_COMPATIBLE) {
/* Ensure PortaPack reference oscillator is off while checking for external clock input. */
if (portapack_present()) {
portapack_reference_oscillator(false);
}
}
#endif
clock_source_t source = CLOCK_SOURCE_HACKRF;
/* Check for external clock input. */
if (si5351c_clkin_signal_valid(&si5351c)) {
source = CLOCK_SOURCE_EXTERNAL;
} else {
#ifdef IS_EXPANSION_COMPATIBLE
if (IS_EXPANSION_COMPATIBLE) {
/* Enable PortaPack reference oscillator (if present), and check for valid clock. */
if (portapack_present()) {
portapack_reference_oscillator(true);
delay_ms(18); // for oscillator to enable.
if (si5351c_clkin_signal_valid(&si5351c)) {
source = CLOCK_SOURCE_PORTAPACK;
} else {
portapack_reference_oscillator(false);
}
}
}
#endif
/* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */
}
si5351c_input_t input = (source == CLOCK_SOURCE_HACKRF) ? SI5351C_INPUT_XTAL :
SI5351C_INPUT_CLKIN;
si5351c_change_input(&si5351c, input);
hackrf_ui()->set_clock_source(source);
return source;
}
/*
* Closest fraction to m/d with denominator <= max_den.
* Returns result in *r / *s with gcd(*r, *s) == 1 and 0 < *s <= max_den.
* Straight port from CPython's fractions, and better documented there.
*/
void limit_denominator(
uint64_t m,
uint64_t d,
const uint64_t max_den,
uint64_t* r,
uint64_t* s)
{
if (d <= max_den) {
*r = m;
*s = d;
return;
}
uint64_t p0 = 0, q0 = 1, p1 = 1, q1 = 0;
uint64_t n = m, orig_d = d;
uint64_t tmp;
while (1) {
uint64_t a = n / d;
uint64_t q2 = q0 + a * q1;
if (q2 > max_den)
break;
tmp = p0 + a * p1;
p0 = p1;
q0 = q1;
p1 = tmp;
q1 = q2;
tmp = n - a * d;
n = d;
d = tmp;
if (d == 0)
break;
}
uint64_t k = (max_den - q0) / q1;
/* Return closer candidate. */
if (2 * d * (q0 + k * q1) <= orig_d) {
*r = p1;
*s = q1;
} else {
*r = p0 + k * p1;
*s = q0 + k * q1;
}
}
/*
* Configure clock generator to produce sample clock in units of 1/(2**36) Hz.
* Can be called with program=false for a dry run that returns the resultant
* frequency without actually configuring the clock generator.
*
* The clock generator output frequency is:
*
* fs = 128 * vco / (512 + p1 + p2/p3))
*
* where p1, p2, and p3 are register values.
*
* For more information see:
* https://www.pa3fwm.nl/technotes/tn42a-si5351-programming.html
*/
fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program)
{
const uint64_t vco_hz = 800 * 1000ULL * 1000ULL;
uint64_t p1, p2, p3;
uint64_t n, d, q1, q2, q3, r1, r2;
fp_28_36_t resultant_rate;
/*
* First double the sample rate so that we can produce a clock at twice
* the intended sample rate. The 2x clock is sometimes used directly,
* and it is divided by two in an output divider to produce the actual
* AFE clock.
*/
fp_28_36_t rate = sample_rate * 2;
/*
* Computes p1 = (N << 36) / rate - 512, where N = 128 * vco_hz.
*
* Full numerator (N << 36) is 73 bits, so we split the division:
*
* (N << 36) / rate = ((N << 27) / rate) << 9
* + (((N << 27) % rate) << 9) / rate
*
* IMPORTANT: Assumes sample rate is in [200e3 << 36, 43.6e6 << 36].
*/
const uint64_t A = (128 * vco_hz) << 27;
q1 = A / rate;
r1 = A % rate;
// Remaining 9 bits with long division.
q2 = 0;
r2 = r1;
for (int j = 0; j < 9; j++) {
uint64_t msb = r2 >> 63;
r2 <<= 1;
q2 <<= 1;
if (msb || r2 >= rate) {
r2 -= rate;
q2 |= 1;
}
}
p1 = (q1 << 9) + q2 - 512;
if (r2) {
/* Use the remainder for the fractional part. */
n = r2;
d = rate;
/* Reduce fraction. */
const uint64_t p3_max = 0xfffff;
limit_denominator(n, d, p3_max, &p2, &p3);
/* Roll over to next p1 to enable integer mode. */
if (p2 >= p3) {
p1++;
p2 = 0;
}
} else {
p2 = 0;
}
/* Maximum: (128 * 2048) - 512 */
if (p1 > 0x3fe00) {
p1 = 0x3fe00;
p2 = 0;
}
if (p2 == 0) {
/* Use unity denominator for integer mode. */
p3 = 1;
n = (128 * vco_hz) << 18;
d = (p1 + 512);
q1 = n / d;
r1 = n % d;
q2 = ((r1 << 18) + (d / 2)) / d;
resultant_rate = (q1 << 18) + q2;
} else {
n = p3 * vco_hz * 128;
d = p3 * (p1 + 512) + p2;
q1 = n / d;
r1 = n % d;
q2 = (r1 << 18) / d;
r2 = (r1 << 18) % d;
q3 = ((r2 << 18) + (d / 2)) / d;
resultant_rate = (q1 << 36) + (q2 << 18) + q3;
}
/* Return MCU sample rate, not AFE clock rate. */
resultant_rate = (resultant_rate + 1) / 2;
if (!program) {
return resultant_rate;
}
bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config);
if (streaming) {
sgpio_cpld_stream_disable(&sgpio_config);
}
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
/* Integer mode can be enabled if p1 is even and p2 is zero. */
if (p1 & 0x1 || p2) {
si5351c_set_int_mode(&si5351c, 0, 0);
} else {
si5351c_set_int_mode(&si5351c, 0, 1);
}
#ifdef IS_H1_R9
if (IS_H1_R9) {
/*
* On HackRF One r9 all sample clocks are externally derived
* from MS1/CLK1 operating at twice the sample rate.
*/
si5351c_configure_multisynth(&si5351c, 1, p1, p2, p3, 0);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
/*
* On other platforms the clock generator produces three
* different sample clocks, all derived from multisynth 0.
*/
/* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */
si5351c_configure_multisynth(&si5351c, 0, p1, p2, p3, 1);
/* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */
si5351c_configure_multisynth(
&si5351c,
1,
0,
0,
0,
0); //p1 doesn't matter
/* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */
si5351c_configure_multisynth(
&si5351c,
2,
0,
0,
0,
0); //p1 doesn't matter
}
#endif
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
/* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */
si5351c_configure_multisynth(&si5351c, 0, p1, p2, p3, 1);
/* MS1/CLK1 is the source for the FPGA (FPGA_CLK and SCT_CLK). */
si5351c_configure_multisynth(&si5351c, 1, p1, p2, p3, 1);
/* Delay FPGA_CLK relative to AFE_CLK. */
uint8_t phase_offset = 0;
if (p1 < 2100) {
phase_offset = (p1 >> 4) - 6;
}
si5351c_set_phase(&si5351c, 1, phase_offset);
if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) {
/*
* On older boards FPGA_CLK is on CLK2 while SCT_CLK is on
* CLK1. We configure both so that behavior is consistent with
* newer boards that use CLK1 for both FPGA_CLK and SCT_CLK.
*/
si5351c_configure_multisynth(&si5351c, 2, p1, p2, p3, 1);
si5351c_set_phase(&si5351c, 2, phase_offset);
}
/* Reset PLL to synchronize output clock phase. */
si5351c_reset_plls(&si5351c, SI5351C_PLL_MASK_A);
}
#endif
if (streaming) {
sgpio_cpld_stream_enable(&sgpio_config);
}
return resultant_rate;
}

View file

@ -1,47 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include "fixed_point.h"
typedef enum {
CLOCK_SOURCE_HACKRF = 0,
CLOCK_SOURCE_EXTERNAL = 1,
CLOCK_SOURCE_PORTAPACK = 2,
} clock_source_t;
void clock_gen_init(void);
void clock_gen_shutdown(void);
clock_source_t activate_best_clock_source(void);
fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program);
#ifdef __cplusplus
}
#endif

View file

@ -1,60 +0,0 @@
/*
* Copyright 2022-2026 Great Scott Gadgets
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
void clkin_detect_init(void);
uint32_t clkin_frequency(void);
void trigger_enable(const bool enable);
#ifdef IS_PRALINE
typedef enum {
CLKIN_SIGNAL_P1 = 0,
CLKIN_SIGNAL_P22 = 1,
} clkin_signal_t;
typedef enum {
P1_SIGNAL_TRIGGER_IN = 0,
P1_SIGNAL_AUX_CLK1 = 1,
P1_SIGNAL_CLKIN = 2,
P1_SIGNAL_TRIGGER_OUT = 3,
P1_SIGNAL_P22_CLKIN = 4,
P1_SIGNAL_P2_5 = 5,
P1_SIGNAL_NC = 6,
P1_SIGNAL_AUX_CLK2 = 7,
} p1_ctrl_signal_t;
typedef enum {
P2_SIGNAL_CLK3 = 0,
P2_SIGNAL_TRIGGER_IN = 2,
P2_SIGNAL_TRIGGER_OUT = 3,
} p2_ctrl_signal_t;
void clkin_ctrl_set(const clkin_signal_t value);
void p1_ctrl_set(const p1_ctrl_signal_t signal);
void p2_ctrl_set(const p2_ctrl_signal_t signal);
void pps_out_set(const uint8_t value);
#endif

View file

@ -22,12 +22,10 @@
#include "cpld_jtag.h"
#include "platform_detect.h"
#include "platform_gpio.h"
#ifdef IS_NOT_PRALINE
#include <stdbool.h>
#include <stdint.h>
#include "xapp058/micro.h"
#include "cpld_xc2c.h"
#endif
#ifdef IS_NOT_PRALINE
@ -36,35 +34,6 @@ static uint32_t xsvf_buffer_len, xsvf_pos;
static unsigned char* xsvf_buffer;
#endif
/* Driver instance. */
jtag_gpio_t jtag_gpio_cpld = {};
jtag_t jtag_cpld = {
.gpio = &jtag_gpio_cpld,
};
void cpld_jtag_pin_setup(void)
{
const platform_gpio_t* gpio = platform_gpio();
jtag_gpio_cpld.gpio_tck = gpio->cpld_tck;
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
jtag_gpio_cpld.gpio_tms = gpio->cpld_tms;
jtag_gpio_cpld.gpio_tdi = gpio->cpld_tdi;
jtag_gpio_cpld.gpio_tdo = gpio->cpld_tdo;
}
#endif
#ifdef IS_EXPANSION_COMPATIBLE
if (IS_EXPANSION_COMPATIBLE) {
jtag_gpio_cpld.gpio_pp_tms = gpio->cpld_pp_tms;
jtag_gpio_cpld.gpio_pp_tdo = gpio->cpld_pp_tdo;
}
#endif
}
void cpld_jtag_take(jtag_t* const jtag)
{
const jtag_gpio_t* const gpio = jtag->gpio;
@ -163,16 +132,4 @@ unsigned char cpld_jtag_get_next_byte(void)
xsvf_pos++;
return byte;
}
bool cpld_jtag_sram_load(jtag_t* const jtag)
{
cpld_jtag_take(jtag);
cpld_xc2c64a_jtag_sram_write(jtag, &cpld_hackrf_program_sram);
const bool success = cpld_xc2c64a_jtag_sram_verify(
jtag,
&cpld_hackrf_program_sram,
&cpld_hackrf_verify);
cpld_jtag_release(jtag);
return success;
}
#endif

View file

@ -21,10 +21,10 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "gpio.h"
#include "platform_detect.h"
typedef struct {
gpio_t gpio_tck;
@ -45,7 +45,6 @@ typedef struct {
typedef void (*refill_buffer_cb)(void);
void cpld_jtag_pin_setup(void);
void cpld_jtag_take(jtag_t* const jtag);
void cpld_jtag_release(jtag_t* const jtag);
@ -60,8 +59,3 @@ int cpld_jtag_program(
unsigned char* const buffer,
refill_buffer_cb refill);
unsigned char cpld_jtag_get_next_byte(void);
bool cpld_jtag_sram_load(jtag_t* const jtag);
/* Driver instance. */
extern jtag_gpio_t jtag_gpio_cpld;
extern jtag_t jtag_cpld;

View file

@ -1,262 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "cpu_clock.h"
#include <stdint.h>
#include <libopencm3/lpc43xx/cgu.h>
#if defined(IS_JAWBREAKER) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
#include <libopencm3/lpc43xx/ccu.h>
#endif
#include "delay.h"
#include "i2c_bus.h"
#include "i2c_lpc.h"
#include "si5351c.h"
/* We start with the CPU clock at 96MHz */
unsigned int cpu_clock_mhz = 96;
/*
Configure PLL1 (Main MCU Clock) to max speed (204MHz).
Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1.
This function shall be called after cpu_clock_init().
*/
static void cpu_clock_pll1_max_speed(void)
{
uint32_t reg_val;
/* This function implements the sequence recommended in:
* UM10503 Rev 2.4 (Aug 2018), section 13.2.1.1, page 167. */
/* 1. Select the IRC as BASE_M4_CLK source. */
reg_val = CGU_BASE_M4_CLK;
reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK;
reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1);
CGU_BASE_M4_CLK = reg_val;
/* CPU is now at 12MHz */
cpu_clock_mhz = 12;
/* 2. Enable the crystal oscillator. */
CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK;
/* 3. Wait 250us. */
delay_us(250);
/* 4. Set the AUTOBLOCK bit. */
CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1);
/* 5. Reconfigure PLL1 to produce the final output frequency, with the
* crystal oscillator as clock source. */
reg_val = CGU_PLL1_CTRL;
// clang-format off
reg_val &= ~( CGU_PLL1_CTRL_CLK_SEL_MASK |
CGU_PLL1_CTRL_PD_MASK |
CGU_PLL1_CTRL_FBSEL_MASK |
CGU_PLL1_CTRL_BYPASS_MASK |
CGU_PLL1_CTRL_DIRECT_MASK |
CGU_PLL1_CTRL_PSEL_MASK |
CGU_PLL1_CTRL_MSEL_MASK |
CGU_PLL1_CTRL_NSEL_MASK );
/* Set PLL1 up to 12MHz * 17 = 204MHz.
* Direct mode: FCLKOUT = FCCO = M*(FCLKIN/N) */
reg_val |= CGU_PLL1_CTRL_CLK_SEL(CGU_SRC_XTAL) |
CGU_PLL1_CTRL_PSEL(0) |
CGU_PLL1_CTRL_NSEL(0) |
CGU_PLL1_CTRL_MSEL(16) |
CGU_PLL1_CTRL_FBSEL(0) |
CGU_PLL1_CTRL_DIRECT(1);
// clang-format on
CGU_PLL1_CTRL = reg_val;
/* 6. Wait for PLL1 to lock. */
while (!(CGU_PLL1_STAT & CGU_PLL1_STAT_LOCK_MASK)) {}
/* 7. Set the PLL1 P-divider to divide by 2 (DIRECT=0, PSEL=0). */
CGU_PLL1_CTRL &= ~CGU_PLL1_CTRL_DIRECT_MASK;
/* 8. Select PLL1 as BASE_M4_CLK source. */
reg_val = CGU_BASE_M4_CLK;
reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK;
reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1);
CGU_BASE_M4_CLK = reg_val;
/* CPU is now at 102MHz */
cpu_clock_mhz = 102;
/* 9. Wait 50us. */
delay_us(50);
/* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */
CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK;
/* CPU is now at 204MHz */
cpu_clock_mhz = 204;
}
/* clock startup for LPC4320 configure PLL1 to max speed (204MHz).
Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. */
void cpu_clock_init(void)
{
/* use IRC as clock source for APB1 (including I2C0) */
CGU_BASE_APB1_CLK = CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_IRC);
/* use IRC as clock source for APB3 */
CGU_BASE_APB3_CLK = CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_IRC);
//FIXME disable I2C
/* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */
i2c_bus_start(si5351c.bus, &i2c_config_fast_clock);
/*
* 12MHz clock is entering LPC XTAL1/OSC input now.
* On HackRF One and Jawbreaker, there is a 12 MHz crystal at the LPC.
* Set up PLL1 to run from XTAL1 input.
*/
//FIXME a lot of the details here should be in a CGU driver
/* set xtal oscillator to low frequency mode */
CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_HF_MASK;
cpu_clock_pll1_max_speed();
/* use XTAL_OSC as clock source for APB1 */
CGU_BASE_APB1_CLK =
CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_XTAL);
/* use XTAL_OSC as clock source for APB3 */
CGU_BASE_APB3_CLK =
CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_XTAL);
/* use XTAL_OSC as clock source for PLL0USB */
CGU_PLL0USB_CTRL = CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_AUTOBLOCK(1) |
CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL);
while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) {}
/* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */
/* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */
CGU_PLL0USB_MDIV = 0x06167FFA;
CGU_PLL0USB_NP_DIV = 0x00302062;
CGU_PLL0USB_CTRL |=
(CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_DIRECTI(1) |
CGU_PLL0USB_CTRL_DIRECTO(1) | CGU_PLL0USB_CTRL_CLKEN(1));
/* power on PLL0USB and wait until stable */
CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK;
while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) {}
/* use PLL0USB as clock source for USB0 */
CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) |
CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB);
/* Switch peripheral clock over to use PLL1 (204MHz) */
CGU_BASE_PERIPH_CLK = CGU_BASE_PERIPH_CLK_AUTOBLOCK(1) |
CGU_BASE_PERIPH_CLK_CLK_SEL(CGU_SRC_PLL1);
/* Switch APB1 clock over to use PLL1 (204MHz) */
CGU_BASE_APB1_CLK =
CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_PLL1);
/* Switch APB3 clock over to use PLL1 (204MHz) */
CGU_BASE_APB3_CLK =
CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_PLL1);
CGU_BASE_SSP0_CLK =
CGU_BASE_SSP0_CLK_AUTOBLOCK(1) | CGU_BASE_SSP0_CLK_CLK_SEL(CGU_SRC_PLL1);
CGU_BASE_SSP1_CLK =
CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1);
#ifdef IS_NOT_RAD1O
if (IS_NOT_RAD1O) {
/* Disable unused clocks */
/* Start with PLLs */
CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1);
/* Dividers */
CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1);
CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1);
CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1);
CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1);
CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1);
/* Base clocks */
CGU_BASE_SPIFI_CLK =
CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */
CGU_BASE_USB1_CLK =
CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */
CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1);
CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1);
CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1);
CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1);
CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(1);
CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1);
CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1);
CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1);
CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1);
CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1);
CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1);
CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1);
CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1);
/* Disable unused peripheral clocks */
CCU1_CLK_APB1_CAN1_CFG = 0;
CCU1_CLK_APB1_I2S_CFG = 0;
CCU1_CLK_APB1_MOTOCONPWM_CFG = 0;
//CCU1_CLK_APB3_ADC0_CFG = 0;
CCU1_CLK_APB3_ADC1_CFG = 0;
CCU1_CLK_APB3_CAN0_CFG = 0;
CCU1_CLK_APB3_DAC_CFG = 0;
//CCU1_CLK_M4_DMA_CFG = 0;
CCU1_CLK_M4_EMC_CFG = 0;
CCU1_CLK_M4_EMCDIV_CFG = 0;
CCU1_CLK_M4_ETHERNET_CFG = 0;
CCU1_CLK_M4_LCD_CFG = 0;
CCU1_CLK_M4_QEI_CFG = 0;
CCU1_CLK_M4_RITIMER_CFG = 0;
// CCU1_CLK_M4_SCT_CFG = 0;
CCU1_CLK_M4_SDIO_CFG = 0;
CCU1_CLK_M4_SPIFI_CFG = 0;
CCU1_CLK_M4_TIMER0_CFG = 0;
//CCU1_CLK_M4_TIMER1_CFG = 0;
//CCU1_CLK_M4_TIMER2_CFG = 0;
CCU1_CLK_M4_TIMER3_CFG = 0;
CCU1_CLK_M4_UART1_CFG = 0;
CCU1_CLK_M4_USART0_CFG = 0;
CCU1_CLK_M4_USART2_CFG = 0;
CCU1_CLK_M4_USART3_CFG = 0;
CCU1_CLK_M4_USB1_CFG = 0;
CCU1_CLK_M4_VADC_CFG = 0;
// CCU1_CLK_SPIFI_CFG = 0;
// CCU1_CLK_USB1_CFG = 0;
// CCU1_CLK_VADC_CFG = 0;
// CCU2_CLK_APB0_UART1_CFG = 0;
// CCU2_CLK_APB0_USART0_CFG = 0;
// CCU2_CLK_APB2_USART2_CFG = 0;
// CCU2_CLK_APB2_USART3_CFG = 0;
// CCU2_CLK_APLL_CFG = 0;
// CCU2_CLK_SDIO_CFG = 0;
}
#endif
}

View file

@ -1,35 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void cpu_clock_init(void);
// Current clock speed in MHz, updated on clock changes.
extern unsigned int cpu_clock_mhz;
#ifdef __cplusplus
}
#endif

View file

@ -22,8 +22,8 @@
#include <stdint.h>
#include "da7219.h"
#include "hackrf_core.h"
#include "i2c_bus.h"
#include "i2c_lpc.h"
#define DA7219_REG_CHIP_ID1 0x81
#define DA7219_REG_CHIP_ID2 0x82

View file

@ -21,9 +21,16 @@
#include "delay.h"
#include "cpu_clock.h"
void delay(uint32_t duration)
{
uint32_t i;
static void delay_us_at_mhz(uint32_t us, uint32_t mhz)
for (i = 0; i < duration; i++) {
__asm__("nop");
}
}
void delay_us_at_mhz(uint32_t us, uint32_t mhz)
{
#if defined(LPC43XX_M4)
// The loop below takes 3 cycles per iteration.
@ -45,13 +52,3 @@ static void delay_us_at_mhz(uint32_t us, uint32_t mhz)
#error "No delay loop implementation"
#endif
}
void delay_us(uint32_t us)
{
delay_us_at_mhz(us, cpu_clock_mhz);
}
void delay_ms(uint32_t ms)
{
delay_us_at_mhz(ms * 1000, cpu_clock_mhz);
}

View file

@ -27,8 +27,8 @@ extern "C" {
#include <stdint.h>
void delay_us(uint32_t us);
void delay_ms(uint32_t ms);
void delay(uint32_t duration);
void delay_us_at_mhz(uint32_t us, uint32_t mhz);
#ifdef __cplusplus
}

View file

@ -54,20 +54,3 @@ typedef uint64_t fp_28_36_t;
#define SR_FP_MHZ(mhz) (mhz##ULL * SR_FP_ONE_MHZ)
#define SR_FP_KHZ(khz) (khz##ULL * SR_FP_ONE_KHZ)
#define SR_FP_HZ(hz) (hz##ULL * SR_FP_ONE_HZ)
/* 1.63 fixed point */
typedef uint64_t fp_1_63_t;
#define FRAC_ONE (1ULL << 63)
/* one hundredth in 1.63 fixed point */
#define FRAC_ONE_PERCENT (FRAC_ONE / (100ULL))
/* one thousandth in 1.63 fixed point */
#define FRAC_ONE_PERMILLE (FRAC_ONE / (1000ULL))
/* one millionth in 1.63 fixed point */
#define FRAC_ONE_PPM (FRAC_ONE / (1000000ULL))
/* one billionth in 1.63 fixed point */
#define FRAC_ONE_PPB (FRAC_ONE / (1000000000ULL))

View file

@ -20,17 +20,13 @@
*/
#include "fpga.h"
#include "fpga_regs.def"
#include <stdbool.h>
#include "fpga_regs.def"
#include "hackrf_core.h"
#include "ice40_spi.h"
/* Driver instance. */
fpga_driver_t fpga = {
.bus = &ice40,
};
/* Set up all registers according to the loaded bitstream's defaults. */
void fpga_init(fpga_driver_t* const drv)
{
@ -56,7 +52,9 @@ void fpga_setup(fpga_driver_t* const drv)
uint8_t fpga_reg_read(fpga_driver_t* const drv, uint8_t r)
{
uint8_t v;
ssp1_set_mode_ice40();
v = ice40_spi_read(drv->bus, r);
ssp1_set_mode_max283x();
drv->regs[r] = v;
return v;
}
@ -64,7 +62,9 @@ uint8_t fpga_reg_read(fpga_driver_t* const drv, uint8_t r)
void fpga_reg_write(fpga_driver_t* const drv, uint8_t r, uint8_t v)
{
drv->regs[r] = v;
ssp1_set_mode_ice40();
ice40_spi_write(drv->bus, r, v);
ssp1_set_mode_max283x();
FPGA_REG_SET_CLEAN(drv, r);
}

View file

@ -90,6 +90,3 @@ bool fpga_image_load(struct fpga_loader_t* loader, unsigned int index);
bool fpga_spi_selftest(void);
bool fpga_sgpio_selftest(void);
bool fpga_if_xcvr_selftest(void);
/* Driver instance. */
extern fpga_driver_t fpga;

View file

@ -24,6 +24,7 @@
#include <stdint.h>
#include "fpga.h"
#include "hackrf_core.h"
#include "ice40_spi.h"
#include "lz4_blk.h"
#include "selftest.h"
@ -85,6 +86,7 @@ bool fpga_image_load(struct fpga_loader_t* loader, unsigned int index)
// A callback function is used by the FPGA programmer
// to obtain consecutive gateware chunks.
ice40_spi_target_init(&ice40);
ssp1_set_mode_ice40();
struct fpga_image_read_ctx fpga_image_ctx = {
.loader = loader,
.addr = loader->start_addr + bitstream_offset,
@ -94,6 +96,7 @@ bool fpga_image_load(struct fpga_loader_t* loader, unsigned int index)
loader->out_buffer,
fpga_image_read_block_cb,
&fpga_image_ctx);
ssp1_set_mode_max283x();
// Update selftest result.
selftest.fpga_image_load = success ? PASSED : FAILED;

View file

@ -23,10 +23,10 @@
#include <stddef.h>
#include <stdint.h>
#include "clock_gen.h"
#include "delay.h"
#include "fixed_point.h"
#include "fpga.h"
#include "hackrf_core.h"
#include "ice40_spi.h"
#include "m0_state.h"
#include "max283x.h"
@ -36,8 +36,8 @@
#include "streaming.h"
// USB buffer used during selftests.
#define USB_SAMP_BUFFER_SIZE 0x8000
extern uint8_t usb_samp_buffer[USB_SAMP_BUFFER_SIZE];
#define USB_BULK_BUFFER_SIZE 0x8000
extern uint8_t usb_bulk_buffer[USB_BULK_BUFFER_SIZE];
static int rx_samples(const unsigned int num_samples, uint32_t max_cycles)
{
@ -71,8 +71,10 @@ bool fpga_spi_selftest(void)
// Test writing a register and reading it back.
uint8_t reg = 6;
uint8_t write_value = 0xA5;
ssp1_set_mode_ice40();
ice40_spi_write(&ice40, reg, write_value);
uint8_t read_value = ice40_spi_read(&ice40, reg);
ssp1_set_mode_max283x();
// Update selftest result.
selftest.fpga_spi = (read_value == write_value) ? PASSED : FAILED;
@ -112,10 +114,10 @@ bool fpga_sgpio_selftest(void)
fpga_set_prbs_enable(&fpga, false);
// Generate sequence from first value and compare.
bool seq_in_sync = (usb_samp_buffer[0] != 0);
uint8_t seq = lfsr_advance(usb_samp_buffer[0]);
bool seq_in_sync = (usb_bulk_buffer[0] != 0);
uint8_t seq = lfsr_advance(usb_bulk_buffer[0]);
for (int i = 1; i < 512; ++i) {
if (usb_samp_buffer[i] != seq) {
if (usb_bulk_buffer[i] != seq) {
seq_in_sync = false;
break;
}
@ -186,7 +188,7 @@ bool fpga_if_xcvr_selftest(void)
return false;
}
const size_t num_samples = USB_SAMP_BUFFER_SIZE / 2;
const size_t num_samples = USB_BULK_BUFFER_SIZE / 2;
// Set common RX path and gateware settings for the measurements.
fpga_set_tx_nco_pstep(&fpga, 64); // NCO phase increment
@ -198,23 +200,23 @@ bool fpga_if_xcvr_selftest(void)
// Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF
sample_rate_set(SR_FP_MHZ(4), true);
delay_ms(1);
delay_us_at_mhz(1000, 204);
if (rx_samples(num_samples, 2000000) == -1) {
timeout = true;
}
measure_tone(
(int8_t*) usb_samp_buffer,
(int8_t*) usb_bulk_buffer,
num_samples,
&selftest.xcvr_measurements[0]);
// Capture 2: 4 Msps, tone at 0.5 MHz, narrowband filter ON
narrowband_filter_set(1);
delay_ms(1);
delay_us_at_mhz(1000, 204);
if (rx_samples(num_samples, 2000000) == -1) {
timeout = true;
}
measure_tone(
(int8_t*) usb_samp_buffer,
(int8_t*) usb_bulk_buffer,
num_samples,
&selftest.xcvr_measurements[1]);
@ -222,23 +224,23 @@ bool fpga_if_xcvr_selftest(void)
fpga_set_tx_nco_pstep(&fpga, 255);
sample_rate_set(SR_FP_MHZ(20), true);
narrowband_filter_set(0);
delay_ms(1);
delay_us_at_mhz(1000, 204);
if (rx_samples(num_samples, 2000000) == -1) {
timeout = true;
}
measure_tone(
(int8_t*) usb_samp_buffer,
(int8_t*) usb_bulk_buffer,
num_samples,
&selftest.xcvr_measurements[2]);
// Capture 4: 20 Msps, tone at 5 MHz, narrowband filter ON
narrowband_filter_set(1);
delay_ms(1);
delay_us_at_mhz(1000, 204);
if (rx_samples(num_samples, 2000000) == -1) {
timeout = true;
}
measure_tone(
(int8_t*) usb_samp_buffer,
(int8_t*) usb_bulk_buffer,
num_samples,
&selftest.xcvr_measurements[3]);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,148 @@
/*
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Benjamin Vernoux <titanmkd@gmail.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include "cpld_jtag.h"
#include "fixed_point.h"
#include "i2c_bus.h"
#include "max283x.h"
#include "max5864.h"
#include "mixer.h"
#include "platform_detect.h" // IWYU pragma: keep
#include "radio.h"
#include "rf_path.h"
#include "sgpio.h"
#include "si5351c.h"
#include "spi_ssp.h"
#include "w25q80bv.h"
#ifdef IS_PRALINE
#include "fpga.h"
#include "ice40_spi.h"
#endif
/* TODO: Hide these configurations */
extern si5351c_driver_t clock_gen;
extern ssp_config_t ssp_config_w25q80bv;
extern max283x_driver_t max283x;
#ifdef IS_PRALINE
extern ice40_spi_driver_t ice40;
extern fpga_driver_t fpga;
#endif
extern max283x_driver_t max283x;
extern max5864_driver_t max5864;
extern mixer_driver_t mixer;
extern w25q80bv_driver_t spi_flash;
extern sgpio_config_t sgpio_config;
extern radio_t radio;
extern rf_path_t rf_path;
extern jtag_t jtag_cpld;
extern i2c_bus_t i2c0;
void cpu_clock_init(void);
void clock_gen_init(void);
void clock_gen_shutdown(void);
void ssp1_set_mode_max283x(void);
void ssp1_set_mode_max5864(void);
#ifdef IS_PRALINE
void ssp1_set_mode_ice40(void);
#endif
void pin_shutdown(void);
void pin_setup(void);
#ifdef IS_PRALINE
void enable_1v2_power(void);
void disable_1v2_power(void);
void enable_3v3aux_power(void);
void disable_3v3aux_power(void);
#endif
void enable_1v8_power(void);
void disable_1v8_power(void);
fp_28_36_t sample_rate_set(const fp_28_36_t sample_rate, const bool program);
clock_source_t activate_best_clock_source(void);
#if defined(IS_RAD1O) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
void enable_rf_power(void);
void disable_rf_power(void);
#endif
typedef enum {
LED1 = 0,
LED2 = 1,
LED3 = 2,
LED4 = 3,
} led_t;
void led_on(const led_t led);
void led_off(const led_t led);
void led_toggle(const led_t led);
void set_leds(const uint8_t state);
void trigger_enable(const bool enable);
void halt_and_flash(const uint32_t duration);
#ifdef IS_PRALINE
typedef enum {
P1_SIGNAL_TRIGGER_IN = 0,
P1_SIGNAL_AUX_CLK1 = 1,
P1_SIGNAL_CLKIN = 2,
P1_SIGNAL_TRIGGER_OUT = 3,
P1_SIGNAL_P22_CLKIN = 4,
P1_SIGNAL_P2_5 = 5,
P1_SIGNAL_NC = 6,
P1_SIGNAL_AUX_CLK2 = 7,
} p1_ctrl_signal_t;
typedef enum {
P2_SIGNAL_CLK3 = 0,
P2_SIGNAL_TRIGGER_IN = 2,
P2_SIGNAL_TRIGGER_OUT = 3,
} p2_ctrl_signal_t;
typedef enum {
CLKIN_SIGNAL_P1 = 0,
CLKIN_SIGNAL_P22 = 1,
} clkin_signal_t;
void p1_ctrl_set(const p1_ctrl_signal_t signal);
void p2_ctrl_set(const p2_ctrl_signal_t signal);
void narrowband_filter_set(const uint8_t value);
void clkin_ctrl_set(const clkin_signal_t value);
void pps_out_set(const uint8_t value);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -23,6 +23,7 @@
#include <stddef.h>
#include "hackrf_ui.h"
#include "platform_detect.h" // IWYU pragma: keep
#include "transceiver_mode.h"
#ifdef IS_EXPANSION_COMPATIBLE
#include "ui_portapack.h"

View file

@ -25,7 +25,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "clock_gen.h"
#include "radio.h"
#include "rf_path.h"
#include "transceiver_mode.h"

View file

@ -23,28 +23,6 @@
#include "i2c_lpc.h"
#include <libopencm3/lpc43xx/i2c.h>
#include <libopencm3/lpc43xx/memorymap.h>
#include "cpu_clock.h"
/* Driver instances. */
i2c_bus_t i2c0 = {
.obj = (void*) I2C0_BASE,
.start = i2c_lpc_start,
.stop = i2c_lpc_stop,
.transfer = i2c_lpc_transfer,
};
i2c_bus_t i2c1 = {
.obj = (void*) I2C1_BASE,
.start = i2c_lpc_start,
.stop = i2c_lpc_stop,
.transfer = i2c_lpc_transfer,
};
const i2c_lpc_config_t i2c_config_fast_clock = {
.clock_khz = 400,
};
/* FIXME return i2c0 status from each function */
@ -53,9 +31,7 @@ void i2c_lpc_start(i2c_bus_t* const bus, const void* const _config)
const i2c_lpc_config_t* const config = _config;
const uint32_t port = (uint32_t) bus->obj;
const uint16_t duty_cycle_count =
(cpu_clock_mhz * (2000 / config->clock_khz)) / 4;
i2c_init(port, duty_cycle_count);
i2c_init(port, config->duty_cycle_count);
}
void i2c_lpc_stop(i2c_bus_t* const bus)

View file

@ -29,7 +29,7 @@
#include "i2c_bus.h"
typedef struct {
const uint16_t clock_khz;
const uint16_t duty_cycle_count;
} i2c_lpc_config_t;
void i2c_lpc_start(i2c_bus_t* const bus, const void* const config);
@ -42,8 +42,3 @@ void i2c_lpc_transfer(
uint8_t* const data_rx,
const size_t count_rx);
bool i2c_probe(i2c_bus_t* const bus, const uint_fast8_t device_address);
/* Driver instances. */
extern const i2c_lpc_config_t i2c_config_fast_clock;
extern i2c_bus_t i2c0;
extern i2c_bus_t i2c1;

View file

@ -26,32 +26,12 @@
#include <libopencm3/lpc43xx/ssp.h>
#include "delay.h"
#include "platform_gpio.h"
#include "platform_scu.h"
/* Driver instance. */
ssp_config_t ssp_config_ice40_fpga = {
.data_bits = SSP_DATA_8BITS,
.spi_mode = SSP_CPOL_1_CPHA_1,
.serial_clock_rate = 21,
.clock_prescale_rate = 2,
};
ice40_spi_driver_t ice40 = {
.bus = &spi_bus_ssp1,
};
void ice40_spi_target_init(ice40_spi_driver_t* const drv)
{
const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
/* Configure drivers and driver pins */
ssp_config_ice40_fpga.gpio_select = gpio->fpga_cfg_spi_cs;
ice40.gpio_select = gpio->fpga_cfg_spi_cs;
ice40.gpio_creset = gpio->fpga_cfg_creset;
ice40.gpio_cdone = gpio->fpga_cfg_cdone;
/* Configure SSP1 Peripheral and relevant FPGA pins. */
scu_pinmux(scu->SSP1_CIPO, (SCU_SSP_IO | SCU_CONF_FUNCTION5));
scu_pinmux(scu->SSP1_COPI, (SCU_SSP_IO | SCU_CONF_FUNCTION5));
@ -70,14 +50,14 @@ void ice40_spi_target_init(ice40_spi_driver_t* const drv)
uint8_t ice40_spi_read(ice40_spi_driver_t* const drv, uint8_t r)
{
uint8_t value[3] = {r & 0x7F, 0, 0};
spi_bus_transfer(drv->bus, &ssp_config_ice40_fpga, value, 3);
spi_bus_transfer(drv->bus, value, 3);
return value[2];
}
void ice40_spi_write(ice40_spi_driver_t* const drv, uint8_t r, uint16_t v)
{
uint8_t value[3] = {(r & 0x7F) | 0x80, v, 0};
spi_bus_transfer(drv->bus, &ssp_config_ice40_fpga, value, 3);
spi_bus_transfer(drv->bus, value, 3);
}
static void spi_ssp1_wait_for_tx_fifo_not_full(void)
@ -110,21 +90,19 @@ bool ice40_spi_syscfg_program(
size_t (*read_block_cb)(void* ctx),
void* read_ctx)
{
spi_bus_start(drv->bus, &ssp_config_ice40_fpga);
// Drive CRESET_B = 0, SPI_SS = 0, SPI_SCK = 1.
gpio_clear(drv->gpio_creset);
gpio_clear(drv->gpio_select);
// Wait a minimum of 200 ns.
delay_us(1);
delay_us_at_mhz(1, 204 / 4); // 250 ns.
// Release CRESET_B or drive CRESET_B = 1.
gpio_set(drv->gpio_creset);
// Wait a minimum of 1200 μs to clear internal configuration memory.
// Testing showed us that we need to wait longer. Let's wait 1800 μs.
delay_us(1800);
delay_us_at_mhz(1800, 204);
// Set SPI_SS = 1, Send 8 dummy clocks.
gpio_set(drv->gpio_select);

View file

@ -27,7 +27,6 @@
#include "gpio.h"
#include "spi_bus.h"
#include "spi_ssp.h"
typedef struct {
spi_bus_t* const bus;
@ -44,7 +43,3 @@ bool ice40_spi_syscfg_program(
uint8_t* buf,
size_t (*read_block_cb)(void* ctx),
void* read_ctx);
/* Driver instance. */
extern ssp_config_t ssp_config_ice40_fpga;
extern ice40_spi_driver_t ice40;

View file

@ -1,100 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "leds.h"
#include <stdint.h>
#include "delay.h"
#include "gpio.h"
#include "platform_detect.h"
#include "platform_gpio.h"
void led_on(const led_t led)
{
#ifdef IS_PRALINE
if (IS_PRALINE) {
gpio_clear(platform_gpio()->led[led]);
}
#endif
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
gpio_set(platform_gpio()->led[led]);
}
#endif
}
void led_off(const led_t led)
{
#ifdef IS_PRALINE
if (IS_PRALINE) {
gpio_set(platform_gpio()->led[led]);
}
#endif
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
gpio_clear(platform_gpio()->led[led]);
}
#endif
}
void led_toggle(const led_t led)
{
gpio_toggle(platform_gpio()->led[led]);
}
void set_leds(const uint8_t state)
{
int num_leds = 3;
#ifdef IS_FOUR_LEDS
if (IS_FOUR_LEDS) {
num_leds = 4;
}
#endif
for (int i = 0; i < num_leds; i++) {
#ifdef IS_PRALINE
if (IS_PRALINE) {
gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 0);
}
#endif
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 1);
}
#endif
}
}
void halt_and_flash(const uint32_t period_ms)
{
/* blink LED1, LED2, and LED3 */
while (1) {
led_on(LED1);
led_on(LED2);
led_on(LED3);
delay_ms(period_ms / 2);
led_off(LED1);
led_off(LED2);
led_off(LED3);
delay_ms(period_ms / 2);
}
}

View file

@ -1,46 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef enum {
LED1 = 0,
LED2 = 1,
LED3 = 2,
LED4 = 3,
} led_t;
void led_on(const led_t led);
void led_off(const led_t led);
void led_toggle(const led_t led);
void set_leds(const uint8_t state);
void halt_and_flash(const uint32_t duration);
#ifdef __cplusplus
}
#endif

View file

@ -109,7 +109,7 @@ static void max2831_write(max2831_driver_t* const drv, uint8_t r, uint16_t v)
{
uint32_t word = (((uint32_t) v & 0x3fff) << 4) | (r & 0xf);
uint16_t values[2] = {word >> 9, word & 0x1ff};
spi_bus_transfer(drv->bus, drv->config, values, 2);
spi_bus_transfer(drv->bus, values, 2);
}
uint16_t max2831_reg_read(max2831_driver_t* const drv, uint8_t r)

View file

@ -50,7 +50,6 @@ typedef enum {
typedef struct _max2831_driver_t {
spi_bus_t* bus;
void* config;
gpio_t gpio_enable;
gpio_t gpio_rxtx;
gpio_t gpio_rxhp;

View file

@ -156,14 +156,14 @@ void max2837_setup(max2837_driver_t* const drv)
static uint16_t max2837_read(max2837_driver_t* const drv, uint8_t r)
{
uint16_t value = (1 << 15) | (r << 10);
spi_bus_transfer(drv->bus, drv->config, &value, 1);
spi_bus_transfer(drv->bus, &value, 1);
return value & 0x3ff;
}
static void max2837_write(max2837_driver_t* const drv, uint8_t r, uint16_t v)
{
uint16_t value = (r << 10) | (v & 0x3ff);
spi_bus_transfer(drv->bus, drv->config, &value, 1);
spi_bus_transfer(drv->bus, &value, 1);
}
uint16_t max2837_reg_read(max2837_driver_t* const drv, uint8_t r)

View file

@ -43,7 +43,6 @@ typedef enum {
typedef struct _max2837_driver_t {
spi_bus_t* bus;
void* config;
gpio_t gpio_enable;
gpio_t gpio_rx_enable;
gpio_t gpio_tx_enable;

View file

@ -164,14 +164,14 @@ void max2839_setup(max2839_driver_t* const drv)
static uint16_t max2839_read(max2839_driver_t* const drv, uint8_t r)
{
uint16_t value = (1 << 15) | (r << 10);
spi_bus_transfer(drv->bus, drv->config, &value, 1);
spi_bus_transfer(drv->bus, &value, 1);
return value & 0x3ff;
}
static void max2839_write(max2839_driver_t* const drv, uint8_t r, uint16_t v)
{
uint16_t value = (r << 10) | (v & 0x3ff);
spi_bus_transfer(drv->bus, drv->config, &value, 1);
spi_bus_transfer(drv->bus, &value, 1);
}
uint16_t max2839_reg_read(max2839_driver_t* const drv, uint8_t r)

View file

@ -46,7 +46,6 @@ typedef enum {
typedef struct _max2839_driver_t {
spi_bus_t* bus;
void* config;
gpio_t gpio_enable;
gpio_t gpio_rxtx;
void (*target_init)(struct _max2839_driver_t* const drv);

View file

@ -26,11 +26,10 @@
#include <stdbool.h>
#include <string.h>
#include <libopencm3/lpc43xx/ssp.h>
#include "fixed_point.h"
#include "platform_detect.h"
#include "platform_gpio.h"
#include "spi_bus.h"
#ifdef IS_PRALINE
#include "max2831_target.h"
@ -42,25 +41,11 @@
#include "max2839_target.h"
#endif
/* Driver instance. */
ssp_config_t ssp_config_max283x = {
/* FIXME speed up once everything is working reliably */
/*
// Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz
const uint8_t serial_clock_rate = 32;
const uint8_t clock_prescale_rate = 128;
*/
// Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz
.serial_clock_rate = 21,
.clock_prescale_rate = 2,
};
max283x_driver_t max283x = {};
extern spi_bus_t spi_bus_ssp1;
#ifdef IS_PRALINE
max2831_driver_t max2831 = {
.bus = &spi_bus_ssp1,
.config = &ssp_config_max283x,
.target_init = max2831_target_init,
.set_mode = max2831_target_set_mode,
};
@ -69,7 +54,6 @@ max2831_driver_t max2831 = {
#ifdef IS_NOT_PRALINE
max2837_driver_t max2837 = {
.bus = &spi_bus_ssp1,
.config = &ssp_config_max283x,
.target_init = max2837_target_init,
.set_mode = max2837_target_set_mode,
};
@ -78,7 +62,6 @@ max2837_driver_t max2837 = {
#ifdef IS_HACKRF_ONE
max2839_driver_t max2839 = {
.bus = &spi_bus_ssp1,
.config = &ssp_config_max283x,
.target_init = max2839_target_init,
.set_mode = max2839_target_set_mode,
};
@ -90,10 +73,8 @@ void max283x_setup(max283x_driver_t* const drv)
const platform_gpio_t* gpio = platform_gpio();
/* MAX283x GPIO PinMux */
ssp_config_max283x.gpio_select = gpio->max283x_select;
#ifdef IS_PRALINE
if (IS_PRALINE) {
ssp_config_max283x.data_bits = SSP_DATA_9BITS;
drv->type = MAX2831_VARIANT;
max2831.gpio_enable = gpio->max283x_enable;
max2831.gpio_rxtx = gpio->max283x_rx_enable;
@ -105,7 +86,6 @@ void max283x_setup(max283x_driver_t* const drv)
#endif
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
ssp_config_max283x.data_bits = SSP_DATA_16BITS;
#ifdef IS_H1_R9
if (IS_H1_R9) {
drv->type = MAX2839_VARIANT;

View file

@ -27,7 +27,7 @@
#include <stdint.h>
#include "fixed_point.h"
#include "spi_ssp.h"
#include "platform_detect.h"
#ifdef IS_PRALINE
#include "max2831.h"
@ -139,7 +139,3 @@ void max283x_set_rx_hpf_frequency(
/* Perform MAX2831 TX and RX calibration. */
void max283x_tx_calibration(max283x_driver_t* const drv);
void max283x_rx_calibration(max283x_driver_t* const drv);
/* Driver instance. */
extern ssp_config_t ssp_config_max283x;
extern max283x_driver_t max283x;

View file

@ -24,34 +24,9 @@
#include <stdint.h>
#include <libopencm3/lpc43xx/ssp.h>
#include "max5864_target.h"
#include "platform_gpio.h"
#include "spi_bus.h"
/* Driver instance. */
ssp_config_t ssp_config_max5864 = {
/* FIXME speed up once everything is working reliably */
/*
// Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz
const uint8_t serial_clock_rate = 32;
const uint8_t clock_prescale_rate = 128;
*/
// Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz
.data_bits = SSP_DATA_8BITS,
.serial_clock_rate = 21,
.clock_prescale_rate = 2,
};
max5864_driver_t max5864 = {
.bus = &spi_bus_ssp1,
.target_init = max5864_target_init,
};
static void max5864_write(max5864_driver_t* const drv, uint8_t value)
{
spi_bus_transfer(drv->bus, &ssp_config_max5864, &value, 1);
spi_bus_transfer(drv->bus, &value, 1);
}
static void max5864_init(max5864_driver_t* const drv)
@ -61,8 +36,6 @@ static void max5864_init(max5864_driver_t* const drv)
void max5864_setup(max5864_driver_t* const drv)
{
ssp_config_max5864.gpio_select = platform_gpio()->max5864_select;
max5864_init(drv);
}

View file

@ -23,7 +23,6 @@
#pragma once
#include "spi_bus.h"
#include "spi_ssp.h"
typedef struct _max5864_driver_t {
spi_bus_t* const bus;
@ -38,7 +37,3 @@ void max5864_idle(max5864_driver_t* const drv);
void max5864_rx(max5864_driver_t* const drv);
void max5864_tx(max5864_driver_t* const drv);
void max5864_xcvr(max5864_driver_t* const drv);
/* Driver instance. */
extern ssp_config_t ssp_config_max5864;
extern max5864_driver_t max5864;

View file

@ -26,6 +26,8 @@
#include <stdint.h>
#include "fixed_point.h"
#include "platform_detect.h"
#ifdef IS_RAD1O
#include "max2871.h"
#endif
@ -63,6 +65,3 @@ extern fp_40_24_t mixer_set_frequency(
extern void mixer_enable(mixer_driver_t* const mixer);
extern void mixer_disable(mixer_driver_t* const mixer);
extern void mixer_set_gpo(mixer_driver_t* const drv, uint8_t gpo);
/* Mixer instance. */
extern mixer_driver_t mixer;

View file

@ -28,8 +28,8 @@
#include "gpio.h"
#include "gpio_lpc.h"
#include "hackrf_core.h"
#include "i2c_bus.h"
#include "i2c_lpc.h"
#include "operacake_sctimer.h"
#include "platform_scu.h"

View file

@ -28,8 +28,8 @@
#include "delay.h"
#include "platform_detect.h"
#include "platform_scu.h"
#include "sct.h"
#ifdef IS_NOT_PRALINE
#include <libopencm3/cm3/common.h>
#include <libopencm3/lpc43xx/sgpio.h>
@ -62,8 +62,6 @@ static uint32_t default_output = 0;
*/
void operacake_sctimer_init(void)
{
const platform_scu_t* scu = platform_scu();
// We start by resetting the SCTimer
RESET_CTRL1 = RESET_CTRL1_SCT_RST;
@ -75,19 +73,29 @@ void operacake_sctimer_init(void)
// there are additional instructions that fill the time. If the duration of
// the actions from here to the first access to the SCTimer is changed, then
// this delay may need to be increased.
delay_us(1);
delay(8);
// Pin definitions for the HackRF
// U2CTRL0
scu_pinmux(scu->CTOUT_13, scu->CTOUT_PINCFG);
scu_pinmux(
P7_4,
SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
// U2CTRL1
scu_pinmux(scu->CTOUT_12, scu->CTOUT_PINCFG);
scu_pinmux(
P7_5,
SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
// U3CTRL0
scu_pinmux(scu->CTOUT_11, scu->CTOUT_PINCFG);
scu_pinmux(
P7_6,
SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
// U3CTRL1
scu_pinmux(scu->CTOUT_8, scu->CTOUT_PINCFG);
scu_pinmux(
P7_7,
SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
// U1CTRL
scu_pinmux(scu->CTOUT_14, scu->CTOUT_PINCFG);
scu_pinmux(
P7_0,
SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
uint8_t sct_clock_input;
#ifdef IS_NOT_PRALINE
@ -106,8 +114,8 @@ void operacake_sctimer_init(void)
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
// Configure pin P6_4 as CTIN_6
scu_pinmux(scu->SCT_CLK, scu->SCT_CLK_PINCFG);
// Configure pin P6_4 as SCT_IN_6
scu_pinmux(P6_4, SCU_CLK_IN | SCU_CONF_FUNCTION1);
// Use the GIMA to connect MS0/CLK1 (SCT_CLK) on pin P6_4 to the SCTimer
GIMA_CTIN_6_IN = 0x0 << 4;

View file

@ -1,433 +0,0 @@
/* MIT License
*
* Copyright (c) 2023-2026 rom-p
*
* 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 "picoprintf.h"
#include <stdlib.h> // llabs()
#ifdef PICOFORMAT_HANDLE_FLOATS
#include <math.h> // fabs()
#endif
static void flip(char* pLeft, char* pRight)
{
pRight--;
while (pLeft < pRight) {
char tmp = *pLeft;
*pLeft = *pRight;
*pRight = tmp;
pLeft++;
pRight--;
}
}
// using #define over `inline` for enabling porting to old C
#if !defined(MIN)
#define MIN(left, right) (((left) < (right)) ? (left) : (right))
#endif
#if !defined(MAX)
#define MAX(left, right) (((left) > (right)) ? (left) : (right))
#endif
// returns the pointer to the null-terminating character of the filled string
int pico_vsnprintf(char* pDest, size_t cbDest, const char* pFormat, va_list vl)
{
#if defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || \
defined(PICOFORMAT_HANDLE_HEX)
const char* pLowercaseNumberDigits = "0123456789abcdef";
#endif
#if defined(PICOFORMAT_HANDLE_HEX)
const char* pUppercaseNumberDigits = "0123456789ABCDEF";
#elif defined(PICOFORMAT_HANDLE_OCT)
const char* pOctalDigits = pLowercaseNumberDigits;
#elif defined(PICOFORMAT_HANDLE_BIN)
const char* pBinaryDigits = pLowercaseNumberDigits;
#endif
char* pStart = pDest;
for (char* pEnd = pDest + cbDest - 1; *pFormat && pDest < pEnd;) {
if (*pFormat != '%') {
*pDest++ = *pFormat++;
} else { // format starts here
pFormat++; // skipping the '%'
if (*pFormat == '%') { // unless it's indeed a '%'
*pDest++ = *pFormat++;
} else { // first, collect the format
char format = '\0';
#if defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || \
defined(PICOFORMAT_HANDLE_HEX)
int bits_per_digit =
0; // valid in 'b', 'o', 'p' and 'x'/'X' modes only
#endif
int whole_chars =
0; // number of chars in the whole part of the number
int decimal_chars =
-1; // if not specified, %f are rendered with 6, while %g are rendered with 0
#ifdef __aarch64__ // these platforms benefit from packing flags into a single variable
struct {
unsigned force_sign : 1;
unsigned fill_zeros : 1;
unsigned left_align : 1;
unsigned seen_period : 1;
unsigned seen_numbers : 1;
unsigned treat_as_unsigned : 1;
unsigned treat_as_long : 1;
unsigned render_in_lowercase : 1;
} flags = {0};
#define FLAGS flags.
#else // other platforms do not benefit from struct packing
unsigned force_sign = 0;
unsigned fill_zeros = 0;
#ifdef PICOFORMAT_HANDLE_FILL
unsigned left_align = 0;
#endif
unsigned seen_period = 0;
unsigned seen_numbers = 0;
unsigned treat_as_unsigned = 0;
unsigned treat_as_long = 0;
#if defined(PICOFORMAT_HANDLE_HEX) || defined(PICOFORMAT_HANDLE_FLOATS)
unsigned render_in_lowercase = 0;
#endif
#define FLAGS
#endif // struct packing platforms
for (; *pFormat && '\0' == format; pFormat++) {
switch (*pFormat) {
#ifdef PICOFORMAT_HANDLE_FORCEDSIGN
case '+':
FLAGS force_sign = 1;
break;
#endif // PICOFORMAT_HANDLE_FORCEDSIGN
#ifdef PICOFORMAT_HANDLE_FILL
case '-': // left-align flag
FLAGS left_align = 1;
break;
#endif // PICOFORMAT_HANDLE_FILL
case '0':
if (!FLAGS seen_numbers) {
FLAGS fill_zeros = 1;
break;
}
// fall through
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
*(FLAGS seen_period ? &decimal_chars :
&whole_chars) *= 10;
*(FLAGS seen_period ? &decimal_chars :
&whole_chars) +=
*pFormat - '0';
FLAGS seen_numbers = 1;
break;
case '.':
FLAGS seen_numbers = FLAGS seen_period =
1;
decimal_chars = 0;
break;
case '*': // dynamic width/precision: read value from arg list
*(FLAGS seen_period ? &decimal_chars :
&whole_chars) =
va_arg(vl, int);
FLAGS seen_numbers = 1;
break;
case 'l': // long modifier
FLAGS treat_as_long = 1;
break;
case 'u': // unsigned decimal integer
FLAGS treat_as_unsigned = 1;
format = 'd';
break;
#ifdef PICOFORMAT_HANDLE_BIN
case 'b':
bits_per_digit = 0;
FLAGS treat_as_unsigned = 1;
format = 'b';
break;
#endif // PICOFORMAT_HANDLE_BIN
#ifdef PICOFORMAT_HANDLE_OCT
case 'o': // octal integer
bits_per_digit = 3;
FLAGS treat_as_unsigned = 1;
format = 'b';
break;
#endif // PICOFORMAT_HANDLE_OCT
#ifdef PICOFORMAT_HANDLE_HEX
case 'x': // hexadecimal integer
case 'p': // pointer
bits_per_digit = 4;
FLAGS treat_as_unsigned = 1;
FLAGS render_in_lowercase = 1;
format = 'b';
break;
case 'X': // hexadecimal integer, uppercase
bits_per_digit = 4;
FLAGS treat_as_unsigned = 1;
format = 'b';
break;
#endif // PICOFORMAT_HANDLE_HEX
#ifdef PICOFORMAT_HANDLE_FLOATS
case 'a':
#ifdef PICOFORMAT_HANDLE_EXPONENTS
case 'e': // floating point, exponent format
#endif // PICOFORMAT_HANDLE_EXPONENTS
case 'f':
FLAGS render_in_lowercase = 1;
// fall through
case 'F':
#endif // PICOFORMAT_HANDLE_FLOATS
case 'c': // single char: always supported
case 'd': // integer: always supported
case 'i': // integer: always supported
case 's': // string of native `char`s: always supported
format = *pFormat;
break;
default:
FORMAT_ERROR_DELEGATE(
"detected unhandled format specifier: %c",
*pFormat);
break;
}
}
// format is parsed, now render the value
char* pParamStarts = pDest;
switch (format) {
case 'c': // single char
*pDest++ = va_arg(vl, int) & 0xff;
break;
case 's': { // null-terminated string
#ifdef PICOFORMAT_HANDLE_FILL
int len =
0; // effective length, bounded by precision if set
const char* pStr = va_arg(vl, const char*);
for (; pStr[len] &&
(decimal_chars < 0 || len < decimal_chars);
len++)
;
if (!FLAGS left_align) { // right-align: pad on the left
#ifdef PICOFORMAT_CLANG_QUIRK // clang's non-standard: '0' flag zero-pads strings
char chFill =
FLAGS fill_zeros ? '0' : ' ';
#else // PICOFORMAT_CLANG_QUIRK // standard C: '0' flag is undefined for %s, use spaces
char chFill = ' ';
#endif // PICOFORMAT_CLANG_QUIRK
for (; pDest < pEnd && whole_chars > len;
whole_chars--) {
*pDest++ = chFill;
}
}
for (int ii = 0; ii < len && pDest < pEnd; ii++) {
*pDest++ = pStr[ii];
}
if (FLAGS left_align) { // left-align: pad on the right (always spaces; '-' flag overrides '0')
for (; pDest < pEnd && whole_chars > len;
whole_chars--) {
*pDest++ = ' ';
}
}
#else // PICOFORMAT_HANDLE_FILL
for (const char* pStr = va_arg(vl, const char*);
*pStr && pDest < pEnd;) {
*pDest++ = *pStr++;
}
#endif // PICOFORMAT_HANDLE_FILL
} break;
#if defined(PICOFORMAT_HANDLE_HEX) || defined(PICOFORMAT_HANDLE_OCT) || \
defined(PICOFORMAT_HANDLE_BIN)
case 'b': { // binary, oct, or hex integer, always unsigned
long long int val = 0;
if (FLAGS treat_as_long) {
val = va_arg(vl, unsigned long long int);
} else {
val = va_arg(vl, unsigned);
}
unsigned mask = bits_per_digit == 4 ? 0x0f :
bits_per_digit == 3 ? 0x07 :
0x01;
#if defined(PICOFORMAT_HANDLE_HEX)
const char* chars = FLAGS render_in_lowercase ?
pLowercaseNumberDigits :
pUppercaseNumberDigits;
#elif defined(PICOFORMAT_HANDLE_OCT)
const char* chars = pOctalDigits;
#elif defined(PICOFORMAT_HANDLE_BIN)
const char* chars = pBinaryDigits;
#endif // individual non-decimal formats
while (pDest < pEnd &&
(pDest == pParamStarts || val ||
pDest - pParamStarts < whole_chars)) {
char ch;
if (pDest == pParamStarts ||
0 != val) { // if first char or there's still meaningful digits
ch = chars[val & mask];
val >>= bits_per_digit;
} else {
ch = FLAGS fill_zeros ? '0' : ' ';
}
*pDest++ = ch;
}
flip(pParamStarts, pDest);
} break;
#endif // defined(PICOFORMAT_HANDLE_BIN) || defined(PICOFORMAT_HANDLE_OCT) || defined(PICOFORMAT_HANDLE_HEX)
case 'd': // decimal integer
case 'i': {
long long int val = 0;
if (FLAGS treat_as_long) {
if (FLAGS treat_as_unsigned) {
val = va_arg(
vl,
unsigned long long int);
} else {
val = va_arg(vl, long long int);
}
} else {
if (FLAGS treat_as_unsigned) {
val = va_arg(vl, unsigned);
} else {
val = va_arg(vl, int);
}
}
char chSign = '\0';
if ((FLAGS force_sign || val < 0) &&
pDest < pEnd) {
chSign = val < 0 ? '-' : '+';
val = llabs(val);
whole_chars--;
}
while (pDest < pEnd &&
(pDest == pParamStarts || val ||
pDest - pParamStarts < whole_chars)) {
char ch;
if (pDest == pParamStarts ||
0 != val) { // if first digit (i.e. zero) or there's still non-zero digits to write
ch = val % 10 + '0';
val /= 10;
} else {
if ('\0' != chSign &&
!FLAGS fill_zeros) { // write a sign if filling with spaces, not with zeros
*pDest++ = chSign;
whole_chars++;
chSign =
'\0'; // prevent the sign from being written twice
}
#ifdef PICOFORMAT_HANDLE_FILL
ch = FLAGS fill_zeros ? '0' : ' ';
#else // PICOFORMAT_HANDLE_FILL
break;
#endif // PICOFORMAT_HANDLE_FILL
}
*pDest++ = ch;
}
if ('\0' !=
chSign) { // write a sign if it wasn't written before
*pDest++ = chSign;
}
flip(pParamStarts, pDest);
} break;
#ifdef PICOFORMAT_HANDLE_FLOATS
case 'f':
case 'F': {
double val = va_arg(vl, double);
if (FLAGS force_sign || val < 0.f) {
*pDest++ = val < 0.f ? '-' : '+';
val = fabs(val);
pParamStarts = pDest;
}
if (val == INFINITY || val == -INFINITY ||
isnan(val)) {
for (const char* pszVal = isnan(val) ?
FLAGS render_in_lowercase ?
"nan" :
"NAN" :
FLAGS render_in_lowercase ?
"inf" :
"INF";
pDest < pEnd && *pszVal;
*pDest++ = *pszVal++)
;
} else {
if (decimal_chars == -1) {
if (format == 'g') {
decimal_chars = 0;
} else {
decimal_chars = 6;
}
}
whole_chars =
MAX(0,
whole_chars - decimal_chars);
whole_chars = MAX(
whole_chars,
pDest - pParamStarts +
1); // at least sign (if present) and first char
for (float digit = 1.f; pDest < pEnd &&
(val >= digit ||
pDest - pParamStarts < whole_chars);
digit *= 10.f) {
char ch =
(int) (val / digit) % 10 +
'0';
*pDest++ = ch;
}
flip(pParamStarts, pDest);
if (decimal_chars && pDest < pEnd) {
*pDest++ = '.';
val += .5f *
pow(10.f,
-decimal_chars); // compensating for rounding error
for (size_t digit = 0;
pDest < pEnd &&
digit < decimal_chars;
digit++) {
val = (val -
(int) (val)) *
10.f;
*pDest++ =
(int) (val) + '0';
}
}
}
} break;
#endif // PICOFORMAT_HANDLE_FLOATS
}
}
}
}
*pDest = '\0';
return pDest - pStart;
}
int pico_snprintf(char* pDest, size_t cbDest, const char* pFormat, ...)
{
va_list vl;
va_start(vl, pFormat);
int result = pico_vsnprintf(pDest, cbDest, pFormat, vl);
va_end(vl);
return result;
}

View file

@ -1,62 +0,0 @@
/*
* Copyright (c) 2023-2026 rom-p
*
* 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.
*/
#pragma once
#include <stddef.h> // size_t
#include <stdarg.h> // va_*
int pico_snprintf(char* pDest, size_t cbDest, const char* pFormat, ...);
int pico_vsnprintf(char* pDest, size_t cbDest, const char* pFormat, va_list vl);
// PLEASE use `pico_snprintf()` instead!!! This function is vulnerable to buffer overflows
inline int pico_sprintf(char* pDest, const char* pFormat, ...)
{
va_list vl;
va_start(vl, pFormat);
int result = pico_vsnprintf(pDest, -1, pFormat, vl);
va_end(vl);
return result;
}
//
// IMPORTANT!!!
// comment/uncomment the following lines corresponding to the features you need
//
#define PICOFORMAT_HANDLE_FILL // uncomment this line to handle "%6i" and "%04d" -- the fill with zeroes or spaces
// #define PICOFORMAT_HANDLE_FORCEDSIGN // uncomment this line to handle "%+d" -- the forced sign placement
// #define PICOFORMAT_HANDLE_BIN // uncomment this line to handle "%b" -- binary representation
// #define PICOFORMAT_HANDLE_OCT // uncomment this line to handle "%o"
#define PICOFORMAT_HANDLE_HEX // uncomment this line to handle "%x" and "%X"
// #define PICOFORMAT_HANDLE_FLOATS // uncomment this line to handle the "%f"
// #define PICOFORMAT_CLANG_QUIRK // uncomment this line to match clang's non-standard "%010s" behavior (zero-pad strings when both '0' flag and width are set)
// by default, the debug builds (determined by `#define _DEBUG`) will real-time print errors when a feature is used that is not enabled above
#ifndef FORMAT_ERROR_DELEGATE
#ifdef _DEBUG
#define FORMAT_ERROR_DELEGATE(__message, __arg) \
printf(__message, __arg); \
printf("\n");
#else // _DEBUG
#define FORMAT_ERROR_DELEGATE(__message, __arg)
#endif // _DEBUG
#endif // FORMAT_ERROR_DELEGATE

View file

@ -1,256 +0,0 @@
/*
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
* Copyright 2013 Benjamin Vernoux <titanmkd@gmail.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "pins.h"
#include <libopencm3/lpc43xx/scu.h>
#include "gpio.h"
#include "leds.h"
#include "platform_detect.h"
#include "platform_gpio.h"
#include "platform_scu.h"
#include "power.h"
#ifdef IS_PRALINE
#include "clock_io.h"
#endif
void pins_shutdown(void)
{
/* Configure all GPIO as Input (safe state) */
gpio_init();
/* Detect Platform */
const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
/* TDI and TMS pull-ups are required in all JTAG-compliant devices.
*
* The HackRF CPLD is always present, so let the CPLD pull up its TDI and TMS.
*
* The PortaPack may not be present, so pull up the PortaPack TMS pin from the
* microcontroller.
*
* TCK is recommended to be held low, so use microcontroller pull-down.
*
* TDO is undriven except when in Shift-IR or Shift-DR phases.
* Use the microcontroller to pull down to keep from floating.
*
* LPC43xx pull-up and pull-down resistors are approximately 53K.
*/
#ifdef IS_EXPANSION_COMPATIBLE
if (IS_EXPANSION_COMPATIBLE) {
scu_pinmux(scu->PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
}
#endif
scu_pinmux(scu->PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
scu_pinmux(scu->PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
}
#endif
/* Configure SCU Pin Mux as GPIO */
scu_pinmux(scu->PINMUX_LED1, SCU_GPIO_NOPULL);
scu_pinmux(scu->PINMUX_LED2, SCU_GPIO_NOPULL);
scu_pinmux(scu->PINMUX_LED3, SCU_GPIO_NOPULL);
#ifdef IS_RAD1O
if (IS_RAD1O) {
scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4);
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
}
#endif
/* Configure USB indicators */
#ifdef IS_JAWBREAKER
if (IS_JAWBREAKER) {
scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3);
scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3);
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
disable_1v2_power();
disable_3v3aux_power();
gpio_output(gpio->gpio_1v2_enable);
gpio_output(gpio->gpio_3v3aux_enable_n);
scu_pinmux(scu->PINMUX_EN1V2, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_EN3V3_AUX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
}
#endif
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
disable_1v8_power();
#ifdef IS_H1_R9
if (IS_H1_R9) {
gpio_output(gpio->h1r9_1v8_enable);
scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
gpio_output(gpio->gpio_1v8_enable);
scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
}
#endif
}
#endif
#ifdef IS_H1_OR_PRALINE
if (IS_H1_OR_PRALINE) {
/* Safe state: start with VAA turned off: */
disable_rf_power();
/* Configure RF power supply (VAA) switch control signal as output */
#ifdef IS_H1_R9
if (IS_H1_R9) {
gpio_output(gpio->h1r9_vaa_disable);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
gpio_output(gpio->vaa_disable);
}
#endif
}
#endif
#ifdef IS_RAD1O
if (IS_RAD1O) {
/* Safe state: start with VAA turned off: */
disable_rf_power();
/* Configure RF power supply (VAA) switch control signal as output */
gpio_output(gpio->vaa_enable);
/* Disable unused clock outputs. They generate noise. */
scu_pinmux(CLK0, SCU_CLK_IN | SCU_CONF_FUNCTION7);
scu_pinmux(CLK2, SCU_CLK_IN | SCU_CONF_FUNCTION7);
scu_pinmux(scu->PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
scu_pinmux(scu->P2_CTRL0, scu->P2_CTRL0_PINCFG);
scu_pinmux(scu->P2_CTRL1, scu->P2_CTRL1_PINCFG);
scu_pinmux(scu->P1_CTRL0, scu->P1_CTRL0_PINCFG);
scu_pinmux(scu->P1_CTRL1, scu->P1_CTRL1_PINCFG);
scu_pinmux(scu->P1_CTRL2, scu->P1_CTRL2_PINCFG);
scu_pinmux(scu->CLKIN_CTRL, scu->CLKIN_CTRL_PINCFG);
scu_pinmux(scu->AA_EN, scu->AA_EN_PINCFG);
scu_pinmux(scu->TRIGGER_IN, scu->TRIGGER_IN_PINCFG);
scu_pinmux(scu->TRIGGER_OUT, scu->TRIGGER_OUT_PINCFG);
scu_pinmux(scu->PPS_OUT, scu->PPS_OUT_PINCFG);
scu_pinmux(scu->SCT_CLK, scu->SCT_CLK_PINCFG);
scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->SSP1_CIPO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->SSP1_COPI, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->SSP1_SCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION2);
scu_pinmux(scu->XCVR_ENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_RXENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_RXHP, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->MIXER_SCLK, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->MIXER_SDATA, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_ENX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_RESETX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_ENBL, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->AD_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
p2_ctrl_set(P2_SIGNAL_CLK3);
p1_ctrl_set(P1_SIGNAL_CLKIN);
clkin_ctrl_set(CLKIN_SIGNAL_P1);
gpio_output(gpio->p2_ctrl0);
gpio_output(gpio->p2_ctrl1);
gpio_output(gpio->p1_ctrl0);
gpio_output(gpio->p1_ctrl1);
gpio_output(gpio->p1_ctrl2);
gpio_output(gpio->clkin_ctrl);
gpio_output(gpio->pps_out);
gpio_input(gpio->trigger_in);
gpio_input(gpio->trigger_out);
gpio_clear(gpio->fpga_cfg_spi_cs);
gpio_output(gpio->fpga_cfg_spi_cs);
gpio_clear(gpio->fpga_cfg_creset);
gpio_output(gpio->fpga_cfg_creset);
gpio_input(gpio->fpga_cfg_cdone);
gpio_input(gpio->max5864_select);
}
#endif
/* enable input on SCL and SDA pins */
SCU_SFSI2C0 = SCU_I2C0_NOMINAL;
}
/* Run after pins_shutdown() and prior to enabling power supplies. */
void pins_setup(void)
{
/* Detect Platform */
const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
/* Configure LEDs */
led_off(0);
led_off(1);
led_off(2);
#ifdef IS_FOUR_LEDS
if (IS_FOUR_LEDS) {
led_off(3);
}
#endif
gpio_output(gpio->led[0]);
gpio_output(gpio->led[1]);
gpio_output(gpio->led[2]);
#ifdef IS_FOUR_LEDS
if (IS_FOUR_LEDS) {
gpio_output(gpio->led[3]);
}
#endif
/* Configure external clock in */
scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1);
}

View file

@ -1,35 +0,0 @@
/*
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Benjamin Vernoux <titanmkd@gmail.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void pins_shutdown(void);
void pins_setup(void);
#ifdef __cplusplus
}
#endif

View file

@ -26,7 +26,7 @@
#include "firmware_info.h"
#include "gpio.h"
#include "gpio_lpc.h"
#include "leds.h"
#include "hackrf_core.h"
#include "platform_detect.h"
#include "platform_scu.h"
@ -194,11 +194,11 @@ void detect_hardware_platform(void)
/* activate internal pull-down */
scu_pinmux(P5_0, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(P6_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
delay_us(4);
delay_us_at_mhz(4, 96);
/* tri-state for a moment before testing input */
scu_pinmux(P5_0, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
scu_pinmux(P6_10, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
delay_us(4);
delay_us_at_mhz(4, 96);
/* if input rose quickly, there must be an external pull-up */
detected_resistors |= (gpio_read(&gpio2_9_on_P5_0)) ? P5_0_PUP : 0;
detected_resistors |= (gpio_read(&gpio3_6_on_P6_10)) ? P6_10_PUP : 0;
@ -207,12 +207,12 @@ void detect_hardware_platform(void)
scu_pinmux(P5_0, SCU_GPIO_PUP | SCU_CONF_FUNCTION0);
scu_pinmux(P6_10, SCU_GPIO_PUP | SCU_CONF_FUNCTION0);
scu_pinmux(P6_5, SCU_GPIO_PUP | SCU_CONF_FUNCTION0);
delay_us(4);
delay_us_at_mhz(4, 96);
/* tri-state for a moment before testing input */
scu_pinmux(P5_0, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
scu_pinmux(P6_10, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
scu_pinmux(P6_5, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
delay_us(4);
delay_us_at_mhz(4, 96);
/* if input fell quickly, there must be an external pull-down */
detected_resistors |= (gpio_read(&gpio2_9_on_P5_0)) ? 0 : P5_0_PDN;
detected_resistors |= (gpio_read(&gpio3_6_on_P6_10)) ? 0 : P6_10_PDN;
@ -221,37 +221,37 @@ void detect_hardware_platform(void)
switch (detected_resistors) {
case JAWBREAKER_RESISTORS:
if (!(supported_platform() & PLATFORM_JAWBREAKER)) {
halt_and_flash(500);
halt_and_flash(3000000);
}
platform = BOARD_ID_JAWBREAKER;
return;
case RAD1O_RESISTORS:
if (!(supported_platform() & PLATFORM_RAD1O)) {
halt_and_flash(500);
halt_and_flash(3000000);
}
platform = BOARD_ID_RAD1O;
return;
case HACKRF1_OG_RESISTORS:
if (!(supported_platform() & PLATFORM_HACKRF1_OG)) {
halt_and_flash(500);
halt_and_flash(3000000);
}
platform = BOARD_ID_HACKRF1_OG;
break;
case HACKRF1_R9_RESISTORS:
if (!(supported_platform() & PLATFORM_HACKRF1_R9)) {
halt_and_flash(500);
halt_and_flash(3000000);
}
platform = BOARD_ID_HACKRF1_R9;
break;
case PRALINE_RESISTORS:
if (!(supported_platform() & PLATFORM_PRALINE)) {
halt_and_flash(500);
halt_and_flash(3000000);
}
platform = BOARD_ID_PRALINE;
break;
default:
platform = BOARD_ID_UNRECOGNIZED;
halt_and_flash(150);
halt_and_flash(1000000);
}
uint32_t adc0_3 = check_pin_strap(3);

View file

@ -21,6 +21,7 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#define BOARD_REV_GSG (0x80)
@ -31,6 +32,67 @@
#define PLATFORM_HACKRF1_R9 (1 << 3)
#define PLATFORM_PRALINE (1 << 4)
/* clang-format off */
/* Helper macros for platform-specific code. */
#if defined(UNIVERSAL)
#define IS_PRALINE (detected_platform() == BOARD_ID_PRALINE)
#define IS_NOT_PRALINE (!IS_PRALINE)
#define IS_HACKRF_ONE ( \
detected_platform() == BOARD_ID_HACKRF1_OG || \
detected_platform() == BOARD_ID_HACKRF1_R9 \
)
#define IS_NOT_HACKRF_ONE (!IS_HACKRF_ONE)
#define IS_H1_R9 (detected_platform() == BOARD_ID_HACKRF1_R9)
#define IS_NOT_H1_R9 (!IS_H1_R9)
#define IS_NOT_RAD1O true
#define IS_NOT_JAWBREAKER true
#define IS_H1_OR_PRALINE true
#define IS_H1_OR_RAD1O IS_HACKRF_ONE
#define IS_H1_OR_JAWBREAKER IS_HACKRF_ONE
#define IS_FOUR_LEDS IS_PRALINE
#define IS_EXPANSION_COMPATIBLE true
#elif defined(HACKRF_ONE)
#define IS_NOT_PRALINE true
#define IS_HACKRF_ONE true
#define IS_H1_R9 (detected_platform() == BOARD_ID_HACKRF1_R9)
#define IS_NOT_H1_R9 (!IS_H1_R9)
#define IS_NOT_RAD1O true
#define IS_NOT_JAWBREAKER true
#define IS_H1_OR_PRALINE true
#define IS_H1_OR_RAD1O true
#define IS_H1_OR_JAWBREAKER true
#define IS_EXPANSION_COMPATIBLE true
#elif defined(PRALINE)
#define IS_PRALINE true
#define IS_NOT_HACKRF_ONE true
#define IS_NOT_H1_R9 true
#define IS_NOT_RAD1O true
#define IS_NOT_JAWBREAKER true
#define IS_H1_OR_PRALINE true
#define IS_FOUR_LEDS true
#define IS_EXPANSION_COMPATIBLE true
#elif defined(RAD1O)
#define IS_NOT_PRALINE true
#define IS_NOT_HACKRF_ONE true
#define IS_NOT_H1_R9 true
#define IS_RAD1O true
#define IS_NOT_JAWBREAKER true
#define IS_H1_OR_RAD1O true
#define IS_FOUR_LEDS true
#elif defined(JAWBREAKER)
#define IS_NOT_PRALINE true
#define IS_NOT_HACKRF_ONE true
#define IS_NOT_H1_R9 true
#define IS_NOT_RAD1O true
#define IS_JAWBREAKER true
#define IS_H1_OR_JAWBREAKER true
#else
#error "No recognised platform defined"
#endif
/* clang-format on */
typedef enum {
BOARD_ID_JELLYBEAN = 0,
BOARD_ID_JAWBREAKER = 1,

View file

@ -234,7 +234,6 @@ const platform_gpio_t* platform_gpio(void)
gpio.rffc5072_data = &GPIO4_14;
gpio.rffc5072_reset = &GPIO2_14;
gpio.rffc5072_ld = &GPIO6_25;
gpio.rffc5072_enbl = &GPIO4_12;
}
#endif

View file

@ -27,6 +27,7 @@ extern "C" {
#include "gpio.h"
#include "gpio_lpc.h"
#include "platform_detect.h"
typedef struct {
/* LEDs */
@ -136,7 +137,6 @@ typedef struct {
gpio_t rffc5072_data;
gpio_t rffc5072_reset;
gpio_t rffc5072_ld; // PRALINE
gpio_t rffc5072_enbl; // PRALINE
gpio_t vco_ce; // RAD1O
gpio_t vco_sclk; // RAD1O
gpio_t vco_sdata; // RAD1O

View file

@ -258,12 +258,10 @@ const platform_scu_t* platform_scu(void)
scu.MIXER_SDATA = (P9_2); /* GPIO4[14] on P9_2 */
scu.MIXER_RESETX = (P5_5); /* GPIO2[14] on P5_5 */
scu.MIXER_LD = (PD_11); /* GPIO6[25] on PD_11 */
scu.MIXER_ENBL = (P9_0); /* GPIO4[12] on P9_0 */
scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
scu.MIXER_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
scu.MIXER_ENBL_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
}
#endif
#ifdef IS_RAD1O
@ -367,7 +365,6 @@ const platform_scu_t* platform_scu(void)
scu.TRIGGER_IN = (PD_12); /* GPIO6[26] on PD_12 */
scu.TRIGGER_OUT = (P2_6); /* GPIO5[6] on P2_6 */
scu.PPS_OUT = (P2_5); /* GPIO5[5] on P2_5 */
scu.SCT_CLK = (P6_4); /* CTIN_6 on P6_4 */
scu.P2_CTRL0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
scu.P2_CTRL1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
@ -379,7 +376,6 @@ const platform_scu_t* platform_scu(void)
scu.TRIGGER_IN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
scu.TRIGGER_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
scu.PPS_OUT_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
scu.SCT_CLK_PINCFG = (SCU_CLK_IN | SCU_CONF_FUNCTION1);
}
#endif
@ -442,14 +438,6 @@ const platform_scu_t* platform_scu(void)
}
#endif
/* SCT */
scu.CTOUT_8 = (P7_7);
scu.CTOUT_11 = (P7_6);
scu.CTOUT_12 = (P7_5);
scu.CTOUT_13 = (P7_4);
scu.CTOUT_14 = (P7_0);
scu.CTOUT_PINCFG = (SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
scu.PINMUX_ISP = (P2_7); /* GPIO0[7] */
scu.PINMUX_GP_CLKIN = (P4_7);

View file

@ -29,6 +29,8 @@ extern "C" {
#include <libopencm3/lpc43xx/scu.h>
#include "platform_detect.h"
/*
* SCU PinMux
*/
@ -155,9 +157,7 @@ typedef struct {
#endif
#ifdef IS_PRALINE
scu_grp_pin_t MIXER_LD;
scu_grp_pin_t MIXER_ENBL;
uint32_t MIXER_LD_PINCFG;
uint32_t MIXER_ENBL_PINCFG;
#endif
#ifdef IS_RAD1O
scu_grp_pin_t VCO_CE;
@ -240,7 +240,6 @@ typedef struct {
scu_grp_pin_t TRIGGER_IN;
scu_grp_pin_t TRIGGER_OUT;
scu_grp_pin_t PPS_OUT;
scu_grp_pin_t SCT_CLK;
scu_grp_pin_t P2_CTRL0_PINCFG;
scu_grp_pin_t P2_CTRL1_PINCFG;
@ -252,7 +251,6 @@ typedef struct {
scu_grp_pin_t TRIGGER_IN_PINCFG;
scu_grp_pin_t TRIGGER_OUT_PINCFG;
scu_grp_pin_t PPS_OUT_PINCFG;
scu_grp_pin_t SCT_CLK_PINCFG;
#endif
/* HackRF One r9 */
@ -302,14 +300,6 @@ typedef struct {
scu_grp_pin_t PINMUX_U0_TXD;
scu_grp_pin_t PINMUX_U0_RXD;
/* SCT */
scu_grp_pin_t CTOUT_8;
scu_grp_pin_t CTOUT_11;
scu_grp_pin_t CTOUT_12;
scu_grp_pin_t CTOUT_13;
scu_grp_pin_t CTOUT_14;
scu_grp_pin_t CTOUT_PINCFG;
scu_grp_pin_t PINMUX_ISP;
scu_grp_pin_t PINMUX_GP_CLKIN;

View file

@ -28,9 +28,16 @@
#include "delay.h"
#include "gpio.h"
#include "gpio_lpc.h"
#include "hackrf_core.h"
#include "platform_gpio.h"
#include "platform_scu.h"
static void portapack_sleep_milliseconds(const uint32_t milliseconds)
{
/* NOTE: Naively assumes 204 MHz instruction cycle clock and five instructions per count */
delay(milliseconds * 40800);
}
typedef struct {
gpio_t gpio_dir;
gpio_t gpio_lcd_rdx;
@ -238,7 +245,7 @@ static void portapack_lcd_sleep_out(void)
// "It will be necessary to wait 120msec after sending Sleep Out
// command (when in Sleep In Mode) before Sleep In command can be
// sent."
delay_ms(120);
portapack_sleep_milliseconds(120);
}
static void portapack_lcd_display_on(void)
@ -304,11 +311,11 @@ static void portapack_lcd_wake(void)
static void portapack_lcd_reset(void)
{
portapack_lcd_reset_state(false);
delay_ms(1);
portapack_sleep_milliseconds(1);
portapack_lcd_reset_state(true);
delay_ms(10);
portapack_sleep_milliseconds(10);
portapack_lcd_reset_state(false);
delay_ms(120);
portapack_sleep_milliseconds(120);
}
static void portapack_lcd_init(void)
@ -610,23 +617,23 @@ static bool portapack_detect(void)
return idcode == 0x020A50DD || idcode == 0x00025610;
}
static bool portapack_detected = false;
static const portapack_t portapack_instance = {};
bool portapack_present(void)
static const portapack_t* portapack_pointer = NULL;
const portapack_t* portapack(void)
{
return portapack_detected;
return portapack_pointer;
}
bool portapack_init(void)
void portapack_init(void)
{
if (portapack_detect()) {
portapack_if_init();
portapack_lcd_reset();
portapack_lcd_init();
portapack_detected = true;
portapack_pointer = &portapack_instance;
} else {
portapack_detected = false;
portapack_pointer = NULL;
}
return portapack_detected;
}

View file

@ -63,13 +63,15 @@ typedef struct {
typedef struct {
} portapack_t;
bool portapack_init(void);
void portapack_init(void);
bool portapack_present(void);
/* If the "portapack" symbol is defined, PortaPack support is compiled in */
/* If the portapack() call returns non-NULL, a PortaPack was detected and is initialized. */
const portapack_t* portapack(void) __attribute__((weak));
void portapack_backlight(const bool on);
void portapack_reference_oscillator(const bool on);
void portapack_reference_oscillator(const bool on) __attribute__((weak));
void portapack_fill_rectangle(const ui_rect_t rect, const ui_color_t color);

View file

@ -1,195 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "power.h"
#if defined(IS_HACKRF_ONE)
#include <stdint.h>
#endif
#include "gpio.h"
#include "platform_detect.h"
#include "platform_gpio.h"
#if defined(IS_PRALINE) || defined(IS_RAD1O)
#include "delay.h"
#endif
#ifdef IS_PRALINE
void enable_1v2_power(void)
{
if (IS_PRALINE) {
gpio_set(platform_gpio()->gpio_1v2_enable);
}
}
void disable_1v2_power(void)
{
if (IS_PRALINE) {
gpio_clear(platform_gpio()->gpio_1v2_enable);
}
}
void enable_3v3aux_power(void)
{
if (IS_PRALINE) {
gpio_clear(platform_gpio()->gpio_3v3aux_enable_n);
}
}
void disable_3v3aux_power(void)
{
if (IS_PRALINE) {
gpio_set(platform_gpio()->gpio_3v3aux_enable_n);
}
}
#endif
void enable_1v8_power(void)
{
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
#ifdef IS_H1_R9
if (IS_H1_R9) {
gpio_set(platform_gpio()->h1r9_1v8_enable);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
gpio_set(platform_gpio()->gpio_1v8_enable);
}
#endif
}
#endif
}
void disable_1v8_power(void)
{
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
#ifdef IS_H1_R9
if (IS_H1_R9) {
gpio_clear(platform_gpio()->h1r9_1v8_enable);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
gpio_clear(platform_gpio()->gpio_1v8_enable);
}
#endif
}
#endif
}
#ifdef IS_HACKRF_ONE
static inline void enable_rf_power_hackrf_one(void)
{
const platform_gpio_t* gpio = platform_gpio();
uint32_t i;
/* many short pulses to avoid one big voltage glitch */
for (i = 0; i < 1000; i++) {
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
gpio_set(gpio->h1r9_vaa_disable);
gpio_clear(gpio->h1r9_vaa_disable);
} else {
gpio_set(gpio->vaa_disable);
gpio_clear(gpio->vaa_disable);
}
}
}
static inline void disable_rf_power_hackrf_one(void)
{
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
gpio_set(platform_gpio()->h1r9_vaa_disable);
} else {
gpio_set(platform_gpio()->vaa_disable);
}
}
#endif
#ifdef IS_PRALINE
static inline void enable_rf_power_praline(void)
{
gpio_clear(platform_gpio()->vaa_disable);
/* Let the voltage stabilize */
delay_ms(35);
}
static inline void disable_rf_power_praline(void)
{
gpio_set(platform_gpio()->vaa_disable);
}
#endif
#ifdef IS_RAD1O
static inline void enable_rf_power_rad1o(void)
{
gpio_set(platform_gpio()->vaa_enable);
/* Let the voltage stabilize */
delay_ms(35);
}
static inline void disable_rf_power_rad1o(void)
{
gpio_clear(platform_gpio()->vaa_enable);
}
#endif
void enable_rf_power(void)
{
#ifdef IS_HACKRF_ONE
if (IS_HACKRF_ONE) {
enable_rf_power_hackrf_one();
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
enable_rf_power_praline();
}
#endif
#ifdef IS_RAD1O
if (IS_RAD1O) {
enable_rf_power_rad1o();
}
#endif
}
void disable_rf_power(void)
{
#ifdef IS_HACKRF_ONE
if (IS_HACKRF_ONE) {
disable_rf_power_hackrf_one();
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
disable_rf_power_praline();
}
#endif
#ifdef IS_RAD1O
if (IS_RAD1O) {
disable_rf_power_rad1o();
}
#endif
}

View file

@ -1,44 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifdef IS_PRALINE
void enable_1v2_power(void);
void disable_1v2_power(void);
void enable_3v3aux_power(void);
void disable_3v3aux_power(void);
#endif
void enable_1v8_power(void);
void disable_1v8_power(void);
#if defined(IS_RAD1O) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
void enable_rf_power(void);
void disable_rf_power(void);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -10,6 +10,12 @@
#include "gpio_lpc.h"
#include "delay.h"
static void delayms(const uint32_t milliseconds)
{
/* NOTE: Naively assumes 204 MHz instruction cycle clock and five instructions per count */
delay(milliseconds * 40800);
}
static struct gpio gpio_lcd_cs = GPIO(4, 12); /* P9_0 */
static struct gpio gpio_lcd_bl_en = GPIO(0, 8); /* P1_1 */
static struct gpio gpio_lcd_reset = GPIO(5, 17); /* P9_4 */
@ -79,9 +85,9 @@ void rad1o_lcdInit(void)
// Reset the display
gpio_clear(&gpio_lcd_reset);
delay_ms(100);
delayms(100);
gpio_set(&gpio_lcd_reset);
delay_ms(100);
delayms(100);
select();
@ -109,7 +115,7 @@ void rad1o_lcdInit(void)
(1 << 12) | (0 << 13) | (0 << 14) | 0);
write(0, 0x01); /* most color displays need the pause */
delay_ms(10);
delayms(10);
size_t i = 0;
while (i < sizeof(initseq_d)) {

View file

@ -1,11 +1,5 @@
#include "print.h"
#include <alloca.h>
#include <stdarg.h>
#include <stddef.h>
#include <picoprintf.h>
#include "display.h"
#include "render.h"
#include "smallfonts.h"
@ -13,17 +7,9 @@
static int32_t x = 0;
static int32_t y = 0;
void rad1o_lcdPrint(const char* fmt, ...)
void rad1o_lcdPrint(const char* string)
{
va_list args1, args2;
va_start(args1, fmt);
va_copy(args2, args1);
int buflen = pico_vsnprintf(NULL, 0, fmt, args1) + 1;
va_end(args1);
char* buf = alloca(buflen);
pico_vsnprintf(buf, buflen, fmt, args2);
va_end(args2);
x = rad1o_DoString(x, y, buf);
x = rad1o_DoString(x, y, string);
}
void rad1o_lcdNl(void)

View file

@ -2,7 +2,7 @@
#include <stdint.h>
void rad1o_lcdPrint(const char* string, ...);
void rad1o_lcdPrint(const char* string);
void rad1o_lcdNl(void);
void rad1o_lcdClear(void);
void rad1o_lcdMoveCrsr(int32_t dx, int32_t dy);

View file

@ -25,27 +25,27 @@
#include <libopencm3/cm3/nvic.h>
#include "clock_gen.h"
#include "clock_io.h"
#include "fixed_point.h"
#include "max283x.h"
#include "hackrf_core.h"
#include "hackrf_ui.h"
#include "mixer.h"
#include "max283x.h"
#include "platform_detect.h"
#include "rf_path.h"
#include "sgpio.h"
#include "transceiver_mode.h"
#include "tuning.h"
#include "u128.h"
#ifdef IS_PRALINE
#include "fpga.h"
#include "tune_config.h"
#endif
#ifdef IS_EXPANSION_COMPATIBLE
#include "operacake.h"
#endif
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
/* Driver instance. */
radio_t radio;
void radio_init(radio_t* const radio)
{
for (uint8_t bank = 0; bank < RADIO_NUM_BANKS; bank++) {
@ -54,11 +54,10 @@ void radio_init(radio_t* const radio)
}
}
radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
radio->config[RADIO_BANK_REQUESTED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
radio->config[RADIO_BANK_IDLE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
radio->config[RADIO_BANK_RX][RADIO_OPMODE] = TRANSCEIVER_MODE_RX;
radio->config[RADIO_BANK_TX][RADIO_OPMODE] = TRANSCEIVER_MODE_TX;
radio->config[RADIO_BANK_IDLE][RADIO_BIAS_TEE] = false;
radio->regs_dirty = 0;
}
@ -78,7 +77,7 @@ radio_error_t radio_reg_write(
}
switch (bank) {
case RADIO_BANK_REQUESTED:
case RADIO_BANK_ACTIVE:
mark_dirty(radio, reg);
/* fall through */
case RADIO_BANK_IDLE:
@ -107,12 +106,12 @@ uint64_t radio_reg_read(
return radio->config[bank][reg];
}
static uint32_t radio_update_direction(radio_t* const radio, uint64_t* bank)
static bool radio_update_direction(radio_t* const radio, uint64_t* bank)
{
const uint64_t requested = bank[RADIO_OPMODE];
if (requested == RADIO_UNSET) {
return 0;
return false;
}
rf_path_direction_t direction;
@ -130,111 +129,29 @@ static uint32_t radio_update_direction(radio_t* const radio, uint64_t* bank)
}
radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE] = requested;
rf_path_set_direction(&rf_path, direction);
return (1 << RADIO_OPMODE);
return true;
}
/* Apply correction to a true frequency to get an adjusted frequency. */
static uint64_t adjust(uint64_t freq, uint64_t correction)
{
/* skip if we can */
if (correction == FRAC_ONE) {
return freq;
}
u128 product = u128_multiply(freq, correction);
/* product >> 63 */
return (product.hi << 1) | (product.lo >> 63);
}
/* Given an adjusted frequency and correction, return the true frequency. */
static uint64_t unadjust(uint64_t freq, uint64_t correction)
{
/* skip if we can */
if (correction == FRAC_ONE) {
return freq;
}
/* (1 << (63 + 63)) */
u128 numerator = (u128){
.hi = 1ULL << 62,
.lo = 0,
};
u128 denominator = (u128){
.hi = 0,
.lo = correction,
};
u128 reciprocal = u128_divide(numerator, denominator);
return adjust(freq, reciprocal.lo);
}
static uint64_t restrict_correction(uint64_t requested_correction)
{
/* Correction should be in the range 0.99 to 1.01 */
const uint64_t min_correction = FRAC_ONE - FRAC_ONE_PERCENT;
const uint64_t max_correction = FRAC_ONE + FRAC_ONE_PERCENT;
if (requested_correction >= min_correction &&
requested_correction <= max_correction) {
return requested_correction;
} else {
return FRAC_ONE;
}
}
static uint64_t set_lo(uint64_t freq_lo, uint64_t correction, bool apply)
{
fp_40_24_t corrected_lo = adjust(freq_lo, correction);
fp_40_24_t achieved_lo = mixer_set_frequency(&mixer, corrected_lo, apply);
return unadjust(achieved_lo, correction);
}
static uint64_t set_if(uint64_t freq_if, uint64_t correction, bool apply)
{
fp_40_24_t corrected_if = adjust(freq_if, correction);
fp_40_24_t achieved_if = max283x_set_frequency(&max283x, corrected_if, apply);
return unadjust(achieved_if, correction);
}
static uint64_t set_afe_rate(uint64_t afe_rate, uint64_t correction, bool apply)
{
fp_28_36_t corrected_rate = adjust(afe_rate, correction);
fp_28_36_t achieved_rate = sample_rate_set(corrected_rate, apply);
return unadjust(achieved_rate, correction);
}
#define ABSOLUTE_MIN_AFE_RATE SR_FP_KHZ(200)
#define ABSOLUTE_MAX_AFE_RATE SR_FP_KHZ(43600)
#define MAX_SUPPORTED_AFE_RATE SR_FP_KHZ(40000)
#define MAX_AFE_RATE_HZ (40000000UL)
static inline uint8_t compute_resample_log(
const fp_28_36_t sample_rate,
const uint32_t sample_rate_hz,
const uint64_t requested_n)
{
if (detected_platform() != BOARD_ID_PRALINE) {
return 0;
}
uint8_t n = 0; // resampling ratio is 2**n
const uint8_t max_n = 5;
fp_28_36_t afe_rate = 0;
const uint8_t min_n = 1;
uint8_t max_n = 5;
if (requested_n == RADIO_UNSET) {
/* Find highest supported resampling ratio of at least 2 (n=1). */
while (((afe_rate * 2) <= MAX_SUPPORTED_AFE_RATE) && (n < max_n)) {
n++;
afe_rate = sample_rate << n;
}
} else {
/* Restrict requested resampling ratio within allowable limits. */
n = MIN(max_n, requested_n);
afe_rate = sample_rate << n;
while ((afe_rate < ABSOLUTE_MIN_AFE_RATE) && (n < max_n)) {
n++;
afe_rate <<= 1;
}
while ((afe_rate > ABSOLUTE_MAX_AFE_RATE) && (n > 0)) {
n--;
afe_rate >>= 1;
}
if (requested_n != RADIO_UNSET) {
max_n = MIN(max_n, requested_n);
}
uint8_t n = min_n; // resampling ratio is 2**n
uint32_t afe_rate_x2 = 4 * sample_rate_hz;
while ((afe_rate_x2 <= MAX_AFE_RATE_HZ) && (n < max_n)) {
afe_rate_x2 <<= 1;
n++;
}
return n;
}
@ -243,19 +160,17 @@ static inline uint8_t compute_resample_log(
#define MAX_MCU_RATE SR_FP_KHZ(21800)
#define DEFAULT_MCU_RATE SR_FP_KHZ(10000)
static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
{
fp_28_36_t rate, afe_rate, previous_rate, previous_afe_rate;
uint64_t previous_n, requested_n;
uint64_t previous_n;
uint8_t n = 0;
bool new_afe_rate = false;
bool new_rate = false;
bool new_n = false;
const uint64_t requested_rate = bank[RADIO_SAMPLE_RATE];
const uint64_t requested_correction = bank[RADIO_CLOCK_CORRECTION];
uint64_t correction = restrict_correction(requested_correction);
const uint64_t requested_n = bank[RADIO_RESAMPLE_RX];
if (requested_rate != RADIO_UNSET) {
rate = MIN(requested_rate, MAX_MCU_RATE);
@ -283,8 +198,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
switch (opmode) {
case TRANSCEIVER_MODE_TX:
case TRANSCEIVER_MODE_SS:
requested_n = bank[RADIO_RESAMPLE_TX];
n = compute_resample_log(rate, requested_n);
n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n);
if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) {
#ifdef IS_PRALINE
if (IS_PRALINE) {
@ -295,8 +209,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
}
break;
default:
requested_n = bank[RADIO_RESAMPLE_RX];
n = compute_resample_log(rate, requested_n);
n = compute_resample_log(rate / SR_FP_ONE_HZ, requested_n);
if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) {
#ifdef IS_PRALINE
if (IS_PRALINE) {
@ -308,26 +221,35 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
}
new_n = (n != previous_n);
afe_rate = rate << n;
afe_rate = set_afe_rate(afe_rate, correction, false);
previous_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE];
if ((previous_n == RADIO_UNSET) || previous_rate == RADIO_UNSET) {
previous_afe_rate = RADIO_UNSET;
if (radio->sample_rate_cb) {
afe_rate = rate << n;
afe_rate = radio->sample_rate_cb(afe_rate, false);
previous_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE];
if ((previous_n == RADIO_UNSET) || previous_rate == RADIO_UNSET) {
previous_afe_rate = RADIO_UNSET;
} else {
previous_afe_rate = previous_rate << previous_n;
}
new_afe_rate = (afe_rate != previous_afe_rate);
if (new_afe_rate) {
afe_rate = radio->sample_rate_cb(afe_rate, true);
}
} else {
previous_afe_rate = previous_rate << previous_n;
return false;
}
new_afe_rate = (afe_rate != previous_afe_rate);
if (new_afe_rate) {
afe_rate = set_afe_rate(afe_rate, correction, true);
}
rate = afe_rate >> n;
new_rate = (rate != previous_rate);
if (new_rate) {
radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] = rate;
if (rate != RADIO_UNSET) {
/* Round to the nearest Hz for display. */
const uint32_t rate_hz =
(rate + (SR_FP_ONE_HZ >> 1)) / SR_FP_ONE_HZ;
hackrf_ui()->set_sample_rate(rate_hz);
}
}
return ((new_afe_rate || new_rate || new_n) << RADIO_SAMPLE_RATE);
return (new_afe_rate || new_rate || new_n);
}
/*
@ -434,10 +356,11 @@ static const tune_config_t* select_tune_config(uint64_t opmode, fp_40_24_t freq_
}
#endif
static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
static bool radio_update_frequency(radio_t* const radio, uint64_t* bank)
{
uint32_t changed = 0;
bool new_freq = false;
bool high_lo = false;
bool invert_spectrum = false;
const uint64_t requested_rf = bank[RADIO_FREQUENCY_RF];
const uint64_t requested_if = bank[RADIO_FREQUENCY_IF];
@ -446,7 +369,6 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
#ifdef IS_PRALINE
const uint64_t requested_rotation = bank[RADIO_ROTATION];
#endif
const uint64_t requested_correction = bank[RADIO_CLOCK_CORRECTION];
const uint64_t applied_rf = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF];
const uint64_t applied_if = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF];
@ -455,8 +377,6 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT];
const uint64_t applied_rotation =
radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION];
const uint64_t applied_correction =
radio->config[RADIO_BANK_APPLIED][RADIO_CLOCK_CORRECTION];
uint64_t freq_rf = applied_rf;
uint64_t analog_rf = applied_rf;
@ -464,7 +384,6 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
uint64_t freq_lo = applied_lo;
uint64_t img_reject = applied_img_reject;
uint64_t rotation = applied_rotation;
uint64_t correction = applied_correction;
uint64_t opmode = bank[RADIO_OPMODE];
if (opmode == RADIO_UNSET) {
@ -482,12 +401,11 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
img_reject = RF_PATH_FILTER_BYPASS;
}
}
correction = restrict_correction(requested_correction);
if (requested_if != RADIO_UNSET) {
freq_if = set_if(requested_if, correction, false);
freq_if = max283x_set_frequency(&max283x, requested_if, false);
}
if (requested_lo != RADIO_UNSET) {
freq_lo = set_lo(requested_lo, correction, false);
freq_lo = mixer_set_frequency(&mixer, freq_lo, false);
}
#ifdef IS_PRALINE
if (IS_PRALINE) {
@ -569,7 +487,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
freq_lo = RADIO_UNSET;
}
if (freq_lo != RADIO_UNSET) {
freq_lo = set_lo(freq_lo, correction, false);
freq_lo = mixer_set_frequency(&mixer, freq_lo, false);
}
}
@ -591,19 +509,19 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
/* Apply settings. */
if ((freq_if != applied_if) && (freq_if != RADIO_UNSET)) {
freq_if = set_if(freq_if, correction, true);
freq_if = max283x_set_frequency(&max283x, freq_if, true);
radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = freq_if;
changed |= (1 << RADIO_FREQUENCY_IF);
new_freq = true;
}
if ((freq_lo != applied_lo) && (freq_lo != RADIO_UNSET)) {
freq_lo = set_lo(freq_lo, correction, true);
freq_lo = mixer_set_frequency(&mixer, freq_lo, true);
radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = freq_lo;
changed |= (1 << RADIO_FREQUENCY_LO);
new_freq = true;
}
if ((img_reject != applied_img_reject) && (img_reject != RADIO_UNSET)) {
rf_path_set_filter(&rf_path, img_reject);
rf_path_set_filter(&rf_path, img_reject, opmode);
radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT] = img_reject;
changed |= (1 << RADIO_IMAGE_REJECT);
new_freq = true;
}
if ((rotation != applied_rotation) && (rotation != RADIO_UNSET)) {
#ifdef IS_PRALINE
@ -612,7 +530,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
}
#endif
radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = rotation;
changed |= (1 << RADIO_ROTATION);
new_freq = true;
}
/* Compute precise RF. */
@ -623,6 +541,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
case RF_PATH_FILTER_LOW_PASS:
if (freq_lo > freq_if) {
analog_rf = freq_lo - freq_if;
invert_spectrum = true;
} else {
analog_rf = freq_if - freq_lo;
}
@ -637,14 +556,21 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
}
if ((freq_rf != applied_rf) && (freq_rf != RADIO_UNSET)) {
radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] = freq_rf;
changed |= (1 << RADIO_FREQUENCY_RF);
}
if ((correction != applied_correction) && (correction != RADIO_UNSET)) {
radio->config[RADIO_BANK_APPLIED][RADIO_CLOCK_CORRECTION] = correction;
changed |= (1 << RADIO_CLOCK_CORRECTION);
new_freq = true;
}
return changed;
if (new_freq) {
sgpio_cpld_set_mixer_invert(&sgpio_config, invert_spectrum);
if (opmode != TRANSCEIVER_MODE_RX_SWEEP) {
hackrf_ui()->set_frequency(freq_rf / FP_ONE_MHZ);
}
#ifdef IS_EXPANSION_COMPATIBLE
if (IS_EXPANSION_COMPATIBLE) {
operacake_set_range(freq_rf / FP_ONE_MHZ);
}
#endif
}
return new_freq;
}
static uint32_t auto_bandwidth(radio_t* const radio, uint64_t opmode)
@ -670,9 +596,9 @@ static uint32_t auto_bandwidth(radio_t* const radio, uint64_t opmode)
return lpf_bandwidth;
}
static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank)
static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank)
{
uint32_t changed = 0;
bool new_bw = false;
uint64_t opmode = bank[RADIO_OPMODE];
if (opmode == RADIO_UNSET) {
@ -693,7 +619,7 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank)
lpf_bandwidth);
radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] =
lpf_bandwidth;
changed |= (1 << RADIO_XCVR_TX_LPF);
new_bw = true;
}
if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] !=
lpf_bandwidth) {
@ -703,7 +629,7 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank)
lpf_bandwidth);
radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] =
lpf_bandwidth;
changed |= (1 << RADIO_XCVR_RX_LPF);
new_bw = true;
}
bool narrow_lpf_enable = false;
bool applied_narrow_lpf_enable =
@ -715,7 +641,7 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank)
narrowband_filter_set(narrow_lpf_enable);
radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF] =
narrow_lpf_enable;
changed |= (1 << RADIO_RX_NARROW_LPF);
new_bw = true;
}
/* Always set HPF bandwidth to 30 kHz for now. */
const max283x_rx_hpf_freq_t hpf_bandwidth = MAX283x_RX_HPF_30_KHZ;
@ -724,7 +650,7 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank)
max283x_set_rx_hpf_frequency(&max283x, hpf_bandwidth);
radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] =
hpf_bandwidth;
changed |= (1 << RADIO_XCVR_RX_HPF);
new_bw = true;
}
}
#endif
@ -763,24 +689,21 @@ static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank)
lpf_bandwidth;
radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] =
lpf_bandwidth;
changed |=
((1 << RADIO_BB_BANDWIDTH_RX) |
(1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_XCVR_TX_LPF) |
(1 << RADIO_XCVR_RX_LPF));
new_bw = true;
}
}
#endif
return changed;
return new_bw;
}
#define DEFAULT_GAIN_RF (0)
#define DEFAULT_GAIN_IF (16)
#define DEFAULT_GAIN_BB (12)
static uint32_t radio_update_gain(radio_t* const radio, uint64_t* bank)
static bool radio_update_gain(radio_t* const radio, uint64_t* bank)
{
uint32_t changed = 0;
uint64_t gain, tx_rf_gain, rx_rf_gain;
bool new_gain = false;
uint64_t gain;
uint64_t opmode = bank[RADIO_OPMODE];
if (opmode == RADIO_UNSET) {
opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE];
@ -794,34 +717,34 @@ static uint32_t radio_update_gain(radio_t* const radio, uint64_t* bank)
switch (opmode) {
case TRANSCEIVER_MODE_TX:
case TRANSCEIVER_MODE_SS:
tx_rf_gain = bank[RADIO_GAIN_TX_RF];
if (tx_rf_gain == RADIO_UNSET) {
tx_rf_gain = DEFAULT_GAIN_RF;
gain = bank[RADIO_GAIN_TX_RF];
if (gain == RADIO_UNSET) {
gain = DEFAULT_GAIN_RF;
}
rf_path_set_lna(&rf_path, tx_rf_gain);
rx_rf_gain = 0;
rf_path_set_lna(&rf_path, gain);
if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] != gain) {
new_gain = true;
}
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] = gain;
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] = 0;
break;
case TRANSCEIVER_MODE_RX:
case TRANSCEIVER_MODE_RX_SWEEP:
rx_rf_gain = bank[RADIO_GAIN_RX_RF];
if (rx_rf_gain == RADIO_UNSET) {
rx_rf_gain = DEFAULT_GAIN_RF;
gain = bank[RADIO_GAIN_RX_RF];
if (gain == RADIO_UNSET) {
gain = DEFAULT_GAIN_RF;
}
rf_path_set_lna(&rf_path, rx_rf_gain);
tx_rf_gain = 0;
rf_path_set_lna(&rf_path, gain);
if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] != gain) {
new_gain = true;
}
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] = gain;
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] = 0;
break;
default:
rf_path_set_lna(&rf_path, 0);
tx_rf_gain = 0;
rx_rf_gain = 0;
}
if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] != tx_rf_gain) {
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] = tx_rf_gain;
changed |= (1 << RADIO_GAIN_TX_RF);
}
if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] != rx_rf_gain) {
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] = rx_rf_gain;
changed |= (1 << RADIO_GAIN_RX_RF);
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] = 0;
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] = 0;
}
gain = bank[RADIO_GAIN_TX_IF];
@ -829,11 +752,11 @@ static uint32_t radio_update_gain(radio_t* const radio, uint64_t* bank)
(gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF])) {
max283x_set_txvga_gain(&max283x, gain);
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] = gain;
changed |= (1 << RADIO_GAIN_TX_IF);
new_gain = true;
} else if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] == RADIO_UNSET) {
max283x_set_txvga_gain(&max283x, DEFAULT_GAIN_IF);
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] = DEFAULT_GAIN_IF;
changed |= (1 << RADIO_GAIN_TX_IF);
new_gain = true;
}
gain = bank[RADIO_GAIN_RX_IF];
@ -841,11 +764,11 @@ static uint32_t radio_update_gain(radio_t* const radio, uint64_t* bank)
(gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF])) {
max283x_set_lna_gain(&max283x, gain);
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] = gain;
changed |= (1 << RADIO_GAIN_RX_IF);
new_gain = true;
} else if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] == RADIO_UNSET) {
max283x_set_lna_gain(&max283x, DEFAULT_GAIN_IF);
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] = DEFAULT_GAIN_IF;
changed |= (1 << RADIO_GAIN_RX_IF);
new_gain = true;
}
gain = bank[RADIO_GAIN_RX_BB];
@ -853,39 +776,39 @@ static uint32_t radio_update_gain(radio_t* const radio, uint64_t* bank)
(gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_BB])) {
max283x_set_vga_gain(&max283x, gain);
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_BB] = gain;
changed |= (1 << RADIO_GAIN_RX_BB);
new_gain = true;
} else if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_BB] == RADIO_UNSET) {
max283x_set_vga_gain(&max283x, DEFAULT_GAIN_BB);
radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_BB] = DEFAULT_GAIN_BB;
changed |= (1 << RADIO_GAIN_RX_BB);
new_gain = true;
}
return changed;
return new_gain;
}
static uint32_t radio_update_bias_tee(radio_t* const radio, uint64_t* bank)
static bool radio_update_bias_tee(radio_t* const radio, uint64_t* bank)
{
if (detected_platform() == BOARD_ID_JAWBREAKER) {
return 0;
return false;
}
const uint64_t requested = bank[RADIO_BIAS_TEE];
const bool enable = requested;
if (requested == RADIO_UNSET) {
return 0;
return false;
}
if (radio->config[RADIO_BANK_APPLIED][RADIO_BIAS_TEE] == (uint64_t) enable) {
return 0;
return false;
}
rf_path_set_antenna(&rf_path, enable);
radio->config[RADIO_BANK_APPLIED][RADIO_BIAS_TEE] = enable;
return (1 << RADIO_BIAS_TEE);
return true;
}
static uint32_t radio_update_trigger(radio_t* const radio, uint64_t* bank)
static bool radio_update_trigger(radio_t* const radio, uint64_t* bank)
{
const uint64_t requested = bank[RADIO_TRIGGER];
bool enable = requested;
@ -895,29 +818,33 @@ static uint32_t radio_update_trigger(radio_t* const radio, uint64_t* bank)
}
if (radio->config[RADIO_BANK_APPLIED][RADIO_TRIGGER] == (uint64_t) enable) {
return 0;
return false;
}
trigger_enable(enable);
radio->config[RADIO_BANK_APPLIED][RADIO_TRIGGER] = enable;
return (1 << RADIO_TRIGGER);
return true;
}
static uint32_t radio_update_dc_block(radio_t* const radio, uint64_t* bank)
static bool radio_update_dc_block(radio_t* const radio, uint64_t* bank)
{
#ifdef IS_PRALINE
if (IS_PRALINE) {
const uint64_t requested = bank[RADIO_DC_BLOCK];
bool enable = requested;
if (requested == RADIO_UNSET) {
enable = true;
}
if (radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] ==
(uint64_t) enable) {
return 0;
return false;
}
fpga_set_rx_dc_block_enable(&fpga, enable);
radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] = enable;
return (1 << RADIO_DC_BLOCK);
return true;
}
#endif
@ -931,50 +858,64 @@ bool radio_update(radio_t* const radio)
uint64_t tmp_bank[RADIO_NUM_REGS];
nvic_disable_irq(NVIC_USB0_IRQ);
uint32_t dirty = radio->regs_dirty;
uint32_t changed = 0;
if (dirty == 0) {
nvic_enable_irq(NVIC_USB0_IRQ);
return false;
}
radio->regs_dirty = 0;
memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_REQUESTED][0]), sizeof(tmp_bank));
memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_ACTIVE][0]), sizeof(tmp_bank));
nvic_enable_irq(NVIC_USB0_IRQ);
if ((dirty & RADIO_REG_GROUP_RATE) ||
bool dir = false;
bool rate = false;
bool freq = false;
bool bw = false;
bool gain = false;
bool bias = false;
bool trig = false;
bool dc = false;
if ((dirty &
((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) |
(1 << RADIO_RESAMPLE_RX))) ||
((detected_platform() == BOARD_ID_PRALINE) &&
(dirty & (1 << RADIO_OPMODE)))) {
changed |= radio_update_sample_rate(radio, &tmp_bank[0]);
rate = radio_update_sample_rate(radio, &tmp_bank[0]);
}
if ((dirty & RADIO_REG_GROUP_FREQ) ||
if ((dirty &
((1 << RADIO_FREQUENCY_RF) | (1 << RADIO_FREQUENCY_IF) |
(1 << RADIO_FREQUENCY_LO) | (1 << RADIO_IMAGE_REJECT) |
(1 << RADIO_ROTATION))) ||
((detected_platform() == BOARD_ID_PRALINE) &&
((changed & RADIO_REG_GROUP_RATE) || (dirty & (1 << RADIO_OPMODE))))) {
changed |= radio_update_frequency(radio, &tmp_bank[0]);
(rate || (dirty & (1 << RADIO_OPMODE))))) {
freq = radio_update_frequency(radio, &tmp_bank[0]);
}
if ((dirty & RADIO_REG_GROUP_BW) ||
((detected_platform() == BOARD_ID_PRALINE) &&
(changed & (RADIO_REG_GROUP_RATE | RADIO_REG_GROUP_FREQ)))) {
changed |= radio_update_bandwidth(radio, &tmp_bank[0]);
if ((dirty &
((1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_BB_BANDWIDTH_RX) |
(1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF) |
(1 << RADIO_XCVR_RX_HPF) | (1 << RADIO_RX_NARROW_LPF))) ||
((detected_platform() == BOARD_ID_PRALINE) && (rate || freq))) {
bw = radio_update_bandwidth(radio, &tmp_bank[0]);
}
if (dirty & (RADIO_REG_GROUP_GAIN | (1 << RADIO_OPMODE))) {
changed |= radio_update_gain(radio, &tmp_bank[0]);
if (dirty &
((1 << RADIO_GAIN_TX_RF) | (1 << RADIO_GAIN_TX_IF) | (1 << RADIO_GAIN_RX_RF) |
(1 << RADIO_GAIN_RX_IF) | (1 << RADIO_GAIN_RX_BB) | (1 << RADIO_OPMODE))) {
gain = radio_update_gain(radio, &tmp_bank[0]);
}
if (dirty & ((1 << RADIO_BIAS_TEE) | (1 << RADIO_OPMODE))) {
changed |= radio_update_bias_tee(radio, &tmp_bank[0]);
bias = radio_update_bias_tee(radio, &tmp_bank[0]);
}
if (dirty & (1 << RADIO_TRIGGER)) {
changed |= radio_update_trigger(radio, &tmp_bank[0]);
trig = radio_update_trigger(radio, &tmp_bank[0]);
}
if (dirty & (1 << RADIO_DC_BLOCK)) {
changed |= radio_update_dc_block(radio, &tmp_bank[0]);
dc = radio_update_dc_block(radio, &tmp_bank[0]);
}
if (dirty & (1 << RADIO_OPMODE)) {
changed |= radio_update_direction(radio, &tmp_bank[0]);
dir = radio_update_direction(radio, &tmp_bank[0]);
}
if (radio->update_cb) {
radio->update_cb(changed);
}
return (changed != 0);
return trig || dir || rate || freq || bw || gain || bias || dc;
}
void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode)
@ -998,14 +939,14 @@ void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode)
nvic_disable_irq(NVIC_USB0_IRQ);
for (uint8_t reg = 0; reg < RADIO_NUM_REGS; reg++) {
value = radio->config[source_bank][reg];
previous = radio->config[RADIO_BANK_REQUESTED][reg];
previous = radio->config[RADIO_BANK_ACTIVE][reg];
if ((value != RADIO_UNSET) && (value != previous)) {
radio->config[RADIO_BANK_REQUESTED][reg] = value;
radio->config[RADIO_BANK_ACTIVE][reg] = value;
mark_dirty(radio, reg);
}
}
radio->config[RADIO_BANK_REQUESTED][RADIO_OPMODE] = mode;
radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = mode;
mark_dirty(radio, RADIO_OPMODE);
nvic_enable_irq(NVIC_USB0_IRQ);
radio_update(radio);

View file

@ -54,6 +54,13 @@ typedef struct {
uint32_t hz;
} radio_sample_rate_t;
// legacy type, moved from hackrf_core
typedef enum {
CLOCK_SOURCE_HACKRF = 0,
CLOCK_SOURCE_EXTERNAL = 1,
CLOCK_SOURCE_PORTAPACK = 2,
} clock_source_t;
/**
* Configurable registers stored as uint64_t. Any register may be set to
* RADIO_UNSET. When not RADIO_UNSET, some registers are read as a specific type
@ -165,33 +172,13 @@ typedef enum {
* DC block enable of type bool.
*/
RADIO_DC_BLOCK = 22,
/**
* Correction factor for radio reference clock, of type fp_1_63_t.
*/
RADIO_CLOCK_CORRECTION = 23,
} radio_register_t;
#define RADIO_NUM_REGS (24)
#define RADIO_NUM_REGS (23)
#define RADIO_UNSET (0xffffffffffffffff)
/* register groups for bitfield convenience */
#define RADIO_REG_GROUP_RATE \
((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) | \
(1 << RADIO_RESAMPLE_RX) | (1 << RADIO_CLOCK_CORRECTION))
#define RADIO_REG_GROUP_FREQ \
((1 << RADIO_FREQUENCY_RF) | (1 << RADIO_FREQUENCY_IF) | \
(1 << RADIO_FREQUENCY_LO) | (1 << RADIO_IMAGE_REJECT) | (1 << RADIO_ROTATION) | \
(1 << RADIO_CLOCK_CORRECTION))
#define RADIO_REG_GROUP_BW \
((1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_BB_BANDWIDTH_RX) | \
(1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF) | \
(1 << RADIO_XCVR_RX_HPF) | (1 << RADIO_RX_NARROW_LPF))
#define RADIO_REG_GROUP_GAIN \
((1 << RADIO_GAIN_TX_RF) | (1 << RADIO_GAIN_TX_IF) | (1 << RADIO_GAIN_RX_RF) | \
(1 << RADIO_GAIN_RX_IF) | (1 << RADIO_GAIN_RX_BB))
/**
* Register bank RADIO_BANK_REQUESTED stores the active configuration. Active
* Register bank RADIO_BANK_ACTIVE stores the active configuration. Active
* register settings are copied to the applied register when applied.
*
* The other three banks store settings that will be applied when switching to
@ -202,7 +189,7 @@ typedef enum {
*/
typedef enum {
RADIO_BANK_APPLIED = 0,
RADIO_BANK_REQUESTED = 1,
RADIO_BANK_ACTIVE = 1,
RADIO_BANK_IDLE = 2,
RADIO_BANK_RX = 3,
RADIO_BANK_TX = 4,
@ -220,24 +207,17 @@ typedef enum {
*/
typedef fp_28_36_t (*sample_rate_fn)(const fp_28_36_t sample_rate, const bool program);
/**
* An optional callback may be provided that is called after each time the
* radio configuration has been updated. The single argument is a bitfield
* indicating registers that have been changed.
*/
typedef void (*update_fn)(const uint32_t changed_regs);
typedef struct {
radio_config_mode_t config_mode;
uint64_t config[RADIO_NUM_BANKS][RADIO_NUM_REGS];
volatile uint32_t regs_dirty;
update_fn update_cb;
sample_rate_fn sample_rate_cb;
} radio_t;
void radio_init(radio_t* const radio);
/**
* Write to one or more registers. Writes to RADIO_BANK_REQUESTED are applied at
* Write to one or more registers. Writes to RADIO_BANK_ACTIVE are applied at
* the next radio_update(). Writes to RADIO_BANK_APPLIED are not supported.
*/
radio_error_t radio_reg_write(
@ -255,7 +235,7 @@ uint64_t radio_reg_read(
const radio_register_t reg);
/**
* Apply changes requested in RADIO_BANK_REQUESTED.
* Apply changes requested in RADIO_BANK_ACTIVE.
* Return true if any changes were applied.
*/
bool radio_update(radio_t* const radio);
@ -265,8 +245,3 @@ bool radio_update(radio_t* const radio);
* the request bank for the new mode.
*/
void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode);
/**
* Driver instance.
*/
extern radio_t radio;

View file

@ -23,21 +23,21 @@
#include "rf_path.h"
#include "hackrf_core.h"
#include "hackrf_ui.h"
#include "max283x.h"
#include "max5864.h"
#include "mixer.h"
#include "sgpio.h"
#include "platform_detect.h"
#include "transceiver_mode.h"
#ifdef IS_NOT_JAWBREAKER
#include <libopencm3/lpc43xx/scu.h>
#include "gpio.h"
#include "platform_scu.h"
#include "platform_gpio.h"
#endif
/* RF Path instance. */
rf_path_t rf_path = {
.switchctrl = 0,
};
/*
* RF switches on Jawbreaker are controlled by General Purpose Outputs (GPO) on
* the RFFC5072.
@ -319,113 +319,14 @@ static void switchctrl_set(rf_path_t* const rf_path, const uint8_t gpo)
#endif
}
void rf_path_pin_shutdown(void)
{
#ifdef IS_PRALINE
if (IS_PRALINE) {
const platform_scu_t* scu = platform_scu();
/* Configure RF switch control signals */
scu_pinmux(scu->TX_EN, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
board_rev_t rev = detected_revision();
if ((rev == BOARD_REV_PRALINE_R1_0) ||
(rev == BOARD_REV_GSG_PRALINE_R1_0)) {
scu_pinmux(scu->MIX_EN_N_R1_0, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
} else {
scu_pinmux(scu->MIX_EN_N, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
}
scu_pinmux(scu->LPF_EN, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->RF_AMP_EN, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
/* Configure antenna port power control signal */
scu_pinmux(scu->ANT_BIAS_EN_N, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
/* Configure RF power supply (VAA) switch */
scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
/* Disable narrowband filter. */
narrowband_filter_set(0);
gpio_output(platform_gpio()->aa_en);
}
#endif
}
void rf_path_pin_setup(rf_path_t* const rf_path)
{
#ifdef IS_JAWBREAKER
#ifdef JAWBREAKER
(void) rf_path;
#else
const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
#endif
// initialize rf_path struct and assign gpio's
#ifdef IS_HACKRF_ONE
if (IS_HACKRF_ONE) {
*rf_path = (rf_path_t){
.switchctrl = 0,
.gpio_hp = gpio->hp,
.gpio_lp = gpio->lp,
.gpio_tx_mix_bp = gpio->tx_mix_bp,
.gpio_no_mix_bypass = gpio->no_mix_bypass,
.gpio_rx_mix_bp = gpio->rx_mix_bp,
.gpio_tx_amp = gpio->tx_amp,
.gpio_tx = gpio->tx,
.gpio_mix_bypass = gpio->mix_bypass,
.gpio_rx = gpio->rx,
.gpio_no_tx_amp_pwr = gpio->no_tx_amp_pwr,
.gpio_amp_bypass = gpio->amp_bypass,
.gpio_rx_amp = gpio->rx_amp,
.gpio_no_rx_amp_pwr = gpio->no_rx_amp_pwr,
};
#ifdef IS_H1_R9
if (IS_H1_R9) {
rf_path->gpio_rx = gpio->h1r9_rx;
rf_path->gpio_h1r9_no_ant_pwr = gpio->h1r9_no_ant_pwr;
}
#endif
}
#endif
#ifdef IS_RAD1O
if (IS_RAD1O) {
*rf_path = (rf_path_t){
.switchctrl = 0,
.gpio_tx_rx_n = gpio->tx_rx_n,
.gpio_tx_rx = gpio->tx_rx,
.gpio_by_mix = gpio->by_mix,
.gpio_by_mix_n = gpio->by_mix_n,
.gpio_by_amp = gpio->by_amp,
.gpio_by_amp_n = gpio->by_amp_n,
.gpio_mixer_en = gpio->mixer_en,
.gpio_low_high_filt = gpio->low_high_filt,
.gpio_low_high_filt_n = gpio->low_high_filt_n,
.gpio_tx_amp = gpio->tx_amp,
.gpio_rx_lna = gpio->rx_lna,
};
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
*rf_path = (rf_path_t){
.switchctrl = 0,
.gpio_tx_en = gpio->tx_en,
.gpio_mix_en_n = gpio->mix_en_n,
.gpio_lpf_en = gpio->lpf_en,
.gpio_rf_amp_en = gpio->rf_amp_en,
.gpio_ant_bias_en_n = gpio->ant_bias_en_n,
};
if ((detected_revision() == BOARD_REV_PRALINE_R1_0) ||
(detected_revision() == BOARD_REV_GSG_PRALINE_R1_0)) {
rf_path->gpio_mix_en_n = gpio->mix_en_n_r1_0;
}
scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PUP | SCU_CONF_FUNCTION4);
scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
}
#endif
#ifdef IS_HACKRF_ONE
if (IS_HACKRF_ONE) {
/* Configure RF switch control signals */
@ -564,9 +465,11 @@ void rf_path_pin_setup(rf_path_t* const rf_path)
void rf_path_init(rf_path_t* const rf_path)
{
ssp1_set_mode_max5864();
max5864_setup(&max5864);
max5864_shutdown(&max5864);
ssp1_set_mode_max283x();
max283x_setup(&max283x);
max283x_start(&max283x);
@ -604,8 +507,11 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
} else {
mixer_enable(&mixer);
}
ssp1_set_mode_max5864();
max5864_tx(&max5864);
ssp1_set_mode_max283x();
max283x_tx(&max283x);
sgpio_configure(&sgpio_config, SGPIO_DIRECTION_TX);
break;
case RF_PATH_DIRECTION_RX:
@ -619,8 +525,11 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
} else {
mixer_enable(&mixer);
}
ssp1_set_mode_max5864();
max5864_rx(&max5864);
ssp1_set_mode_max283x();
max283x_rx(&max283x);
sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX);
break;
#ifdef IS_PRALINE
@ -628,12 +537,15 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
case RF_PATH_DIRECTION_RX_CALIBRATION:
rf_path->switchctrl &= ~SWITCHCTRL_TX;
mixer_disable(&mixer);
ssp1_set_mode_max5864();
max5864_xcvr(&max5864);
ssp1_set_mode_max283x();
if (direction == RF_PATH_DIRECTION_TX_CALIBRATION) {
max283x_tx_calibration(&max283x);
} else {
max283x_rx_calibration(&max283x);
}
sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX);
break;
#endif
@ -643,15 +555,23 @@ void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t d
/* Set RF path to receive direction when "off" */
rf_path->switchctrl &= ~SWITCHCTRL_TX;
mixer_disable(&mixer);
ssp1_set_mode_max5864();
max5864_standby(&max5864);
ssp1_set_mode_max283x();
max283x_set_mode(&max283x, MAX283x_MODE_STANDBY);
sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX);
break;
}
switchctrl_set(rf_path, rf_path->switchctrl);
hackrf_ui()->set_direction(direction);
}
void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter)
void rf_path_set_filter(
rf_path_t* const rf_path,
const rf_path_filter_t filter,
const transceiver_mode_t opmode)
{
switch (filter) {
default:
@ -673,6 +593,10 @@ void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter)
}
switchctrl_set(rf_path, rf_path->switchctrl);
if (opmode != TRANSCEIVER_MODE_RX_SWEEP) {
hackrf_ui()->set_filter(filter);
}
}
void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable)
@ -696,6 +620,8 @@ void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable)
}
switchctrl_set(rf_path, rf_path->switchctrl);
hackrf_ui()->set_lna_power(enable);
}
/* antenna port power control */
@ -708,11 +634,6 @@ void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable)
}
switchctrl_set(rf_path, rf_path->switchctrl);
}
#ifdef IS_PRALINE
void narrowband_filter_set(const uint8_t value)
{
gpio_write(platform_gpio()->aa_en, value & 1);
hackrf_ui()->set_antenna_bias(enable);
}
#endif

View file

@ -25,7 +25,9 @@
#include <stdint.h>
#ifdef IS_NOT_JAWBREAKER
#include "platform_detect.h" // IWYU pragma: keep
#include "transceiver_mode.h"
#if defined(IS_HACKRF_ONE) || defined(IS_RAD1O) || defined(IS_PRALINE)
#include "gpio.h"
#endif
@ -91,20 +93,15 @@ typedef struct {
};
} rf_path_t;
void rf_path_pin_shutdown(void);
void rf_path_pin_setup(rf_path_t* const rf_path);
void rf_path_init(rf_path_t* const rf_path);
void rf_path_set_direction(rf_path_t* const rf_path, const rf_path_direction_t direction);
void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter);
void rf_path_set_filter(
rf_path_t* const rf_path,
const rf_path_filter_t filter,
const transceiver_mode_t opmode);
void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable);
void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable);
#ifdef IS_PRALINE
void narrowband_filter_set(const uint8_t value);
#endif
/* RF Path instance. */
extern rf_path_t rf_path;

View file

@ -163,13 +163,13 @@ void rffc5071_lock_test(rffc5071_driver_t* const drv)
rffc5071_enable(drv);
// Wait 1ms.
delay_ms(1);
delay_us_at_mhz(1000, 204);
// Check for lock.
lock = rffc5071_check_lock(drv);
rffc5071_disable(drv);
delay_us(100);
delay_us_at_mhz(100, 204);
selftest.mixer_locks[i] = lock;
}
@ -202,7 +202,7 @@ static uint16_t rffc5071_spi_read(rffc5071_driver_t* const drv, uint8_t r)
(void) drv;
uint16_t data[] = {0x80 | (r & 0x7f), 0xffff};
spi_bus_transfer(drv->bus, drv->bus->config, data, 2);
spi_bus_transfer(drv->bus, data, 2);
return data[1];
}
@ -211,7 +211,7 @@ static void rffc5071_spi_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t
(void) drv;
uint16_t data[] = {0x00 | (r & 0x7f), v};
spi_bus_transfer(drv->bus, drv->bus->config, data, 2);
spi_bus_transfer(drv->bus, data, 2);
}
uint16_t rffc5071_reg_read(rffc5071_driver_t* const drv, uint8_t r)

View file

@ -27,6 +27,7 @@
#include "fixed_point.h"
#include "gpio.h"
#include "platform_detect.h" // IWYU pragma: keep
#include "spi_bus.h"
/* 31 registers, each containing 16 bits of data. */

View file

@ -22,7 +22,9 @@
#include <stdint.h>
#include "hackrf_core.h"
#include "rom_iap.h"
#include "spi_bus.h"
#include "w25q80bv.h"
#define ROM_IAP_ADDR (0x10400100)
@ -76,6 +78,7 @@ isp_iap_ret_code_t iap_cmd_call(iap_cmd_res_t* iap_cmd_res)
Alternative way to retrieve Part Id on MCU with no IAP
Read Serial No => Read Unique ID in SPIFI (only compatible with W25Q80BV
*/
spi_bus_start(spi_flash.bus, &ssp_config_w25q80bv);
w25q80bv_setup(&spi_flash);
switch (iap_cmd_res->cmd_param.command_code) {

View file

@ -24,6 +24,8 @@
#include <stdbool.h>
#include <stdint.h>
#include "platform_detect.h" // IWYU pragma: keep
#define NUM_LOCK_ATTEMPTS 3
enum {

View file

@ -29,86 +29,18 @@
#include <libopencm3/lpc43xx/sgpio.h>
#include "platform_detect.h"
#include "platform_gpio.h"
#include "platform_scu.h"
#include "sgpio.h"
#ifdef IS_NOT_PRALINE
#include "clock_io.h"
#include "hackrf_core.h"
#endif
/* Driver configuration instance. */
sgpio_config_t sgpio_config = {
.slice_mode_multislice = true,
};
static void update_q_invert(sgpio_config_t* const config);
void sgpio_pin_shutdown(sgpio_config_t* const config)
{
const platform_scu_t* scu = platform_scu();
#ifdef IS_PRALINE
if (IS_PRALINE) {
scu_pinmux(scu->PINMUX_SGPIO0, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO1, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO2, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO3, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO4, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->PINMUX_SGPIO5, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO6, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->PINMUX_SGPIO7, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO8, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO9, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_SGPIO12, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
}
#endif
#ifdef IS_H1_R9
if (IS_H1_R9) {
scu_pinmux(
scu->H1R9_TRIGGER_EN,
SCU_GPIO_PDN | SCU_CONF_FUNCTION4); /* GPIO5[5] */
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
scu_pinmux(
scu->TRIGGER_EN,
SCU_GPIO_PDN | SCU_CONF_FUNCTION4); /* GPIO5[12] */
}
#endif
gpio_input(config->gpio_q_invert);
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
trigger_enable(false);
gpio_output(config->gpio_trigger_enable);
}
#endif
}
void sgpio_configure_pin_functions(sgpio_config_t* const config)
{
const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
config->gpio_q_invert = gpio->q_invert;
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
config->gpio_trigger_enable = gpio->trigger_enable;
}
#endif
#ifdef IS_H1_R9
if (IS_H1_R9) {
config->gpio_trigger_enable = gpio->h1r9_trigger_enable;
}
#endif
scu_pinmux(scu->PINMUX_SGPIO0, scu->PINMUX_SGPIO0_PINCFG);
scu_pinmux(scu->PINMUX_SGPIO1, scu->PINMUX_SGPIO1_PINCFG);
scu_pinmux(scu->PINMUX_SGPIO2, scu->PINMUX_SGPIO2_PINCFG);

View file

@ -26,6 +26,7 @@
#include <stdint.h>
#include "gpio.h"
#include "platform_detect.h" // IWYU pragma: keep
typedef enum {
SGPIO_DIRECTION_RX,
@ -40,7 +41,6 @@ typedef struct {
bool slice_mode_multislice;
} sgpio_config_t;
void sgpio_pin_shutdown(sgpio_config_t* const config);
void sgpio_configure_pin_functions(sgpio_config_t* const config);
void sgpio_test_interface(sgpio_config_t* const config);
void sgpio_set_slice_mode(sgpio_config_t* const config, const bool multi_slice);
@ -50,6 +50,3 @@ void sgpio_cpld_stream_disable(sgpio_config_t* const config);
bool sgpio_cpld_stream_is_enabled(sgpio_config_t* const config);
void sgpio_cpld_set_mixer_invert(sgpio_config_t* const config, uint_fast8_t invert);
/* Driver configuration instance. */
extern sgpio_config_t sgpio_config;

View file

@ -1,5 +1,5 @@
/*
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
*
* This file is part of HackRF.
@ -22,11 +22,9 @@
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "clock_io.h"
#include "clkin.h"
#include "delay.h"
#include "i2c_lpc.h"
#include "platform_detect.h"
#include "selftest.h"
#include "si5351c.h"
@ -37,23 +35,16 @@
#include "platform_scu.h"
#endif
#include "si5351c_regs.def"
/* Driver instance. */
si5351c_driver_t si5351c = {
.bus = &i2c0,
.i2c_address = 0x60,
};
static enum pll_sources active_clock_source = PLL_SOURCE_UNINITIALIZED;
/* External clock output default is deactivated as it creates noise */
static bool clkout_enabled = false;
static uint8_t outputs_disabled = 0xff;
/* write to single register */
void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val)
{
const uint8_t data_tx[] = {reg, val};
i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 2, NULL, 0);
if (reg < SI5351C_CACHED_REGS) {
drv->regs[reg] = val;
si5351c_reg_set_clean(drv, reg);
}
si5351c_write(drv, data_tx, 2);
}
/* read single register */
@ -62,100 +53,72 @@ uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg)
const uint8_t data_tx[] = {reg};
uint8_t data_rx[] = {0x00};
i2c_bus_transfer(drv->bus, drv->i2c_address, data_tx, 1, data_rx, 1);
uint8_t val = data_rx[0];
if (reg < SI5351C_CACHED_REGS) {
drv->regs[reg] = val;
si5351c_reg_set_clean(drv, reg);
}
return val;
return data_rx[0];
}
/* Commit changes to register values. */
void si5351c_regs_commit(si5351c_driver_t* drv)
/*
* Write to one or more contiguous registers. data[0] should be the first
* register number, one or more values follow.
*/
void si5351c_write(
si5351c_driver_t* const drv,
const uint8_t* const data,
const size_t data_count)
{
for (int start = 0; start < SI5351C_CACHED_REGS; start++) {
if (si5351c_reg_is_dirty(drv, start)) {
if (start == SI5351C_CACHED_REGS - 1) {
si5351c_write_single(drv, start, drv->regs[start]);
} else {
int end;
for (end = start + 1; end < 256; end++) {
if (!si5351c_reg_is_dirty(drv, end))
break;
}
size_t len = 1 + (end - start);
uint8_t data_tx[len];
data_tx[0] = start;
for (int i = 0; i < (end - start); i++) {
data_tx[1 + i] = drv->regs[start + i];
}
i2c_bus_transfer(
drv->bus,
drv->i2c_address,
data_tx,
len,
NULL,
0);
for (int i = start; i < end; i++) {
si5351c_reg_set_clean(drv, i);
}
start = end;
}
}
}
/* Reset bits are self-clearing. */
drv->regs[177] = 0;
i2c_bus_transfer(drv->bus, drv->i2c_address, data, data_count, NULL, 0);
}
/* Disable all CLKx outputs. */
void si5351c_disable_all_outputs(si5351c_driver_t* const drv)
{
set_all_CLK_OEB(drv, SI5351C_OUTPUT_DISABLE);
si5351c_regs_commit(drv);
outputs_disabled = 0xff;
uint8_t data[] = {3, outputs_disabled};
si5351c_write(drv, data, sizeof(data));
}
/* Disable all CLKx outputs using selected PLL. */
void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_mask_t mask)
void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_t pll)
{
/* For each CLKx output, check if it is using the specified PLL. */
for (int op = 0; op < 8; op++) {
int ms;
/* First check which multisynth is used by this output. */
switch (get_CLK_SRC(drv, op)) {
case SI5351C_SRC_MULTISYNTH_SELF:
ms = op;
break;
case SI5351C_SRC_MULTISYNTH_0_4:
ms = (op < 4) ? 0 : 4;
break;
default:
/* This output is not using any PLL */
continue;
}
/* Now check which PLL is used by that multisynth. */
if (mask & (1 << get_MS_SRC(drv, ms))) {
/* This output depends on the specified PLL; disable it. */
set_CLK_OEB(drv, op, SI5351C_OUTPUT_DISABLE);
}
/*
* Bitmask defines outputs using PLL B. Other outputs are assumed to
* use PLL A.
*/
uint8_t pllb_outputs = 0x00;
if (detected_platform() == BOARD_ID_PRALINE) {
pllb_outputs = 0x30;
}
si5351c_regs_commit(drv);
if (pll & SI5351C_PLL_A) {
outputs_disabled |= ~pllb_outputs;
}
if (pll & SI5351C_PLL_B) {
outputs_disabled |= pllb_outputs;
}
uint8_t data[] = {3, outputs_disabled};
si5351c_write(drv, data, sizeof(data));
}
/* Turn off OEB pin control for all CLKx */
void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv)
{
set_all_OEB_MASK(drv, true);
si5351c_regs_commit(drv);
uint8_t data[] = {9, 0xFF};
si5351c_write(drv, data, sizeof(data));
}
/* Power down all CLKx */
void si5351c_power_down_all_clocks(si5351c_driver_t* const drv)
{
set_all_CLK_PDN(drv, true);
set_FBA_INT(drv, true);
set_FBB_INT(drv, true);
si5351c_regs_commit(drv);
uint8_t data[] = {
16,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN,
SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE,
SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE};
si5351c_write(drv, data, sizeof(data));
}
/*
@ -165,8 +128,8 @@ void si5351c_power_down_all_clocks(si5351c_driver_t* const drv)
*/
void si5351c_set_crystal_configuration(si5351c_driver_t* const drv)
{
set_XTAL_CL(drv, SI5351C_XTAL_8PF);
si5351c_regs_commit(drv);
uint8_t data[] = {183, 0x80};
si5351c_write(drv, data, sizeof(data));
}
/*
@ -175,10 +138,8 @@ void si5351c_set_crystal_configuration(si5351c_driver_t* const drv)
*/
void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv)
{
set_CLKIN_FANOUT_EN(drv, true);
set_XO_FANOUT_EN(drv, true);
set_MS_FANOUT_EN(drv, true);
si5351c_regs_commit(drv);
uint8_t data[] = {187, 0xD0};
si5351c_write(drv, data, sizeof(data));
}
/*
@ -186,40 +147,51 @@ void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv)
* CLKIN_DIV=0 (Divide by 1)
* Set both PLLA_SRC and PLLB_SRC
*/
void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input)
void si5351c_configure_pll_sources(
si5351c_driver_t* const drv,
const enum pll_sources source)
{
set_CLKIN_DIV(drv, SI5351C_DIV_1);
set_PLLA_SRC(drv, input);
set_PLLB_SRC(drv, input);
si5351c_regs_commit(drv);
uint8_t data[] = {15, 0x00};
if (source == PLL_SOURCE_CLKIN) {
data[1] = 0x0c;
}
si5351c_write(drv, data, sizeof(data));
}
/* MultiSynth NA (PLLA) and NB (PLLB) */
void si5351c_configure_pll_multisynth(
si5351c_driver_t* const drv,
const si5351c_input_t input)
const enum pll_sources source)
{
for (si5351c_pll_t pll = SI5351C_PLL_A; pll <= SI5351C_PLL_B; pll++) {
if (input == SI5351C_INPUT_CLKIN) {
/* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */
set_MSN_P1(drv, pll, 0x2600);
} else {
/* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */
set_MSN_P1(drv, pll, 0x0E00);
}
set_MSN_P2(drv, pll, 0);
set_MSN_P3(drv, pll, 1);
/* XTAL: 25 MHz * (0x0e00 + 512) / 128 = 800 MHz, integer mode */
uint8_t data[] = {26, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00};
if (source == PLL_SOURCE_CLKIN) {
/* CLKIN: 10 MHz * (0x2600 + 512) / 128 = 800 MHz, integer mode */
data[4] = 0x26;
}
si5351c_regs_commit(drv);
si5351c_write(drv, data, sizeof(data));
/* Apply same configuration to PLL B. */
data[0] = 34;
si5351c_write(drv, data, sizeof(data));
}
void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask)
void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll)
{
si5351c_disable_pll_outputs(drv, mask);
set_PLLA_RST(drv, (mask & SI5351C_PLL_MASK_A) ? true : false);
set_PLLB_RST(drv, (mask & SI5351C_PLL_MASK_B) ? true : false);
si5351c_regs_commit(drv);
delay_ms(2);
uint8_t value = 0;
if (pll & SI5351C_PLL_A) {
value |= 0x20;
}
if (pll & SI5351C_PLL_B) {
value |= 0x80;
}
si5351c_disable_pll_outputs(drv, pll);
uint8_t data[] = {177, value};
si5351c_write(drv, data, sizeof(data));
delay_us_at_mhz(2000, 204);
si5351c_enable_clock_outputs(drv);
}
@ -243,37 +215,170 @@ void si5351c_configure_multisynth(
* ...
* 7 means divide by 128
*/
set_MS_P1(drv, ms_number, p1);
set_MS_P2(drv, ms_number, p2);
set_MS_P3(drv, ms_number, p3);
set_R_DIV(drv, ms_number, r_div);
si5351c_regs_commit(drv);
const uint_fast8_t register_number = 42 + (ms_number * 8);
uint8_t data[] = {
register_number,
(p3 >> 8) & 0xFF,
(p3 >> 0) & 0xFF,
(r_div << 4) | (0 << 2) | ((p1 >> 16) & 0x3),
(p1 >> 8) & 0xFF,
(p1 >> 0) & 0xFF,
(((p3 >> 16) & 0xF) << 4) | (((p2 >> 16) & 0xF) << 0),
(p2 >> 8) & 0xFF,
(p2 >> 0) & 0xFF};
si5351c_write(drv, data, sizeof(data));
}
void si5351c_configure_clock_control(si5351c_driver_t* const drv)
{
for (int i = 0; i < 8; i++) {
set_CLK_PDN(drv, i, drv->clk[i].power_down);
set_MS_INT(drv, i, drv->clk[i].mode);
set_MS_SRC(drv, i, drv->clk[i].pll);
set_CLK_SRC(drv, i, drv->clk[i].source);
set_CLK_IDRV(drv, i, drv->clk[i].drive);
set_CLK_INV(drv, i, drv->clk[i].invert);
uint8_t clkout_ctrl;
if (clkout_enabled) {
clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA);
} else {
clkout_ctrl = SI5351C_CLK_POWERDOWN | SI5351C_CLK_INT_MODE;
}
si5351c_regs_commit(drv);
/* Clock to CPU is deactivated as it is not used and creates noise */
/* External clock output is kept in current state */
uint8_t data[] = {
16,
SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA),
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA) | SI5351C_CLK_INV,
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_0_4) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA),
clkout_ctrl,
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA) | SI5351C_CLK_INV,
SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA),
SI5351C_CLK_POWERDOWN |
SI5351C_CLK_INT_MODE, /* not connected, but: PLL A int mode */
SI5351C_CLK_POWERDOWN |
SI5351C_CLK_INT_MODE /* not connected, but: PLL B int mode */
};
#ifdef IS_H1_R9
if (IS_H1_R9) {
data[1] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_6MA);
data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA);
data[3] = clkout_ctrl;
data[4] = SI5351C_CLK_POWERDOWN;
data[5] = SI5351C_CLK_POWERDOWN;
data[6] = SI5351C_CLK_POWERDOWN;
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
/* CLK0: AFE_CLK */
data[1] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA);
/* CLK1: SCT_CLK and FPGA_CLK */
data[2] = SI5351C_CLK_FRAC_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA);
/* CLK3: CLKOUT */
clkout_ctrl = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_8MA);
/* CLK4: XCVR_CLK */
data[5] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA) | SI5351C_CLK_INV;
data[6] = SI5351C_CLK_INT_MODE | SI5351C_CLK_PLL_SRC(SI5351C_PLL_B) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_4MA);
if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) {
/* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */
data[3] = SI5351C_CLK_FRAC_MODE |
SI5351C_CLK_PLL_SRC(SI5351C_PLL_A) |
SI5351C_CLK_SRC(SI5351C_CLK_SRC_MULTISYNTH_SELF) |
SI5351C_CLK_IDRV(SI5351C_CLK_IDRV_2MA);
} else {
/* CLK2: MCU_CLK */
data[3] = SI5351C_CLK_POWERDOWN;
}
}
#endif
si5351c_write(drv, data, sizeof(data));
}
#define SI5351C_CLK_ENABLE(x) (0 << x)
#define SI5351C_CLK_DISABLE(x) (1 << x)
#define SI5351C_REG_OUTPUT_EN (3)
void si5351c_enable_clock_outputs(si5351c_driver_t* const drv)
{
for (int i = 0; i < 8; i++) {
set_CLK_OEB(
drv,
i,
drv->clk[i].output_enable ? SI5351C_OUTPUT_ENABLE :
SI5351C_OUTPUT_DISABLE);
/* Enable CLK outputs 0, 1, 2, 4, 5 only. */
/* Praline: enable 0, 1, 4, 5 only. */
/* 7: Clock to CPU is deactivated as it is not used and creates noise */
/* 3: External clock output is deactivated by default */
uint8_t clkout = 3;
uint8_t value = 0;
#ifdef IS_PRALINE
if (IS_PRALINE) {
value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) |
SI5351C_CLK_ENABLE(4) | SI5351C_CLK_ENABLE(5) |
SI5351C_CLK_DISABLE(6) | SI5351C_CLK_DISABLE(7);
if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) {
/* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */
value |= SI5351C_CLK_ENABLE(2);
} else {
value |= SI5351C_CLK_DISABLE(2);
}
}
si5351c_regs_commit(drv);
#endif
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) |
SI5351C_CLK_ENABLE(2) | SI5351C_CLK_ENABLE(4) |
SI5351C_CLK_ENABLE(5) | SI5351C_CLK_DISABLE(6) |
SI5351C_CLK_DISABLE(7);
/* HackRF One r9 has only three clock generator outputs. */
#ifdef IS_H1_R9
if (IS_H1_R9) {
clkout = 2;
value = SI5351C_CLK_ENABLE(0) | SI5351C_CLK_ENABLE(1) |
SI5351C_CLK_DISABLE(3) | SI5351C_CLK_DISABLE(4) |
SI5351C_CLK_DISABLE(5) | SI5351C_CLK_DISABLE(6) |
SI5351C_CLK_DISABLE(7);
}
#endif
}
#endif
value |= (clkout_enabled) ? SI5351C_CLK_ENABLE(clkout) :
SI5351C_CLK_DISABLE(clkout);
uint8_t data[] = {SI5351C_REG_OUTPUT_EN, value};
si5351c_write(drv, data, sizeof(data));
outputs_disabled = value;
#ifdef IS_H1_R9
if (IS_H1_R9) {
const platform_gpio_t* gpio = platform_gpio();
if (clkout_enabled) {
gpio_set(gpio->h1r9_clkout_en);
} else {
gpio_clear(gpio->h1r9_clkout_en);
}
}
#endif
}
void si5351c_set_int_mode(
@ -281,13 +386,25 @@ void si5351c_set_int_mode(
const uint_fast8_t ms_number,
const uint_fast8_t on)
{
set_MS_INT(drv, ms_number, on);
si5351c_regs_commit(drv);
uint8_t data[] = {16, 0};
if (ms_number < 8) {
data[0] = 16 + ms_number;
data[1] = si5351c_read_single(drv, data[0]);
if (on) {
data[1] |= SI5351C_CLK_INT_MODE;
} else {
data[1] &= ~(SI5351C_CLK_INT_MODE);
}
si5351c_write(drv, data, 2);
}
}
void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input)
void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source)
{
if (drv->input_initialized && input == drv->active_input) {
if (source == active_clock_source) {
return;
}
si5351c_disable_all_outputs(drv);
@ -297,8 +414,8 @@ void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input)
* HackRF One r9 always uses PLL A on the XTAL input
* but externally switches that input to CLKIN.
*/
si5351c_configure_inputs(drv, SI5351C_INPUT_XTAL);
if (input == SI5351C_INPUT_CLKIN) {
si5351c_configure_pll_sources(drv, PLL_SOURCE_XTAL);
if (source == PLL_SOURCE_CLKIN) {
gpio_set(platform_gpio()->h1r9_clkin_en);
} else {
gpio_clear(platform_gpio()->h1r9_clkin_en);
@ -307,13 +424,12 @@ void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input)
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
si5351c_configure_inputs(drv, input);
si5351c_configure_pll_sources(drv, source);
}
#endif
si5351c_configure_pll_multisynth(drv, input);
drv->active_input = input;
drv->input_initialized = true;
si5351c_reset_plls(drv, SI5351C_PLL_MASK_BOTH);
si5351c_configure_pll_multisynth(drv, source);
active_clock_source = source;
si5351c_reset_pll(drv, SI5351C_PLL_BOTH);
}
bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv)
@ -322,93 +438,31 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv)
uint32_t f = clkin_frequency();
return (f > 9000000) && (f < 11000000);
} else {
si5351c_read_single(drv, LOS_CLKIN);
bool los_clkin = get_LOS_CLKIN(drv);
return !los_clkin;
return (si5351c_read_single(drv, 0) & SI5351C_LOS) == 0;
}
}
static void si5351c_clkout_ms_enable(si5351c_driver_t* const drv, bool enable)
void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable)
{
drv->clk[drv->clkout_id].output_enable = enable;
drv->clk[drv->clkout_id].power_down = !enable;
clkout_enabled = (enable > 0);
uint8_t clkout = 3;
/* HackRF One r9 has only three clock generator outputs. */
if (detected_platform() == BOARD_ID_HACKRF1_R9) {
clkout = 2;
}
/* Configure clock to 10MHz */
si5351c_configure_multisynth(drv, drv->clkout_id, 80 * 128 - 512, 0, 1, 0);
si5351c_configure_multisynth(drv, clkout, 80 * 128 - 512, 0, 1, 0);
si5351c_configure_clock_control(drv);
si5351c_enable_clock_outputs(drv);
}
void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable)
{
#ifdef IS_H1_R9
if (IS_H1_R9) {
const platform_gpio_t* gpio = platform_gpio();
/* CLKOUT is shared with MCU_CLK, enable MS when either on. */
bool mcu_clkin_enabled = gpio_read(gpio->h1r9_mcu_clk_en);
bool ms_needed = enable | mcu_clkin_enabled;
si5351c_clkout_ms_enable(drv, ms_needed);
/* Set GPIO to gate CLKOUT output downstream of MS. */
gpio_write(gpio->h1r9_clkout_en, enable);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
/* We have a dedicated CLKOUT multisynth. */
si5351c_clkout_ms_enable(drv, enable);
}
#endif
}
void si5351c_mcu_clkin_enable(si5351c_driver_t* const drv, bool enable)
{
#ifdef IS_H1_R9
if (IS_H1_R9) {
const platform_gpio_t* gpio = platform_gpio();
/* MCU_CLK is shared with CLKOUT, enable MS when either on. */
bool clkout_enabled = gpio_read(gpio->h1r9_clkout_en);
bool ms_needed = enable | clkout_enabled;
si5351c_clkout_ms_enable(drv, ms_needed);
/* Set GPIO to gate MCU_CLK output downstream of MS. */
gpio_write(gpio->h1r9_mcu_clk_en, enable);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
drv->clk[drv->mcu_clkin_id].output_enable = enable;
drv->clk[drv->mcu_clkin_id].power_down = !enable;
/* Configure clock to 40MHz */
if (drv->mcu_clkin_id >= 6) {
// MCU_CLKIN on CLK6 or CLK7, integer only MS.
si5351c_configure_multisynth(drv, drv->mcu_clkin_id, 20, 0, 0, 0);
} else {
// MCU_CLKIN on CLK0 to CLK5, fractional-capable MS.
si5351c_configure_multisynth(
drv,
drv->mcu_clkin_id,
20 * 128 - 512,
0,
1,
0);
}
si5351c_configure_clock_control(drv);
si5351c_enable_clock_outputs(drv);
}
#endif
}
void si5351c_init(si5351c_driver_t* const drv)
{
/* Read revision ID */
si5351c_read_single(drv, REVID);
selftest.si5351_rev_id = get_REVID(drv);
selftest.si5351_rev_id = si5351c_read_single(drv, 0) & SI5351C_REVID;
/* Read back interrupt status mask register, flip the mask bits and verify. */
uint8_t int_mask = si5351c_read_single(drv, 2);
@ -427,22 +481,6 @@ void si5351c_init(si5351c_driver_t* const drv)
selftest.report.pass = false;
}
/* Wait for on-chip initialization to complete. */
while (get_SYS_INIT(drv)) {
si5351c_read_single(drv, SYS_INIT);
}
/* Cache all current register values. */
uint8_t data_tx[] = {0};
i2c_bus_transfer(
drv->bus,
drv->i2c_address,
data_tx,
1,
drv->regs,
sizeof(drv->regs));
memset(drv->regs_dirty, 0, sizeof(drv->regs_dirty));
#ifdef IS_H1_R9
if (IS_H1_R9) {
const platform_gpio_t* gpio = platform_gpio();
@ -464,170 +502,6 @@ void si5351c_init(si5351c_driver_t* const drv)
gpio_output(gpio->h1r9_mcu_clk_en);
}
#endif
si5351c_clk_t clkout = {
.output_enable = false,
.power_down = true,
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_8MA,
};
si5351c_clk_t mcu_clkin = {
.output_enable = false,
.power_down = true,
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_2MA,
};
si5351c_clk_t powered_down = {
.output_enable = false,
.power_down = true,
.mode = SI5351C_MODE_INT,
};
/* CLK0: MAX5864/CPLD */
drv->clk[0] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_8MA,
};
/* CLK1: CPLD */
drv->clk[1] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_0_4,
.drive = SI5351C_DRIVE_2MA,
.invert = true,
};
/* CLK2: SGPIO */
drv->clk[2] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_0_4,
.drive = SI5351C_DRIVE_2MA,
};
/* CLK3: CLKOUT */
drv->clk[3] = clkout;
drv->clkout_id = 3;
/* CLK4: RFFC5072 (MAX2837 on rad1o) */
drv->clk[4] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_6MA,
.invert = true,
};
/* CLK5: MAX2837 (MAX2871 on rad1o) */
drv->clk[5] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
};
/* CLK6: none */
drv->clk[6] = powered_down;
/* CLK7: LPC43xx */
drv->clk[7] = mcu_clkin;
drv->mcu_clkin_id = 7;
#ifdef IS_H1_R9
if (IS_H1_R9) {
/* CLK0: MAX5864/CPLD/SGPIO (sample clocks) */
drv->clk[0] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_6MA,
};
/* CLK1: RFFC5072/MAX2839 */
drv->clk[1] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
};
/* CLK2: CLKOUT/LPC4320 */
drv->clk[2] = clkout;
drv->clkout_id = 2;
drv->mcu_clkin_id = 2;
/* Other outputs not present on Si5351A */
drv->clk[3] = powered_down;
drv->clk[4] = powered_down;
drv->clk[5] = powered_down;
drv->clk[6] = powered_down;
drv->clk[7] = powered_down;
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
/* CLK0: AFE_CLK */
drv->clk[0] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
};
/* CLK1: SCT_CLK and FPGA_CLK */
drv->clk[1] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_2MA,
};
/* CLK3: CLKOUT */
clkout.pll = SI5351C_PLL_B;
drv->clk[3] = clkout;
drv->clkout_id = 3;
/* CLK4: XCVR_CLK */
drv->clk[4] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_B,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
.invert = true,
};
/* CLK5: MIX_CLK */
drv->clk[5] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_INT,
.pll = SI5351C_PLL_B,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_4MA,
};
if ((detected_revision() & ~BOARD_REV_GSG) < BOARD_REV_PRALINE_R1_1) {
/* CLK2: FPGA_CLK (not shared with SCT_CLK on older boards) */
drv->clk[2] = (si5351c_clk_t){
.output_enable = true,
.mode = SI5351C_MODE_FRAC,
.pll = SI5351C_PLL_A,
.source = SI5351C_SRC_MULTISYNTH_SELF,
.drive = SI5351C_DRIVE_2MA,
};
} else {
/* CLK2: MCU_CLK */
drv->clk[2] = mcu_clkin;
drv->mcu_clkin_id = 2;
}
// Use PLL B for MCU, since we reset PLL A during sample_rate_set.
drv->clk[drv->mcu_clkin_id].pll = SI5351C_PLL_B;
}
#endif
}
/*
@ -642,6 +516,8 @@ void si5351c_set_phase(
const uint8_t ms_number,
const uint8_t offset)
{
set_CLK_PHOFF(drv, ms_number, offset);
si5351c_regs_commit(drv);
const uint8_t address = 165 + ms_number;
if (ms_number < 8) {
si5351c_write_single(drv, address, offset & 0x7f);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
*
* This file is part of HackRF.
@ -28,86 +28,50 @@ extern "C" {
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "i2c_bus.h"
#define SI_INTDIV(x) (x * 128 - 512)
#define SI5351C_CACHED_REGS 188
#define SI5351C_CLK_POWERDOWN (1 << 7)
#define SI5351C_CLK_INT_MODE (1 << 6)
#define SI5351C_CLK_FRAC_MODE (0 << 6)
#define SI5351C_CLK_INV (1 << 4)
#define SI5351C_CLK_SRC(x) (x << 2)
#define SI5351C_CLK_SRC_XTAL 0
#define SI5351C_CLK_SRC_CLKIN 1
#define SI5351C_CLK_SRC_MULTISYNTH_0_4 2
#define SI5351C_CLK_SRC_MULTISYNTH_SELF 3
#define SI5351C_CLK_IDRV(x) (x << 0)
#define SI5351C_CLK_IDRV_2MA 0
#define SI5351C_CLK_IDRV_4MA 1
#define SI5351C_CLK_IDRV_6MA 2
#define SI5351C_CLK_IDRV_8MA 3
#define SI5351C_LOS (1 << 4)
#define SI5351C_REVID 0x03
typedef enum {
SI5351C_MODE_FRAC = 0,
SI5351C_MODE_INT = 1,
} si5351c_mode_t;
typedef enum {
SI5351C_SRC_XTAL = 0,
SI5351C_SRC_CLKIN = 1,
SI5351C_SRC_MULTISYNTH_0_4 = 2,
SI5351C_SRC_MULTISYNTH_SELF = 3,
} si5351c_src_t;
typedef enum {
SI5351C_DRIVE_2MA = 0,
SI5351C_DRIVE_4MA = 1,
SI5351C_DRIVE_6MA = 2,
SI5351C_DRIVE_8MA = 3,
} si5351c_drive_t;
typedef enum {
SI5351C_PLL_A = 0,
SI5351C_PLL_B = 1,
SI5351C_PLL_A = 1,
SI5351C_PLL_B = 2,
SI5351C_PLL_BOTH = 3,
} si5351c_pll_t;
typedef enum {
SI5351C_PLL_MASK_A = 1,
SI5351C_PLL_MASK_B = 2,
SI5351C_PLL_MASK_BOTH = 3,
} si5351c_pll_mask_t;
#define SI5351C_CLK_PLL_SRC(x) ((x & SI5351C_PLL_B) << 4)
typedef enum {
SI5351C_XTAL_6PF = 1,
SI5351C_XTAL_8PF = 2,
SI5351C_XTAL_10PF = 3,
} si5351c_xtal_t;
typedef enum {
SI5351C_OUTPUT_ENABLE = 0,
SI5351C_OUTPUT_DISABLE = 1,
} si5351c_output_t;
typedef enum {
SI5351C_DIV_1 = 0,
SI5351C_DIV_2 = 1,
SI5351C_DIV_4 = 2,
SI5351C_DIV_8 = 3,
} si5351c_div_t;
typedef enum {
SI5351C_INPUT_XTAL = 0,
SI5351C_INPUT_CLKIN = 1,
} si5351c_input_t;
typedef struct {
bool output_enable;
bool power_down;
si5351c_mode_t mode;
si5351c_pll_t pll;
si5351c_src_t source;
si5351c_drive_t drive;
bool invert;
} si5351c_clk_t;
enum pll_sources {
PLL_SOURCE_UNINITIALIZED = -1,
PLL_SOURCE_XTAL = 0,
PLL_SOURCE_CLKIN = 1,
};
typedef struct {
i2c_bus_t* const bus;
uint8_t i2c_address;
si5351c_clk_t clk[8];
uint8_t clkout_id;
uint8_t mcu_clkin_id;
bool input_initialized;
si5351c_input_t active_input;
uint8_t regs[SI5351C_CACHED_REGS];
uint32_t regs_dirty[(SI5351C_CACHED_REGS + 31) / 32];
} si5351c_driver_t;
void si5351c_disable_all_outputs(si5351c_driver_t* const drv);
@ -115,11 +79,13 @@ void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv);
void si5351c_power_down_all_clocks(si5351c_driver_t* const drv);
void si5351c_set_crystal_configuration(si5351c_driver_t* const drv);
void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv);
void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input);
void si5351c_configure_pll_sources(
si5351c_driver_t* const drv,
const enum pll_sources source);
void si5351c_configure_pll_multisynth(
si5351c_driver_t* const drv,
const si5351c_input_t input);
void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask);
const enum pll_sources source);
void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll);
void si5351c_configure_multisynth(
si5351c_driver_t* const drv,
const uint_fast8_t ms_number,
@ -133,22 +99,22 @@ void si5351c_set_int_mode(
si5351c_driver_t* const drv,
const uint_fast8_t ms_number,
const uint_fast8_t on);
void si5351c_change_input(si5351c_driver_t* const drv, const si5351c_input_t input);
void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source);
bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv);
void si5351c_write_single(si5351c_driver_t* const drv, uint8_t reg, uint8_t val);
uint8_t si5351c_read_single(si5351c_driver_t* const drv, uint8_t reg);
void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable);
void si5351c_mcu_clkin_enable(si5351c_driver_t* const drv, bool enable);
void si5351c_write(
si5351c_driver_t* const drv,
const uint8_t* const data,
const size_t data_count);
void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable);
void si5351c_init(si5351c_driver_t* const drv);
void si5351c_set_phase(
si5351c_driver_t* const drv,
const uint8_t ms_number,
const uint8_t offset);
/* Driver Instance. */
extern si5351c_driver_t si5351c;
#ifdef __cplusplus
}
#endif

View file

@ -1,494 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
static inline void si5351c_reg_set_clean(si5351c_driver_t* drv, int reg)
{
drv->regs_dirty[reg / 32] &= ~(1 << (reg % 32));
}
static inline void si5351c_reg_set_dirty(si5351c_driver_t* drv, int reg)
{
drv->regs_dirty[reg / 32] |= (1 << (reg % 32));
}
static inline bool si5351c_reg_is_dirty(si5351c_driver_t* drv, int reg)
{
return (drv->regs_dirty[reg / 32] & (1 << (reg % 32))) ? true : false;
}
/* Generate static inline accessors that operate on the global
* regs. Done this way to (1) allow defs to be scraped out and used
* elsewhere, e.g. in scripts, (2) to avoid dealing with endian
* (structs). This may be used in firmware, or on host predefined
* register loads. */
/* On set_, register is always set dirty, even if nothing
* changed. This makes sure that writes that have side effects,
* e.g. frequency setting, are not skipped. */
/* n=name, r=regnum, o=offset (bits from LSB) of LSB of field,
* l=length (bits) */
#define __MREG__(n,r,o,l) \
static inline uint8_t get_##n(si5351c_driver_t* const _d) { \
return (_d->regs[r] >> o) & ((1L<<l)-1); \
} \
static inline void set_##n(si5351c_driver_t* const _d, uint8_t v) { \
_d->regs[r] &= (uint8_t)(~(((1L<<l)-1)<<o)); \
_d->regs[r] |= (uint8_t)(((v&((1L<<l)-1))<<o)); \
si5351c_reg_set_dirty(_d, r); \
} \
const uint8_t n = r;
/* Generate accessors for numeric fields split across multiple registers */
/* n = name */
#define __2PART__(n) \
static inline uint16_t get_##n(si5351c_driver_t* const _d) { \
return (get_##n##_TOP(_d) << 8) | get_##n##_BOT(_d); \
} \
static inline void set_##n(si5351c_driver_t* const _d, uint16_t v) { \
set_##n##_TOP(_d, v >> 8); \
set_##n##_BOT(_d, v); \
}
#define __3PART__(n) \
static inline uint32_t get_##n(si5351c_driver_t* const _d) { \
return (get_##n##_TOP(_d) << 16) | (get_##n##_MID(_d) << 8) | get_##n##_BOT(_d); \
} \
static inline void set_##n(si5351c_driver_t* const _d, uint32_t v) { \
set_##n##_TOP(_d, v >> 16); \
set_##n##_MID(_d, v >> 8); \
set_##n##_BOT(_d, v); \
}
/* Generate accessors for per-PLL and per-multisynth registers */
/* n=name, p=prefix, s=suffix */
#define __PER_PLL__(n, p, s) \
static inline uint32_t get_##n(si5351c_driver_t* const _d, si5351c_pll_t pll) { \
return (pll == SI5351C_PLL_A) ? get_##p##A##s(_d) : get_##p##B##s(_d); \
} \
static inline void set_##n(si5351c_driver_t* const _d, si5351c_pll_t pll, uint32_t v) { \
if (pll == SI5351C_PLL_A) { \
set_##p##A##s(_d, v); \
} else { \
set_##p##B##s(_d, v); \
} \
}
#define __PER_MS__(n, p, s) \
static inline uint32_t get_##n(si5351c_driver_t* const _d, uint8_t i) { \
switch (i) { \
case 0: return get_##p##0##s(_d); \
case 1: return get_##p##1##s(_d); \
case 2: return get_##p##2##s(_d); \
case 3: return get_##p##3##s(_d); \
case 4: return get_##p##4##s(_d); \
case 5: return get_##p##5##s(_d); \
case 6: return get_##p##6##s(_d); \
case 7: return get_##p##7##s(_d); \
default: return 0; \
} \
} \
static inline void set_##n(si5351c_driver_t* const _d, uint8_t i, uint32_t v) { \
switch (i) { \
case 0: set_##p##0##s(_d, v); return; \
case 1: set_##p##1##s(_d, v); return; \
case 2: set_##p##2##s(_d, v); return; \
case 3: set_##p##3##s(_d, v); return; \
case 4: set_##p##4##s(_d, v); return; \
case 5: set_##p##5##s(_d, v); return; \
case 6: set_##p##6##s(_d, v); return; \
case 7: set_##p##7##s(_d, v); return; \
} \
} \
static inline void set_all_##n(si5351c_driver_t* const _d, uint32_t v) { \
set_##p##0##s(_d, v); \
set_##p##1##s(_d, v); \
set_##p##2##s(_d, v); \
set_##p##3##s(_d, v); \
set_##p##4##s(_d, v); \
set_##p##5##s(_d, v); \
set_##p##6##s(_d, v); \
set_##p##7##s(_d, v); \
}
#define __PER_FRAC_MS__(n, p, s) \
static inline void set_##n(si5351c_driver_t* const _d, uint8_t i, uint32_t v) { \
switch (i) { \
case 0: set_##p##0##s(_d, v); return; \
case 1: set_##p##1##s(_d, v); return; \
case 2: set_##p##2##s(_d, v); return; \
case 3: set_##p##3##s(_d, v); return; \
case 4: set_##p##4##s(_d, v); return; \
case 5: set_##p##5##s(_d, v); return; \
} \
}
/* Register 0: Device Status */
__MREG__(SYS_INIT, 0, 7, 1)
__MREG__(LOL_B, 0, 6, 1)
__MREG__(LOL_A, 0, 5, 1)
__MREG__(LOS_CLKIN, 0, 4, 1)
__MREG__(LOS_XTAL, 0, 3, 1)
__MREG__(REVID, 0, 0, 2)
/* Register 1: Interrupt Status Sticky */
__MREG__(SYS_INIT_STKY, 1, 7, 1)
__MREG__(LOL_B_STKY, 1, 6, 1)
__MREG__(LOL_A_STKY, 1, 5, 1)
__MREG__(LOS_CLKIN_STKY, 1, 4, 1)
__MREG__(LOS_XTAL_STKY, 1, 3, 1)
/* Register 2: Interrupt Status Mask */
__MREG__(SYS_INIT_MASK, 2, 7, 1)
__MREG__(LOL_B_MASK, 2, 6, 1)
__MREG__(LOL_A_MASK, 2, 5, 1)
__MREG__(LOS_CLKIN_MASK, 2, 4, 1)
__MREG__(LOS_XTAL_MASK, 2, 3, 1)
/* Register 3: Output Enable Control */
__MREG__(CLK7_OEB, 3, 7, 1)
__MREG__(CLK6_OEB, 3, 6, 1)
__MREG__(CLK5_OEB, 3, 5, 1)
__MREG__(CLK4_OEB, 3, 4, 1)
__MREG__(CLK3_OEB, 3, 3, 1)
__MREG__(CLK2_OEB, 3, 2, 1)
__MREG__(CLK1_OEB, 3, 1, 1)
__MREG__(CLK0_OEB, 3, 0, 1)
/* Register 4: OEB Pin Enable Control */
__MREG__(OEB_CLK7, 4, 7, 1)
__MREG__(OEB_CLK6, 4, 6, 1)
__MREG__(OEB_CLK5, 4, 5, 1)
__MREG__(OEB_CLK4, 4, 4, 1)
__MREG__(OEB_CLK3, 4, 3, 1)
__MREG__(OEB_CLK2, 4, 2, 1)
__MREG__(OEB_CLK1, 4, 1, 1)
__MREG__(OEB_CLK0, 4, 0, 1)
/* Register 9: OEB Pin Enable Control Mask */
__MREG__(OEB_MASK7, 9, 7, 1)
__MREG__(OEB_MASK6, 9, 6, 1)
__MREG__(OEB_MASK5, 9, 5, 1)
__MREG__(OEB_MASK4, 9, 4, 1)
__MREG__(OEB_MASK3, 9, 3, 1)
__MREG__(OEB_MASK2, 9, 2, 1)
__MREG__(OEB_MASK1, 9, 1, 1)
__MREG__(OEB_MASK0, 9, 0, 1)
/* Register 15: PLL Input Source */
__MREG__(CLKIN_DIV, 15, 6, 2)
__MREG__(PLLB_SRC, 15, 3, 1)
__MREG__(PLLA_SRC, 15, 2, 1)
/* Register 16: CLK0 Control */
__MREG__(CLK0_PDN, 16, 7, 1)
__MREG__(MS0_INT, 16, 6, 1)
__MREG__(MS0_SRC, 16, 5, 1)
__MREG__(CLK0_INV, 16, 4, 1)
__MREG__(CLK0_SRC, 16, 2, 2)
__MREG__(CLK0_IDRV, 16, 0, 2)
/* Register 17: CLK1 Control */
__MREG__(CLK1_PDN, 17, 7, 1)
__MREG__(MS1_INT, 17, 6, 1)
__MREG__(MS1_SRC, 17, 5, 1)
__MREG__(CLK1_INV, 17, 4, 1)
__MREG__(CLK1_SRC, 17, 2, 2)
__MREG__(CLK1_IDRV, 17, 0, 2)
/* Register 18: CLK2 Control */
__MREG__(CLK2_PDN, 18, 7, 1)
__MREG__(MS2_INT, 18, 6, 1)
__MREG__(MS2_SRC, 18, 5, 1)
__MREG__(CLK2_INV, 18, 4, 1)
__MREG__(CLK2_SRC, 18, 2, 2)
__MREG__(CLK2_IDRV, 18, 0, 2)
/* Register 19: CLK3 Control */
__MREG__(CLK3_PDN, 19, 7, 1)
__MREG__(MS3_INT, 19, 6, 1)
__MREG__(MS3_SRC, 19, 5, 1)
__MREG__(CLK3_INV, 19, 4, 1)
__MREG__(CLK3_SRC, 19, 2, 2)
__MREG__(CLK3_IDRV, 19, 0, 2)
/* Register 20: CLK4 Control */
__MREG__(CLK4_PDN, 20, 7, 1)
__MREG__(MS4_INT, 20, 6, 1)
__MREG__(MS4_SRC, 20, 5, 1)
__MREG__(CLK4_INV, 20, 4, 1)
__MREG__(CLK4_SRC, 20, 2, 2)
__MREG__(CLK4_IDRV, 20, 0, 2)
/* Register 21: CLK5 Control */
__MREG__(CLK5_PDN, 21, 7, 1)
__MREG__(MS5_INT, 21, 6, 1)
__MREG__(MS5_SRC, 21, 5, 1)
__MREG__(CLK5_INV, 21, 4, 1)
__MREG__(CLK5_SRC, 21, 2, 2)
__MREG__(CLK5_IDRV, 21, 0, 2)
/* Register 22: CLK6 Control */
__MREG__(CLK6_PDN, 22, 7, 1)
__MREG__(FBA_INT, 22, 6, 1)
__MREG__(MS6_SRC, 22, 5, 1)
__MREG__(CLK6_INV, 22, 4, 1)
__MREG__(CLK6_SRC, 22, 2, 2)
__MREG__(CLK6_IDRV, 22, 0, 2)
/* Register 23: CLK7 Control */
__MREG__(CLK7_PDN, 23, 7, 1)
__MREG__(FBB_INT, 23, 6, 1)
__MREG__(MS7_SRC, 23, 5, 1)
__MREG__(CLK7_INV, 23, 4, 1)
__MREG__(CLK7_SRC, 23, 2, 2)
__MREG__(CLK7_IDRV, 23, 0, 2)
/* Register 24: CLK0-CLK3 Disable State */
__MREG__(CLK3_DIS_STATE, 24, 6, 2)
__MREG__(CLK2_DIS_STATE, 24, 4, 2)
__MREG__(CLK1_DIS_STATE, 24, 2, 2)
__MREG__(CLK0_DIS_STATE, 24, 0, 2)
/* Register 25: CLK4-CLK7 Disable State */
__MREG__(CLK7_DIS_STATE, 25, 6, 2)
__MREG__(CLK6_DIS_STATE, 25, 4, 2)
__MREG__(CLK5_DIS_STATE, 25, 2, 2)
__MREG__(CLK4_DIS_STATE, 25, 0, 2)
/* Registers 26-33: Multisynth NA Parameters */
__MREG__(MSNA_P3_MID, 26, 0, 8)
__MREG__(MSNA_P3_BOT, 27, 0, 8)
__MREG__(MSNA_P1_TOP, 28, 0, 2)
__MREG__(MSNA_P1_MID, 29, 0, 8)
__MREG__(MSNA_P1_BOT, 30, 0, 8)
__MREG__(MSNA_P3_TOP, 31, 4, 4)
__MREG__(MSNA_P2_TOP, 31, 0, 4)
__MREG__(MSNA_P2_MID, 32, 0, 8)
__MREG__(MSNA_P2_BOT, 33, 0, 8)
/* Registers 34-41: Multisynth NB Parameters */
__MREG__(MSNB_P3_MID, 34, 0, 8)
__MREG__(MSNB_P3_BOT, 35, 0, 8)
__MREG__(MSNB_P1_TOP, 36, 0, 2)
__MREG__(MSNB_P1_MID, 37, 0, 8)
__MREG__(MSNB_P1_BOT, 38, 0, 8)
__MREG__(MSNB_P3_TOP, 39, 4, 4)
__MREG__(MSNB_P2_TOP, 39, 0, 4)
__MREG__(MSNB_P2_MID, 40, 0, 8)
__MREG__(MSNB_P2_BOT, 41, 0, 8)
/* Registers 42-49: Multisynth 0 Parameters */
__MREG__(MS0_P3_MID, 42, 0, 8)
__MREG__(MS0_P3_BOT, 43, 0, 8)
__MREG__(R0_DIV, 44, 4, 3)
__MREG__(MS0_DIVBY4, 44, 2, 2)
__MREG__(MS0_P1_TOP, 44, 0, 2)
__MREG__(MS0_P1_MID, 45, 0, 8)
__MREG__(MS0_P1_BOT, 46, 0, 8)
__MREG__(MS0_P3_TOP, 47, 4, 4)
__MREG__(MS0_P2_TOP, 47, 0, 4)
__MREG__(MS0_P2_MID, 48, 0, 8)
__MREG__(MS0_P2_BOT, 49, 0, 8)
/* Registers 50-57: Multisynth 1 Parameters */
__MREG__(MS1_P3_MID, 50, 0, 8)
__MREG__(MS1_P3_BOT, 51, 0, 8)
__MREG__(R1_DIV, 52, 4, 3)
__MREG__(MS1_DIVBY4, 52, 2, 2)
__MREG__(MS1_P1_TOP, 52, 0, 2)
__MREG__(MS1_P1_MID, 53, 0, 8)
__MREG__(MS1_P1_BOT, 54, 0, 8)
__MREG__(MS1_P3_TOP, 55, 4, 4)
__MREG__(MS1_P2_TOP, 55, 0, 4)
__MREG__(MS1_P2_MID, 56, 0, 8)
__MREG__(MS1_P2_BOT, 57, 0, 8)
/* Registers 58-65: Multisynth 2 Parameters */
__MREG__(MS2_P3_MID, 58, 0, 8)
__MREG__(MS2_P3_BOT, 59, 0, 8)
__MREG__(R2_DIV, 60, 4, 3)
__MREG__(MS2_DIVBY4, 60, 2, 2)
__MREG__(MS2_P1_TOP, 60, 0, 2)
__MREG__(MS2_P1_MID, 61, 0, 8)
__MREG__(MS2_P1_BOT, 62, 0, 8)
__MREG__(MS2_P3_TOP, 63, 4, 4)
__MREG__(MS2_P2_TOP, 63, 0, 4)
__MREG__(MS2_P2_MID, 64, 0, 8)
__MREG__(MS2_P2_BOT, 65, 0, 8)
/* Registers 66-73: Multisynth 3 Parameters */
__MREG__(MS3_P3_MID, 66, 0, 8)
__MREG__(MS3_P3_BOT, 67, 0, 8)
__MREG__(R3_DIV, 68, 4, 3)
__MREG__(MS3_DIVBY4, 68, 2, 2)
__MREG__(MS3_P1_TOP, 68, 0, 2)
__MREG__(MS3_P1_MID, 69, 0, 8)
__MREG__(MS3_P1_BOT, 70, 0, 8)
__MREG__(MS3_P3_TOP, 71, 4, 4)
__MREG__(MS3_P2_TOP, 71, 0, 4)
__MREG__(MS3_P2_MID, 72, 0, 8)
__MREG__(MS3_P2_BOT, 73, 0, 8)
/* Registers 74-81: Multisynth 4 Parameters */
__MREG__(MS4_P3_MID, 74, 0, 8)
__MREG__(MS4_P3_BOT, 75, 0, 8)
__MREG__(R4_DIV, 76, 4, 3)
__MREG__(MS4_DIVBY4, 76, 2, 2)
__MREG__(MS4_P1_TOP, 76, 0, 2)
__MREG__(MS4_P1_MID, 77, 0, 8)
__MREG__(MS4_P1_BOT, 78, 0, 8)
__MREG__(MS4_P3_TOP, 79, 4, 4)
__MREG__(MS4_P2_TOP, 79, 0, 4)
__MREG__(MS4_P2_MID, 80, 0, 8)
__MREG__(MS4_P2_BOT, 81, 0, 8)
/* Registers 82-89: Multisynth 5 Parameters */
__MREG__(MS5_P3_MID, 82, 0, 8)
__MREG__(MS5_P3_BOT, 83, 0, 8)
__MREG__(R5_DIV, 84, 4, 3)
__MREG__(MS5_DIVBY4, 84, 2, 2)
__MREG__(MS5_P1_TOP, 84, 0, 2)
__MREG__(MS5_P1_MID, 85, 0, 8)
__MREG__(MS5_P1_BOT, 86, 0, 8)
__MREG__(MS5_P3_TOP, 87, 4, 4)
__MREG__(MS5_P2_TOP, 87, 0, 4)
__MREG__(MS5_P2_MID, 88, 0, 8)
__MREG__(MS5_P2_BOT, 89, 0, 8)
/* Register 90: Multisynth 6 Parameters */
__MREG__(MS6_P1, 90, 0, 8)
/* Register 91: Multisynth 7 Parameters */
__MREG__(MS7_P1, 91, 0, 8)
/* Register 92: Clock 6 and 7 Output Divider */
__MREG__(R7_DIV, 92, 4, 3)
__MREG__(R6_DIV, 92, 0, 3)
/* Registers 149-161: Spread Spectrum Parameters */
__MREG__(SSC_EN, 149, 7, 1)
__MREG__(SSDN_P2_TOP, 149, 0, 7)
__MREG__(SSDN_P2_BOT, 150, 0, 8)
__MREG__(SSC_MODE, 151, 7, 1)
__MREG__(SSDN_P3_TOP, 151, 0, 7)
__MREG__(SSDN_P3_BOT, 152, 0, 8)
__MREG__(SSDN_P1_BOT, 153, 0, 8)
__MREG__(SSUDP_TOP, 154, 4, 4)
__MREG__(SSDN_P1_TOP, 154, 0, 4)
__MREG__(SSUDP_BOT, 155, 0, 8)
__MREG__(SSUP_P2_TOP, 156, 0, 7)
__MREG__(SSUP_P2_BOT, 157, 0, 8)
__MREG__(SSUP_P3_TOP, 158, 0, 8)
__MREG__(SSUP_P3_BOT, 159, 0, 8)
__MREG__(SSUP_P1_BOT, 160, 0, 8)
__MREG__(SS_NCLK, 161, 4, 4)
__MREG__(SSUP_P1_TOP, 161, 0, 4)
/* Registers 162-163: VCXO Parameter */
__MREG__(VCXO_PARAM_BOT, 162, 0, 8)
__MREG__(VCXO_PARAM_MID, 163, 0, 8)
__MREG__(VCXO_PARAM_TOP, 164, 0, 6)
/* Registers 165-170: CLK0-CLK5 Initial Phase Offsets */
__MREG__(CLK0_PHOFF, 165, 0, 7)
__MREG__(CLK1_PHOFF, 166, 0, 7)
__MREG__(CLK2_PHOFF, 167, 0, 7)
__MREG__(CLK3_PHOFF, 168, 0, 7)
__MREG__(CLK4_PHOFF, 169, 0, 7)
__MREG__(CLK5_PHOFF, 170, 0, 7)
/* Register 177: PLL Reset */
__MREG__(PLLB_RST, 177, 7, 1)
__MREG__(PLLA_RST, 177, 5, 1)
/* Register 183: Crystal Internal Load Capacitance */
__MREG__(XTAL_CL, 183, 6, 2)
/* Register 187: Fanout Enable */
__MREG__(CLKIN_FANOUT_EN, 187, 7, 1)
__MREG__(XO_FANOUT_EN, 187, 6, 1)
__MREG__(MS_FANOUT_EN, 187, 4, 1)
/* 3-part parameters */
__3PART__(MSNA_P1)
__3PART__(MSNA_P2)
__3PART__(MSNA_P3)
__3PART__(MSNB_P1)
__3PART__(MSNB_P2)
__3PART__(MSNB_P3)
__3PART__(MS0_P1)
__3PART__(MS0_P2)
__3PART__(MS0_P3)
__3PART__(MS1_P1)
__3PART__(MS1_P2)
__3PART__(MS1_P3)
__3PART__(MS2_P1)
__3PART__(MS2_P2)
__3PART__(MS2_P3)
__3PART__(MS3_P1)
__3PART__(MS3_P2)
__3PART__(MS3_P3)
__3PART__(MS4_P1)
__3PART__(MS4_P2)
__3PART__(MS4_P3)
__3PART__(MS5_P1)
__3PART__(MS5_P2)
__3PART__(MS5_P3)
/* 2-part parameters */
__2PART__(SSDN_P1)
__2PART__(SSDN_P2)
__2PART__(SSDN_P3)
__2PART__(SSUP_P1)
__2PART__(SSUP_P2)
__2PART__(SSUP_P3)
__2PART__(SSUDP)
/* Per-PLL register groups */
__PER_PLL__(LOL, LOL_, )
__PER_PLL__(LOL_STKY, LOL_, _STKY)
__PER_PLL__(LOL_MASK, LOL_, _MASK)
__PER_PLL__(PLL_SRC, PLL, _SRC)
__PER_PLL__(MSN_P1, MSN, _P1)
__PER_PLL__(MSN_P2, MSN, _P2)
__PER_PLL__(MSN_P3, MSN, _P3)
__PER_PLL__(PLL_RST, PLL, _RST)
/* Per-multisynth register groups */
__PER_MS__(CLK_OEB, CLK, _OEB)
__PER_MS__(OEB_CLK, OEB_CLK, )
__PER_MS__(OEB_MASK, OEB_MASK, )
__PER_MS__(CLK_PDN, CLK, _PDN)
__PER_MS__(MS_SRC, MS, _SRC)
__PER_MS__(CLK_INV, CLK, _INV)
__PER_MS__(CLK_SRC, CLK, _SRC)
__PER_MS__(CLK_IDRV, CLK, _IDRV)
__PER_MS__(CLK_DIS_STATE, CLK, _DIS_STATE)
__PER_MS__(R_DIV, R, _DIV)
__PER_MS__(MS_P1, MS, _P1)
__PER_FRAC_MS__(MS_INT, MS, _INT)
__PER_FRAC_MS__(MS_P2, MS, _P2)
__PER_FRAC_MS__(MS_P3, MS, _P3)
__PER_FRAC_MS__(MS_DIVBY4, MS, _DIVBY4)
__PER_FRAC_MS__(CLK_PHOFF, CLK, _PHOFF)

View file

@ -24,36 +24,23 @@
void spi_bus_start(spi_bus_t* const bus, const void* const config)
{
bus->config = config;
bus->start(bus, config);
}
void spi_bus_stop(spi_bus_t* const bus)
{
bus->stop(bus);
bus->config = NULL;
}
void spi_bus_transfer(
spi_bus_t* const bus,
const void* const config,
void* const data,
const size_t count)
void spi_bus_transfer(spi_bus_t* const bus, void* const data, const size_t count)
{
if (config != bus->config) {
spi_bus_start(bus, config);
}
bus->transfer(bus, data, count);
}
void spi_bus_transfer_gather(
spi_bus_t* const bus,
const void* const config,
const spi_transfer_t* const transfers,
const size_t count)
{
if (config != bus->config) {
spi_bus_start(bus, config);
}
bus->transfer_gather(bus, transfers, count);
}

Some files were not shown because too many files have changed in this diff Show more