diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index eaa1d982..21593d8a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,70 +17,127 @@ jobs:
host:
strategy:
matrix:
- 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']
+ os: ['macos', 'ubuntu', 'windows']
+ cmake: ['3.10.0', '3.21.7', '4.2.6', 'latest']
+ compiler: ['gcc', 'msvc']
exclude:
- # GitHub runners use Visual Studio 2022. Support added in CMake 3.21.
- - sys: {os: 'windows', shell: 'pwsh'}
+ # 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'
cmake: '3.10.0'
- - 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'
+ # The in-between CMake versions aren't so intersting with GCC.
+ - compiler: 'gcc'
+ cmake: '3.21.7'
+ - compiler: 'gcc'
+ cmake: '4.2.6'
include:
- - sys: {os: 'windows', shell: 'pwsh'}
+ # 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'
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
- - sys: {os: 'windows', shell: 'msys2'}
+ - os: 'windows'
+ compiler: 'gcc'
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.sys.os }}-latest
+ runs-on: ${{ matrix.os }}-latest
defaults:
run:
- shell: '${{ matrix.sys.shell }} {0}'
+ shell: '${{ matrix.shell }} {0}'
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Setup cmake
- uses: jwlawson/actions-setup-cmake@v2
+ uses: lukka/get-cmake@latest
with:
- cmake-version: ${{ matrix.cmake }}
- if: matrix.sys.shell != 'msys2'
+ 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'
- name: Install dependencies (macOS)
run: brew install fftw
- if: matrix.sys.os == 'macos'
+ if: matrix.os == 'macos'
- name: Install dependencies (Ubuntu)
run: |
sudo apt update
sudo apt install libfftw3-dev libusb-1.0-0-dev
- if: matrix.sys.os == 'ubuntu'
+ if: matrix.os == 'ubuntu'
- name: Install dependencies (Windows)
run: vcpkg install --triplet=x64-windows libusb fftw3 pthreads pkgconf
- if: matrix.sys.os == 'windows' && matrix.sys.shell != 'msys2'
+ if: matrix.os == 'windows' && matrix.shell != 'msys2'
- name: Setup MSYS (Windows)
- if: matrix.sys.os == 'windows' && matrix.sys.shell == 'msys2'
+ if: matrix.os == 'windows' && matrix.shell == 'msys2'
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
@@ -98,7 +155,7 @@ jobs:
run: |
cmake -E make_directory host/build
cd host/build
- cmake .. -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}}
+ cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}}
cmake --build . --config Release
# Build libhackrf ONLY
@@ -107,22 +164,12 @@ jobs:
run: |
cmake -E make_directory host/libhackrf/build
cd host/libhackrf/build
- cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
+ cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake --build . --config Release
- name: Install (libhackrf)
run: |
- 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'
+ ${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/libhackrf/build ${{ matrix.install_args }} --config Release
# Build hackrf-tools ONLY
@@ -130,22 +177,12 @@ jobs:
run: |
cmake -E make_directory host/hackrf-tools/build
cd host/hackrf-tools/build
- cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
+ cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake --build . --config Release
- name: Install (hackrf-tools)
run: |
- 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'
+ ${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/hackrf-tools/build ${{ matrix.install_args }}
# Publish the contents of install/bin (which should be the combination libhackrf and host-tools) for Windows
- name: Publish Artifacts (Windows)
@@ -153,31 +190,40 @@ jobs:
with:
name: hackrf-tools-windows
path: ${{github.workspace}}/install/bin
- if: matrix.sys.os == 'windows' && matrix.cmake == 'latest' && matrix.sys.shell == 'pwsh'
+ if: matrix.os == 'windows' && matrix.cmake == 'latest' && matrix.shell == 'pwsh'
firmware:
strategy:
matrix:
os: ['macos', 'ubuntu', 'windows']
- board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE']
- cmake: ['3.10.0', 'latest']
+ board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE', 'UNIVERSAL']
+ cmake: ['3.12.0', 'latest']
exclude:
- os: 'windows'
- cmake: '3.10.0'
+ cmake: '3.12.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.
+ - os: 'macos'
+ board: 'HACKRF_ONE'
+ - os: 'macos'
+ board: 'JAWBREAKER'
+ - os: 'macos'
+ board: 'RAD1O'
# Don't cancel all builds when one fails
fail-fast: false
runs-on: ${{ matrix.os }}-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: true
- name: Setup cmake
- uses: jwlawson/actions-setup-cmake@v2
+ uses: lukka/get-cmake@latest
with:
- cmake-version: ${{ matrix.cmake }}
+ cmakeVersion: ${{ matrix.cmake }}
if: matrix.os != 'windows'
- name: Install Arm GNU Toolchain
diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml
index 0bf33429..d498d609 100644
--- a/.github/workflows/clang-format-check.yml
+++ b/.github/workflows/clang-format-check.yml
@@ -16,7 +16,7 @@ jobs:
- check: 'firmware/hackrf_usb'
exclude: ''
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Run clang-format-action
uses: jidicula/clang-format-action@v4.6.2
with:
diff --git a/.github/workflows/includes-check.yml b/.github/workflows/includes-check.yml
new file mode 100644
index 00000000..f16800fe
--- /dev/null
+++ b/.github/workflows/includes-check.yml
@@ -0,0 +1,96 @@
+name: Check includes
+on: [push, pull_request]
+
+defaults:
+ run:
+ shell: bash
+
+jobs:
+ includes-check:
+ runs-on: ubuntu-latest
+ container:
+ image: "debian:13"
+ options: --user 0
+ strategy:
+ matrix:
+ board: ['HACKRF_ONE', 'JAWBREAKER', 'RAD1O', 'PRALINE', 'UNIVERSAL']
+ cmake: ['3.12.0', 'latest']
+
+ # Don't cancel all builds when one fails
+ fail-fast: false
+
+ steps:
+
+ - name: Make it work on debian
+ run: |
+ apt update
+ apt install -y git python3 python3-pip python3-venv nodejs unzip
+ # actions/checkout insists on putting the checkout in the
+ # working directory rather than ${{ github.workspace }}.
+ #
+ # This may just be because the 'runner' user does not exist
+ # in the docker image at startup.
+ #
+ # also see: https://github.com/actions/runner/issues/878
+ useradd -ms /bin/bash runner
+ ln -s /__w /home/runner/work
+
+ - name: Checkout repository
+ uses: actions/checkout@v6
+ with:
+ submodules: true
+
+ - name: Setup cmake
+ uses: lukka/get-cmake@latest
+ with:
+ cmakeVersion: ${{ matrix.cmake }}
+
+ - name: Install Arm GNU Toolchain
+ uses: carlosperate/arm-none-eabi-gcc-action@v1
+
+ - name: Install dependencies
+ run: |
+ python3 -m venv environment && source environment/bin/activate
+ python3 -m pip install PyYAML
+ apt install -y iwyu
+ iwyu --version
+
+ - name: Build libopencm3
+ working-directory: ${{github.workspace}}/firmware/libopencm3/
+ run: |
+ source ../../environment/bin/activate
+ make
+
+ - name: Create Build Environment
+ run: cmake -E make_directory ${{github.workspace}}/firmware/build
+
+ - name: Configure CMake
+ working-directory: ${{github.workspace}}/firmware/build
+ run: cmake ${{github.workspace}}/firmware/ -DCMAKE_BUILD_TYPE=Release -DBOARD=${{ matrix.board }} -DCHECK_INCLUDES=1
+
+ - name: Build
+ working-directory: ${{github.workspace}}/firmware/build
+ run: |
+ source ../../environment/bin/activate
+ output="$(cmake --build . --config Release 2>&1)"
+ while IFS= read -r line
+ do
+ if [[ "${line}" == "Warning: include-what-you-use"* ]]; then
+ exit_code=1
+ dump=1
+ echo
+ elif [[ "${line}" == "---" ]]; then
+ dump=0
+ echo
+ fi
+ if [[ ${dump} == "1" ]]; then
+ echo "${line}"
+ fi
+ done <<< ${output}
+ if [[ ${exit_code} == "1" ]]; then
+ echo "Includes check failed for board target: ${{ matrix.board }}"
+ else
+ echo "Includes check succeeded for board target: ${{ matrix.board }}"
+ fi
+ echo $(include-what-you-use --version)
+ exit ${exit_code}
diff --git a/Jenkinsfile b/Jenkinsfile
index 2ee5b9e5..dcfd9ad8 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,275 +1,123 @@
import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException
+def docker_args = '''--group-add=20 --group-add=46 --device-cgroup-rule="c 189:* rmw" \
+ --device-cgroup-rule="c 166:* rmw" -v /dev/bus/usb:/dev/bus/usb \
+ -v /tmp/req_pipe:/tmp/req_pipe -v /tmp/res_pipe:/tmp/res_pipe'''
+
+def h1_test = '''python3 ci-scripts/hackrf_test.py --ci --log log \
+ --hostdir host/build/hackrf-tools/src/ \
+ --fwupdate firmware/hackrf_usb/build/ \
+ --tester 0000000000000000325866e629a25623 \
+ --eut RunningFromRAM --unattended --rev r4'''
+
+def hpro_test = '''python3 ci-scripts/hackrf_pro_test.py --ci --log log \
+ --hostdir host/build/hackrf-tools/src \
+ --fwupdate firmware/hackrf_usb/build \
+ --tester 0000000000000000a06063c82338145f \
+ --eut RunningFromRAM -p --rev r1.2'''
+
pipeline {
agent any
stages {
stage('Build Docker Image') {
+ options {
+ timeout(time: 20, unit: 'MINUTES')
+ }
steps {
sh 'docker build -t hackrf https://github.com/greatscottgadgets/hackrf.git'
}
}
- stage('Test HackRF One') {
+ stage('Test HackRF One with BOARD=HACKRF_ONE') {
agent {
docker {
image 'hackrf'
reuseNode true
- args '''--group-add=20 --group-add=46 --device-cgroup-rule="c 189:* rmw" \
- --device-cgroup-rule="c 166:* rmw" -v /dev/bus/usb:/dev/bus/usb \
- -v /tmp/req_pipe:/tmp/req_pipe -v /tmp/res_pipe:/tmp/res_pipe'''
+ args docker_args
}
}
+ options {
+ timeout(time: 20, unit: 'MINUTES')
+ }
steps {
- sh './ci-scripts/install_host.sh'
- sh './ci-scripts/build_h1_firmware.sh'
- script {
- try {
- // Allow 20 seconds for the USB hub port power server to respond
- timeout(time: 20, unit: 'SECONDS') {
- sh 'hubs all off'
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "USB hub port power server command timeout reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
- }
- }
- retry(3) {
+ 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 {
- try {
- // Allow 20 seconds for the USB hub port power server to respond
- timeout(time: 20, unit: 'SECONDS') {
- sh 'hubs h1_eut reset'
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "USB hub port power server command timeout reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
- }
+ 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 'sleep 1s'
- sh './ci-scripts/test_host.sh'
- }
- retry(3) {
- script {
- try {
- // Allow 20 seconds for the USB hub port power server to respond
- timeout(time: 20, unit: 'SECONDS') {
- sh 'hubs h1_tester h1_eut reset'
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "USB hub port power server command timeout reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
- }
- }
- sh 'sleep 1s'
- script {
- try {
- // Allow 5 minutes for the test to run
- timeout(time: 5, unit: 'MINUTES') {
- sh '''python3 ci-scripts/hackrf_test.py --ci --log log \
- --hostdir host/build/hackrf-tools/src/ \
- --fwupdate firmware/hackrf_usb/build/ \
- --tester 0000000000000000325866e629a25623 \
- --eut RunningFromRAM --unattended --rev r4'''
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "HackRF One Test timeout limit reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
- }
- }
- script {
- try {
- // Allow 20 seconds for the USB hub port power server to respond
- timeout(time: 20, unit: 'SECONDS') {
- sh 'hubs all off'
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "USB hub port power server command timeout reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
- }
- }
- }
- retry(3) {
- script {
- try {
- // Allow 20 seconds for the USB hub port power server to respond
- timeout(time: 20, unit: 'SECONDS') {
- sh 'hubs h1_eut reset'
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "USB hub port power server command timeout reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
- }
- }
- sh 'sleep 1s'
- sh 'python3 ci-scripts/test_sgpio_debug.py'
}
}
}
- stage('Test HackRF Pro') {
+ stage('Test HackRF One with BOARD=UNIVERSAL') {
agent {
docker {
image 'hackrf'
reuseNode true
- args '''--group-add=20 --group-add=46 --device-cgroup-rule="c 189:* rmw" \
- --device-cgroup-rule="c 166:* rmw" -v /dev/bus/usb:/dev/bus/usb \
- -v /tmp/req_pipe:/tmp/req_pipe -v /tmp/res_pipe:/tmp/res_pipe'''
+ args docker_args
}
}
+ options {
+ timeout(time: 20, unit: 'MINUTES')
+ }
steps {
- sh './ci-scripts/install_host.sh'
- sh './ci-scripts/build_hpro_firmware.sh'
- script {
- try {
- // Allow 20 seconds for the USB hub port power server to respond
- timeout(time: 20, unit: 'SECONDS') {
- sh 'hubs all off'
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "USB hub port power server command timeout reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
+ 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')
}
}
- retry(3) {
- script {
- try {
- // Allow 20 seconds for the USB hub port power server to respond
- timeout(time: 20, unit: 'SECONDS') {
- sh 'hubs hpro_eut reset'
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "USB hub port power server command timeout reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
- }
- }
- sh 'sleep 1s'
- sh './ci-scripts/test_host.sh'
+
+ }
+ }
+ stage('Test HackRF Pro with BOARD=PRALINE') {
+ agent {
+ docker {
+ image 'hackrf'
+ reuseNode true
+ args "$docker_args"
}
- retry(3) {
+ }
+ options {
+ 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 {
- try {
- // Allow 10 seconds for the USB hub port power server to respond
- timeout(time: 20, unit: 'SECONDS') {
- sh 'hubs hpro_tester hpro_eut reset'
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "USB hub port power server command timeout reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
- }
+ allOff()
+ runTest("Check Host", 'hpro_eut', './ci-scripts/test_host.sh')
+ runTest("HackRF Pro HIL Test", 'hpro_tester hpro_eut', hpro_test)
}
- sh 'sleep 1s'
+ }
+ }
+ }
+ stage('Test HackRF Pro with BOARD=UNIVERSAL') {
+ agent {
+ docker {
+ image 'hackrf'
+ reuseNode true
+ args "$docker_args"
+ }
+ }
+ options {
+ 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 {
- try {
- // Allow 5 minutes for the test to run
- timeout(time: 5, unit: 'MINUTES') {
- sh '''python3 ci-scripts/hackrf_pro_test.py --ci --log log \
- --hostdir host/build/hackrf-tools/src \
- --fwupdate firmware/hackrf_usb/build \
- --tester 0000000000000000a06063c82338145f \
- --eut RunningFromRAM -p --rev r1.2'''
- }
- } catch (FlowInterruptedException err) {
- // Check if the cause was specifically an exceeded timeout
- def cause = err.getCauses().get(0)
- if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
- echo "HackRF Pro Test timeout limit reached."
- throw err // Re-throw the exception to fail the build
- } else {
- echo "Build interrupted for another reason."
- throw err // Re-throw the exception to fail the build
- }
- } catch (Exception err) {
- echo "An unrelated error occurred: ${err.getMessage()}"
- throw err
- }
+ allOff()
+ runTest("Check Host", 'hpro_eut', './ci-scripts/test_host.sh')
+ runTest("HackRF Pro HIL Test", 'hpro_tester hpro_eut', hpro_test)
}
}
}
@@ -284,3 +132,46 @@ 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')
+}
+
+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')
+}
+
+def runCommand(title, cmd, retries, time, unit) {
+ retry(retries) {
+ try {
+ timeout(time: time, unit: unit) {
+ sh "${cmd}"
+ }
+ } catch (FlowInterruptedException err) {
+ // Check if the cause was specifically an exceeded timeout
+ def cause = err.getCauses().get(0)
+ if (cause instanceof org.jenkinsci.plugins.workflow.steps.TimeoutStepExecution.ExceededTimeout) {
+ echo "${title} timeout reached."
+ throw err // Re-throw the exception to fail the build
+ } else {
+ echo "Build interrupted for another reason."
+ throw err // Re-throw the exception to fail the build
+ }
+ } catch (Exception err) {
+ echo "An unrelated error occurred: ${err.getMessage()}"
+ throw err
+ }
+ }
+}
+
+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')
+ }
+}
diff --git a/ci-scripts/build_hpro_firmware.sh b/ci-scripts/build_firmware.sh
similarity index 80%
rename from ci-scripts/build_hpro_firmware.sh
rename to ci-scripts/build_firmware.sh
index 830d4bf7..b6fc4ec4 100755
--- a/ci-scripts/build_hpro_firmware.sh
+++ b/ci-scripts/build_firmware.sh
@@ -4,6 +4,6 @@ git submodule init
git submodule update
cd firmware/hackrf_usb
rm -rf build
-cmake -DBOARD=PRALINE -B build
+cmake -DBOARD=$1 -B build
cmake --build build
cd ../..
diff --git a/ci-scripts/build_h1_firmware.sh b/ci-scripts/build_h1_firmware.sh
deleted file mode 100755
index 762d64de..00000000
--- a/ci-scripts/build_h1_firmware.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-set -e
-git submodule init
-git submodule update
-cd firmware/hackrf_usb
-rm -rf build
-cmake -DBOARD=HACKRF_ONE -B build
-cmake --build build
-cd ../..
diff --git a/ci-scripts/hackrf_pro_test.py b/ci-scripts/hackrf_pro_test.py
index 1e9ac9f4..44b6eb56 100644
--- a/ci-scripts/hackrf_pro_test.py
+++ b/ci-scripts/hackrf_pro_test.py
@@ -663,9 +663,6 @@ 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)
@@ -1413,11 +1410,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:
@@ -1433,7 +1430,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(0.1)
+ time.sleep(1)
if not device_found:
fail(75)
diff --git a/docs/images/gateware-basic-block-diagram.png b/docs/images/gateware-basic-block-diagram.png
new file mode 100644
index 00000000..58f99e7c
Binary files /dev/null and b/docs/images/gateware-basic-block-diagram.png differ
diff --git a/docs/images/gateware-basic-block-diagram.svg b/docs/images/gateware-basic-block-diagram.svg
new file mode 100644
index 00000000..ae6d5cf4
--- /dev/null
+++ b/docs/images/gateware-basic-block-diagram.svg
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/docs/images/gateware-ext-prec-rx-dsp-chain.png b/docs/images/gateware-ext-prec-rx-dsp-chain.png
new file mode 100644
index 00000000..6dc48330
Binary files /dev/null and b/docs/images/gateware-ext-prec-rx-dsp-chain.png differ
diff --git a/docs/images/gateware-ext-prec-rx-dsp-chain.svg b/docs/images/gateware-ext-prec-rx-dsp-chain.svg
new file mode 100644
index 00000000..af05bf82
--- /dev/null
+++ b/docs/images/gateware-ext-prec-rx-dsp-chain.svg
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/docs/images/gateware-ext-prec-tx-dsp-chain.png b/docs/images/gateware-ext-prec-tx-dsp-chain.png
new file mode 100644
index 00000000..d0a97190
Binary files /dev/null and b/docs/images/gateware-ext-prec-tx-dsp-chain.png differ
diff --git a/docs/images/gateware-ext-prec-tx-dsp-chain.svg b/docs/images/gateware-ext-prec-tx-dsp-chain.svg
new file mode 100644
index 00000000..707ef6c3
--- /dev/null
+++ b/docs/images/gateware-ext-prec-tx-dsp-chain.svg
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/docs/images/gateware-halfprec-dsp-chain.png b/docs/images/gateware-halfprec-dsp-chain.png
new file mode 100644
index 00000000..a0b6c5ed
Binary files /dev/null and b/docs/images/gateware-halfprec-dsp-chain.png differ
diff --git a/docs/images/gateware-halfprec-dsp-chain.svg b/docs/images/gateware-halfprec-dsp-chain.svg
new file mode 100644
index 00000000..fa0aa1e7
--- /dev/null
+++ b/docs/images/gateware-halfprec-dsp-chain.svg
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/docs/images/gateware-rx-only-basic-block-diagram.png b/docs/images/gateware-rx-only-basic-block-diagram.png
new file mode 100644
index 00000000..421ae2c7
Binary files /dev/null and b/docs/images/gateware-rx-only-basic-block-diagram.png differ
diff --git a/docs/images/gateware-rx-only-basic-block-diagram.svg b/docs/images/gateware-rx-only-basic-block-diagram.svg
new file mode 100644
index 00000000..bf470a17
--- /dev/null
+++ b/docs/images/gateware-rx-only-basic-block-diagram.svg
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/docs/images/gateware-standard-dsp-chain.png b/docs/images/gateware-standard-dsp-chain.png
new file mode 100644
index 00000000..69973a94
Binary files /dev/null and b/docs/images/gateware-standard-dsp-chain.png differ
diff --git a/docs/images/gateware-standard-dsp-chain.svg b/docs/images/gateware-standard-dsp-chain.svg
new file mode 100644
index 00000000..4290646a
--- /dev/null
+++ b/docs/images/gateware-standard-dsp-chain.svg
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/docs/images/gateware-tx-only-basic-block-diagram.png b/docs/images/gateware-tx-only-basic-block-diagram.png
new file mode 100644
index 00000000..fbd1639c
Binary files /dev/null and b/docs/images/gateware-tx-only-basic-block-diagram.png differ
diff --git a/docs/images/gateware-tx-only-basic-block-diagram.svg b/docs/images/gateware-tx-only-basic-block-diagram.svg
new file mode 100644
index 00000000..61aea1e2
--- /dev/null
+++ b/docs/images/gateware-tx-only-basic-block-diagram.svg
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/docs/source/external_clock_interface.rst b/docs/source/external_clock_interface.rst
index fc3f3b1d..9c09ca66 100644
--- a/docs/source/external_clock_interface.rst
+++ b/docs/source/external_clock_interface.rst
@@ -9,12 +9,18 @@ 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 CLKOUT. 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 the CLKOUT SMA port. The signal is a 3.3 V, 10 MHz square wave intended for a high impedance load.
-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.
+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.
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.
diff --git a/docs/source/gateware.rst b/docs/source/gateware.rst
new file mode 100644
index 00000000..f560b583
--- /dev/null
+++ b/docs/source/gateware.rst
@@ -0,0 +1,118 @@
+================================================
+Gateware
+================================================
+
+One of the significant hardware changes in :ref:`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 `__, 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 `__ 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)
+
diff --git a/docs/source/hackrf_pro.rst b/docs/source/hackrf_pro.rst
index 18e4a151..e47dbb0b 100644
--- a/docs/source/hackrf_pro.rst
+++ b/docs/source/hackrf_pro.rst
@@ -2,6 +2,8 @@
HackRF Pro
================================================
+.. _hackrf_pro:
+
.. image:: ../images/hackrf-pro-preliminary-photo.jpg
:alt: HackRF Pro
diff --git a/docs/source/index.rst b/docs/source/index.rst
index ce845e66..401fbb3b 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -57,6 +57,13 @@ Welcome to HackRF's documentation!
setting_gain
virtual_machines
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Gateware
+
+ gateware
+
.. toctree::
:maxdepth: 2
:caption: Opera Cake Documentation
diff --git a/docs/source/software_support.rst b/docs/source/software_support.rst
index e8487652..66594e6b 100644
--- a/docs/source/software_support.rst
+++ b/docs/source/software_support.rst
@@ -43,6 +43,11 @@ Software That Has Direct Support For HackRF
* `https://xakcop.com/aprs-sdr `__
+* SigDigger (Windows/Linux/macOS)
+
+ * `https://github.com/BatchDrake/SigDigger `__
+ * Supports HackRF through SoapySDR / SoapyHackRF
+
Software That Can Use Data From HackRF
@@ -74,4 +79,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.
\ No newline at end of file
+It is also strongly suggested, and usually required, that your HackRF Tools and HackRF firmware match.
diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt
index 10c2d09d..844ae708 100644
--- a/firmware/CMakeLists.txt
+++ b/firmware/CMakeLists.txt
@@ -20,10 +20,17 @@
# Top directory CMake project for HackRF firmware
-cmake_minimum_required(VERSION 3.10.0)
+option(CHECK_INCLUDES
+ "Check firmware sources for unused includes and transitive dependencies. (Requires iwyu)" OFF)
+
+cmake_minimum_required(VERSION 3.12.0)
set(CMAKE_TOOLCHAIN_FILE toolchain-arm-cortex-m.cmake)
project (hackrf_firmware_all C)
+if(CHECK_INCLUDES)
+ include(include-what-you-use.cmake)
+endif()
+
add_subdirectory(blinky)
add_subdirectory(hackrf_usb)
diff --git a/firmware/blinky/blinky.c b/firmware/blinky/blinky.c
index 6d071c97..4bd11106 100644
--- a/firmware/blinky/blinky.c
+++ b/firmware/blinky/blinky.c
@@ -19,14 +19,16 @@
* Boston, MA 02110-1301, USA.
*/
-#include "hackrf_core.h"
-#include "platform_detect.h"
#include "delay.h"
+#include "leds.h"
+#include "pins.h"
+#include "platform_detect.h"
+#include "power.h"
int main(void)
{
detect_hardware_platform();
- pin_setup();
+ pins_setup();
#ifndef PRALINE
/* enable 1V8 power supply so that the 1V8 LED lights up */
@@ -37,19 +39,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(2000000);
-
+ delay_ms(150);
+
led_off(LED1);
led_off(LED2);
led_off(LED3);
-
- delay(2000000);
+
+ delay_ms(150);
}
return 0;
diff --git a/firmware/common/LPC4320_M4_memory.ld b/firmware/common/LPC4320_M4_memory.ld
index a5997f32..48be94da 100644
--- a/firmware/common/LPC4320_M4_memory.ld
+++ b/firmware/common/LPC4320_M4_memory.ld
@@ -26,7 +26,8 @@ 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 = 96K
+ ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 64K
+ ram_usb (rw) : ORIGIN = 0x10010000, LENGTH = 32K
ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 32K
ram_sleep (rwx) : ORIGIN = 0x10088000, LENGTH = 8K
}
diff --git a/firmware/common/LPC4330_M4_memory.ld b/firmware/common/LPC4330_M4_memory.ld
index 1e886d3a..1d42ebee 100644
--- a/firmware/common/LPC4330_M4_memory.ld
+++ b/firmware/common/LPC4330_M4_memory.ld
@@ -26,7 +26,8 @@ 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 = 128K
+ ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 96K
+ ram_usb(rw) : ORIGIN = 0x10018000, LENGTH = 32K
ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 64K
ram_sleep (rwx) : ORIGIN = 0x10090000, LENGTH = 8K
}
diff --git a/firmware/common/LPC43xx_M4_memory.ld b/firmware/common/LPC43xx_M4_memory.ld
index 933fa854..fc7dcee7 100644
--- a/firmware/common/LPC43xx_M4_memory.ld
+++ b/firmware/common/LPC43xx_M4_memory.ld
@@ -35,11 +35,12 @@ MEMORY
* to get performance benefit of having two USB buffers addressable
* simultaneously (on two different buses of the AHB multilayer matrix)
*/
- ram_usb (rwx) : ORIGIN = 0x20008000, LENGTH = 32K
+ ram_samp (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));
diff --git a/firmware/common/adc.h b/firmware/common/adc.h
index 94481678..b74125af 100644
--- a/firmware/common/adc.h
+++ b/firmware/common/adc.h
@@ -19,12 +19,9 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __ADC_H__
-#define __ADC_H__
+#pragma once
#include
uint16_t adc_read(uint8_t pin);
void adc_off(void);
-
-#endif // __ADC_H__
diff --git a/firmware/common/bitband.h b/firmware/common/bitband.h
index dcf7aefb..8a0afed5 100644
--- a/firmware/common/bitband.h
+++ b/firmware/common/bitband.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __BITBAND_H__
-#define __BITBAND_H__
+#pragma once
#include
@@ -37,5 +36,3 @@ void peripheral_bitband_clear(
uint32_t peripheral_bitband_get(
volatile void* const peripheral_address,
const uint_fast8_t bit_number);
-
-#endif //__BITBAND_H__
diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c
new file mode 100644
index 00000000..a709960b
--- /dev/null
+++ b/firmware/common/clock_gen.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright 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.
+ */
+
+#include "clock_gen.h"
+
+#include
+
+#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;
+}
diff --git a/firmware/common/clock_gen.h b/firmware/common/clock_gen.h
new file mode 100644
index 00000000..fa1d3da0
--- /dev/null
+++ b/firmware/common/clock_gen.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#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
diff --git a/firmware/common/clkin.c b/firmware/common/clock_io.c
similarity index 76%
rename from firmware/common/clkin.c
rename to firmware/common/clock_io.c
index 9f63681c..d4d14af0 100644
--- a/firmware/common/clkin.c
+++ b/firmware/common/clock_io.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Great Scott Gadgets
+ * Copyright 2022-2026 Great Scott Gadgets
*
* This file is part of HackRF.
*
@@ -19,8 +19,9 @@
* Boston, MA 02110-1301, USA.
*/
-#include "clkin.h"
+#include "clock_io.h"
+#include
#include
#include
@@ -30,6 +31,15 @@
#include
#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)
@@ -114,4 +124,47 @@ 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
diff --git a/firmware/common/clock_io.h b/firmware/common/clock_io.h
new file mode 100644
index 00000000..4868b2d9
--- /dev/null
+++ b/firmware/common/clock_io.h
@@ -0,0 +1,60 @@
+/*
+ * 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
+#include
+
+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
diff --git a/firmware/common/cpld_jtag.c b/firmware/common/cpld_jtag.c
index 4ef9ce5d..ab998808 100644
--- a/firmware/common/cpld_jtag.c
+++ b/firmware/common/cpld_jtag.c
@@ -19,21 +19,52 @@
* Boston, MA 02110-1301, USA.
*/
-#if !defined(PRALINE)
- #include
-#endif
-
#include "cpld_jtag.h"
-#if !defined(PRALINE)
+
+#include "platform_detect.h"
+#include "platform_gpio.h"
+#ifdef IS_NOT_PRALINE
+ #include
+ #include
#include "xapp058/micro.h"
+ #include "cpld_xc2c.h"
#endif
-#ifndef PRALINE
+#ifdef IS_NOT_PRALINE
static refill_buffer_cb refill_buffer;
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;
@@ -41,25 +72,37 @@ void cpld_jtag_take(jtag_t* const jtag)
/* Set initial GPIO state to the voltages of the internal or external pull-ups/downs,
* to avoid any glitches.
*/
-#if (defined HACKRF_ONE || defined PRALINE)
- gpio_set(gpio->gpio_pp_tms);
-#endif
- gpio_clear(gpio->gpio_tck);
-#ifndef PRALINE
- gpio_set(gpio->gpio_tms);
- gpio_set(gpio->gpio_tdi);
+#ifdef IS_EXPANSION_COMPATIBLE
+ if (IS_EXPANSION_COMPATIBLE) {
+ gpio_set(gpio->gpio_pp_tms);
+ }
#endif
-#if (defined HACKRF_ONE || defined PRALINE)
- /* Do not drive PortaPack-specific TMS pin initially, just to be cautious. */
- gpio_input(gpio->gpio_pp_tms);
- gpio_input(gpio->gpio_pp_tdo);
+ gpio_clear(gpio->gpio_tck);
+
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ gpio_set(gpio->gpio_tms);
+ gpio_set(gpio->gpio_tdi);
+ }
#endif
+
+#ifdef IS_EXPANSION_COMPATIBLE
+ if (IS_EXPANSION_COMPATIBLE) {
+ /* Do not drive PortaPack-specific TMS pin initially, just to be cautious. */
+ gpio_input(gpio->gpio_pp_tms);
+ gpio_input(gpio->gpio_pp_tdo);
+ }
+#endif
+
gpio_output(gpio->gpio_tck);
-#ifndef PRALINE
- gpio_output(gpio->gpio_tms);
- gpio_output(gpio->gpio_tdi);
- gpio_input(gpio->gpio_tdo);
+
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ gpio_output(gpio->gpio_tms);
+ gpio_output(gpio->gpio_tdi);
+ gpio_input(gpio->gpio_tdo);
+ }
#endif
}
@@ -70,20 +113,26 @@ void cpld_jtag_release(jtag_t* const jtag)
/* Make all pins inputs when JTAG interface not active.
* Let the pull-ups/downs do the work.
*/
-#if (defined HACKRF_ONE || defined PRALINE)
- /* Do not drive PortaPack-specific pins, initially, just to be cautious. */
- gpio_input(gpio->gpio_pp_tms);
- gpio_input(gpio->gpio_pp_tdo);
+#ifdef IS_EXPANSION_COMPATIBLE
+ if (IS_EXPANSION_COMPATIBLE) {
+ /* Do not drive PortaPack-specific pins, initially, just to be cautious. */
+ gpio_input(gpio->gpio_pp_tms);
+ gpio_input(gpio->gpio_pp_tdo);
+ }
#endif
+
gpio_input(gpio->gpio_tck);
-#ifndef PRALINE
- gpio_input(gpio->gpio_tms);
- gpio_input(gpio->gpio_tdi);
- gpio_input(gpio->gpio_tdo);
+
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ gpio_input(gpio->gpio_tms);
+ gpio_input(gpio->gpio_tdi);
+ gpio_input(gpio->gpio_tdo);
+ }
#endif
}
-#ifndef PRALINE
+#ifdef IS_NOT_PRALINE
/* return 0 if success else return error code see xsvfExecute() */
int cpld_jtag_program(
jtag_t* const jtag,
@@ -114,4 +163,16 @@ 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
diff --git a/firmware/common/cpld_jtag.h b/firmware/common/cpld_jtag.h
index 9219dbcb..39cfc1e7 100644
--- a/firmware/common/cpld_jtag.h
+++ b/firmware/common/cpld_jtag.h
@@ -19,32 +19,33 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __CPLD_JTAG_H__
-#define __CPLD_JTAG_H__
+#pragma once
+#include
#include
#include "gpio.h"
-typedef struct jtag_gpio_t {
+typedef struct {
gpio_t gpio_tck;
-#ifndef PRALINE
+#ifdef IS_NOT_PRALINE
gpio_t gpio_tms;
gpio_t gpio_tdi;
gpio_t gpio_tdo;
#endif
-#if (defined HACKRF_ONE || defined PRALINE)
+#ifdef IS_EXPANSION_COMPATIBLE
gpio_t gpio_pp_tms;
gpio_t gpio_pp_tdo;
#endif
} jtag_gpio_t;
-typedef struct jtag_t {
+typedef struct {
jtag_gpio_t* const gpio;
} jtag_t;
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);
@@ -59,5 +60,8 @@ 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);
-#endif //__CPLD_JTAG_H__
+/* Driver instance. */
+extern jtag_gpio_t jtag_gpio_cpld;
+extern jtag_t jtag_cpld;
diff --git a/firmware/common/cpld_xc2c.h b/firmware/common/cpld_xc2c.h
index 26ac8fa4..c48dae31 100644
--- a/firmware/common/cpld_xc2c.h
+++ b/firmware/common/cpld_xc2c.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __CPLD_XC2C_H__
-#define __CPLD_XC2C_H__
+#pragma once
#include
#include
@@ -69,5 +68,3 @@ bool cpld_xc2c64a_jtag_sram_verify(
extern const cpld_xc2c64a_program_t cpld_hackrf_program_sram;
extern const cpld_xc2c64a_verify_t cpld_hackrf_verify;
extern const cpld_xc2c64a_row_addresses_t cpld_hackrf_row_addresses;
-
-#endif /*__CPLD_XC2C_H__*/
diff --git a/firmware/common/cpu_clock.c b/firmware/common/cpu_clock.c
new file mode 100644
index 00000000..32fd012c
--- /dev/null
+++ b/firmware/common/cpu_clock.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright 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.
+ */
+
+#include "cpu_clock.h"
+
+#include
+
+#include
+#if defined(IS_JAWBREAKER) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
+ #include
+#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
+}
diff --git a/firmware/common/cpu_clock.h b/firmware/common/cpu_clock.h
new file mode 100644
index 00000000..04929643
--- /dev/null
+++ b/firmware/common/cpu_clock.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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
+
+#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
diff --git a/firmware/common/crc.h b/firmware/common/crc.h
index f8acf5fc..ee33d7df 100644
--- a/firmware/common/crc.h
+++ b/firmware/common/crc.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __CRC_H__
-#define __CRC_H__
+#pragma once
#include
#include
@@ -35,5 +34,3 @@ typedef struct {
void crc32_init(crc32_t* const crc);
void crc32_update(crc32_t* const crc, const uint8_t* const data, const size_t byte_count);
uint32_t crc32_digest(const crc32_t* const crc);
-
-#endif //__CRC_H__
diff --git a/firmware/common/da7219.c b/firmware/common/da7219.c
index 376e04a5..de85d2b8 100644
--- a/firmware/common/da7219.c
+++ b/firmware/common/da7219.c
@@ -22,8 +22,8 @@
#include
#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
diff --git a/firmware/common/da7219.h b/firmware/common/da7219.h
index f0289240..c81ece20 100644
--- a/firmware/common/da7219.h
+++ b/firmware/common/da7219.h
@@ -19,13 +19,10 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __DA7219_H
-#define __DA7219_H
+#pragma once
#include
#define DA7219_ADDRESS 0x1A
bool da7219_detect(void);
-
-#endif /* __DA7219_H */
diff --git a/firmware/common/delay.c b/firmware/common/delay.c
index ceb7693e..b74389b2 100644
--- a/firmware/common/delay.c
+++ b/firmware/common/delay.c
@@ -21,16 +21,9 @@
#include "delay.h"
-void delay(uint32_t duration)
-{
- uint32_t i;
+#include "cpu_clock.h"
- for (i = 0; i < duration; i++) {
- __asm__("nop");
- }
-}
-
-void delay_us_at_mhz(uint32_t us, uint32_t mhz)
+static void delay_us_at_mhz(uint32_t us, uint32_t mhz)
{
#if defined(LPC43XX_M4)
// The loop below takes 3 cycles per iteration.
@@ -52,3 +45,13 @@ 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);
+}
diff --git a/firmware/common/delay.h b/firmware/common/delay.h
index 123141ba..eed806bb 100644
--- a/firmware/common/delay.h
+++ b/firmware/common/delay.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __DELAY_H
-#define __DELAY_H
+#pragma once
#ifdef __cplusplus
extern "C" {
@@ -28,7 +27,9 @@ extern "C" {
#include
-void delay(uint32_t duration);
-void delay_us_at_mhz(uint32_t us, uint32_t mhz);
+void delay_us(uint32_t us);
+void delay_ms(uint32_t ms);
-#endif /* __DELAY_H */
+#ifdef __cplusplus
+}
+#endif
diff --git a/firmware/common/fault_handler.h b/firmware/common/fault_handler.h
index efccea24..0b499acb 100644
--- a/firmware/common/fault_handler.h
+++ b/firmware/common/fault_handler.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __FAULT_HANDLER__
-#define __FAULT_HANDLER__
+#pragma once
#include
@@ -30,9 +29,7 @@
// TODO: Move all this to a Cortex-M(?) include file, since these
// structures are supposedly the same between processors (to an
// undetermined extent).
-typedef struct armv7m_scb_t armv7m_scb_t;
-
-struct armv7m_scb_t {
+typedef struct {
volatile const uint32_t CPUID;
volatile uint32_t ICSR;
volatile uint32_t VTOR;
@@ -64,12 +61,10 @@ struct armv7m_scb_t {
volatile const uint32_t ID_ISAR4;
volatile const uint32_t __reserved_0x74_0x87[5];
volatile uint32_t CPACR;
-} __attribute__((packed));
+} __attribute__((packed)) armv7m_scb_t;
static armv7m_scb_t* const SCB = (armv7m_scb_t*) SCB_BASE;
#define SCB_HFSR_DEBUGEVT (1 << 31)
#define SCB_HFSR_FORCED (1 << 30)
#define SCB_HFSR_VECTTBL (1 << 1)
-
-#endif //__FAULT_HANDLER__
diff --git a/firmware/common/firmware_info.c b/firmware/common/firmware_info.c
index 1ac02074..527dd19b 100644
--- a/firmware/common/firmware_info.c
+++ b/firmware/common/firmware_info.c
@@ -30,6 +30,9 @@
#define SUPPORTED_PLATFORM PLATFORM_RAD1O
#elif PRALINE
#define SUPPORTED_PLATFORM PLATFORM_PRALINE
+#elif UNIVERSAL
+ #define SUPPORTED_PLATFORM \
+ (PLATFORM_PRALINE | PLATFORM_HACKRF1_OG | PLATFORM_HACKRF1_R9)
#else
#define SUPPORTED_PLATFORM 0
#endif
diff --git a/firmware/common/firmware_info.h b/firmware/common/firmware_info.h
index fb087008..a7ae09f6 100644
--- a/firmware/common/firmware_info.h
+++ b/firmware/common/firmware_info.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef FIRMWARE_INFO_H
-#define FIRMWARE_INFO_H
+#pragma once
#include
@@ -33,5 +32,3 @@ struct firmware_info_t {
} __attribute__((packed, aligned(1)));
extern const struct firmware_info_t firmware_info;
-
-#endif
\ No newline at end of file
diff --git a/firmware/common/fixed_point.h b/firmware/common/fixed_point.h
index 500d2e54..974e34d2 100644
--- a/firmware/common/fixed_point.h
+++ b/firmware/common/fixed_point.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __FIXED_POINT_H__
-#define __FIXED_POINT_H__
+#pragma once
#include
@@ -30,7 +29,45 @@ typedef uint64_t fp_40_24_t;
/* one million in 40.24 fixed-point */
#define FP_ONE_MHZ ((1000ULL * 1000ULL) << 24)
+/* one thousand in 40.24 fixed-point */
+#define FP_ONE_KHZ ((1000ULL) << 24)
+
/* one in 40.24 fixed-point */
#define FP_ONE_HZ (1 << 24)
-#endif /*__FIXED_POINT_H__*/
+#define FP_MHZ(mhz) (mhz##ULL * FP_ONE_MHZ)
+#define FP_KHZ(khz) (khz##ULL * FP_ONE_KHZ)
+#define FP_HZ(hz) (hz##ULL * FP_ONE_HZ)
+
+/* 28.36 fixed-point */
+typedef uint64_t fp_28_36_t;
+
+/* one million in 28.36 fixed-point */
+#define SR_FP_ONE_MHZ ((1000ULL * 1000ULL) << 36)
+
+/* one thousand in 28.36 fixed-point */
+#define SR_FP_ONE_KHZ (1000ULL << 36)
+
+/* one in 28.36 fixed-point */
+#define SR_FP_ONE_HZ (1ULL << 36)
+
+#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))
diff --git a/firmware/common/fpga.c b/firmware/common/fpga.c
index 9c4d55f2..ec4ff2ec 100644
--- a/firmware/common/fpga.c
+++ b/firmware/common/fpga.c
@@ -20,13 +20,17 @@
*/
#include "fpga.h"
+#include "fpga_regs.def"
#include
-#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)
{
@@ -52,9 +56,7 @@ 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;
}
@@ -62,9 +64,7 @@ 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);
}
diff --git a/firmware/common/fpga.h b/firmware/common/fpga.h
index bf78cf08..1765fcdd 100644
--- a/firmware/common/fpga.h
+++ b/firmware/common/fpga.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __FPGA_H
-#define __FPGA_H
+#pragma once
#include
#include
@@ -37,14 +36,11 @@ typedef enum {
FPGA_QUARTER_SHIFT_MODE_DOWN = 0b01,
} fpga_quarter_shift_mode_t;
-struct fpga_driver_t; // IWYU pragma: keep - fixed in #1704
-typedef struct fpga_driver_t fpga_driver_t;
-
-struct fpga_driver_t {
+typedef struct {
ice40_spi_driver_t* bus;
uint8_t regs[FPGA_NUM_REGS];
uint8_t regs_dirty;
-};
+} fpga_driver_t;
struct fpga_loader_t {
/* Start address added as an offset to all read() calls. */
@@ -95,4 +91,5 @@ bool fpga_spi_selftest(void);
bool fpga_sgpio_selftest(void);
bool fpga_if_xcvr_selftest(void);
-#endif // __FPGA_H
+/* Driver instance. */
+extern fpga_driver_t fpga;
diff --git a/firmware/common/fpga_image.c b/firmware/common/fpga_image.c
index dc88556b..6b440337 100644
--- a/firmware/common/fpga_image.c
+++ b/firmware/common/fpga_image.c
@@ -24,7 +24,6 @@
#include
#include "fpga.h"
-#include "hackrf_core.h"
#include "ice40_spi.h"
#include "lz4_blk.h"
#include "selftest.h"
@@ -86,7 +85,6 @@ 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,
@@ -96,7 +94,6 @@ 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;
diff --git a/firmware/common/fpga_regs.def b/firmware/common/fpga_regs.def
index 5964657b..0a8ce828 100644
--- a/firmware/common/fpga_regs.def
+++ b/firmware/common/fpga_regs.def
@@ -1,7 +1,7 @@
/* -*- mode: c -*-
*
* Copyright 2012 Michael Ossmann
- * Copyright 2025 Great Scott Gadgets
+ * Copyright 2025-2026 Great Scott Gadgets
*
* This file is part of HackRF.
*
@@ -21,8 +21,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __FPGA_REGS_DEF
-#define __FPGA_REGS_DEF
+#pragma once
#define FPGA_REG_SET_CLEAN(_d, _r) (_d->regs_dirty &= ~(1UL<<_r))
#define FPGA_REG_SET_DIRTY(_d, _r) (_d->regs_dirty |= (1UL<<_r))
@@ -71,5 +70,3 @@ __MREG__(FPGA_STANDARD_TX_INTRP, 5, 0, 3)
/* REG 06 (6): TX_PSTEP */
__MREG__(FPGA_STANDARD_TX_PSTEP, 6, 0, 8)
-
-#endif // __FPGA_REGS_DEF
diff --git a/firmware/common/fpga_selftest.c b/firmware/common/fpga_selftest.c
index cc1d8d9c..58fc7bb4 100644
--- a/firmware/common/fpga_selftest.c
+++ b/firmware/common/fpga_selftest.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Great Scott Gadgets
+ * Copyright 2025-2026 Great Scott Gadgets
*
* This file is part of HackRF.
*
@@ -23,10 +23,10 @@
#include
#include
+#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_BULK_BUFFER_SIZE 0x8000
-extern uint8_t usb_bulk_buffer[USB_BULK_BUFFER_SIZE];
+#define USB_SAMP_BUFFER_SIZE 0x8000
+extern uint8_t usb_samp_buffer[USB_SAMP_BUFFER_SIZE];
static int rx_samples(const unsigned int num_samples, uint32_t max_cycles)
{
@@ -71,10 +71,8 @@ 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;
@@ -114,10 +112,10 @@ bool fpga_sgpio_selftest(void)
fpga_set_prbs_enable(&fpga, false);
// Generate sequence from first value and compare.
- bool seq_in_sync = (usb_bulk_buffer[0] != 0);
- uint8_t seq = lfsr_advance(usb_bulk_buffer[0]);
+ bool seq_in_sync = (usb_samp_buffer[0] != 0);
+ uint8_t seq = lfsr_advance(usb_samp_buffer[0]);
for (int i = 1; i < 512; ++i) {
- if (usb_bulk_buffer[i] != seq) {
+ if (usb_samp_buffer[i] != seq) {
seq_in_sync = false;
break;
}
@@ -188,7 +186,7 @@ bool fpga_if_xcvr_selftest(void)
return false;
}
- const size_t num_samples = USB_BULK_BUFFER_SIZE / 2;
+ const size_t num_samples = USB_SAMP_BUFFER_SIZE / 2;
// Set common RX path and gateware settings for the measurements.
fpga_set_tx_nco_pstep(&fpga, 64); // NCO phase increment
@@ -196,56 +194,56 @@ bool fpga_if_xcvr_selftest(void)
rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_RX_CALIBRATION);
max283x_set_lna_gain(&max283x, 16);
max283x_set_vga_gain(&max283x, 36);
- max283x_set_frequency(&max283x, 2500000000);
+ max283x_set_frequency(&max283x, FP_MHZ(2500), true);
// Capture 1: 4 Msps, tone at 0.5 MHz, narrowband filter OFF
- sample_rate_set(4ULL * FP_ONE_MHZ, true);
- delay_us_at_mhz(1000, 204);
+ sample_rate_set(SR_FP_MHZ(4), true);
+ delay_ms(1);
if (rx_samples(num_samples, 2000000) == -1) {
timeout = true;
}
measure_tone(
- (int8_t*) usb_bulk_buffer,
+ (int8_t*) usb_samp_buffer,
num_samples,
&selftest.xcvr_measurements[0]);
// Capture 2: 4 Msps, tone at 0.5 MHz, narrowband filter ON
narrowband_filter_set(1);
- delay_us_at_mhz(1000, 204);
+ delay_ms(1);
if (rx_samples(num_samples, 2000000) == -1) {
timeout = true;
}
measure_tone(
- (int8_t*) usb_bulk_buffer,
+ (int8_t*) usb_samp_buffer,
num_samples,
&selftest.xcvr_measurements[1]);
// Capture 3: 20 Msps, tone at 5 MHz, narrowband filter OFF
fpga_set_tx_nco_pstep(&fpga, 255);
- sample_rate_set(20ULL * FP_ONE_MHZ, true);
+ sample_rate_set(SR_FP_MHZ(20), true);
narrowband_filter_set(0);
- delay_us_at_mhz(1000, 204);
+ delay_ms(1);
if (rx_samples(num_samples, 2000000) == -1) {
timeout = true;
}
measure_tone(
- (int8_t*) usb_bulk_buffer,
+ (int8_t*) usb_samp_buffer,
num_samples,
&selftest.xcvr_measurements[2]);
// Capture 4: 20 Msps, tone at 5 MHz, narrowband filter ON
narrowband_filter_set(1);
- delay_us_at_mhz(1000, 204);
+ delay_ms(1);
if (rx_samples(num_samples, 2000000) == -1) {
timeout = true;
}
measure_tone(
- (int8_t*) usb_bulk_buffer,
+ (int8_t*) usb_samp_buffer,
num_samples,
&selftest.xcvr_measurements[3]);
// Restore default settings.
- sample_rate_set(10ULL * FP_ONE_MHZ, true);
+ sample_rate_set(SR_FP_MHZ(10), true);
rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_OFF);
narrowband_filter_set(0);
fpga_init(&fpga);
diff --git a/firmware/common/gpdma.h b/firmware/common/gpdma.h
index 053f83a8..dc469da9 100644
--- a/firmware/common/gpdma.h
+++ b/firmware/common/gpdma.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __GPDMA_H__
-#define __GPDMA_H__
+#pragma once
#include
#include
@@ -40,5 +39,3 @@ void gpdma_lli_enable_interrupt(gpdma_lli_t* const lli);
void gpdma_lli_create_loop(gpdma_lli_t* const lli, const size_t lli_count);
void gpdma_lli_create_oneshot(gpdma_lli_t* const lli, const size_t lli_count);
-
-#endif /*__GPDMA_H__*/
diff --git a/firmware/common/gpio.h b/firmware/common/gpio.h
index 387b91a3..749fa021 100644
--- a/firmware/common/gpio.h
+++ b/firmware/common/gpio.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __GPIO_H__
-#define __GPIO_H__
+#pragma once
#include
@@ -35,5 +34,3 @@ void gpio_output(gpio_t gpio);
void gpio_input(gpio_t gpio);
void gpio_write(gpio_t gpio, const bool value);
bool gpio_read(gpio_t gpio);
-
-#endif /*__GPIO_H__*/
diff --git a/firmware/common/gpio_lpc.h b/firmware/common/gpio_lpc.h
index f018e43c..0987537f 100644
--- a/firmware/common/gpio_lpc.h
+++ b/firmware/common/gpio_lpc.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __GPIO_LPC_H__
-#define __GPIO_LPC_H__
+#pragma once
#include
@@ -30,7 +29,7 @@
* register #defines and API declarations into separate header files.
*/
-typedef struct gpio_port {
+typedef struct {
volatile uint32_t dir; /* +0x000 */
uint32_t _reserved0[31];
volatile uint32_t mask; /* +0x080 */
@@ -69,5 +68,3 @@ struct gpio {
.gpio_w = GPIO_LPC_W(_port_num, _pin_num), \
}
// clang-format on
-
-#endif /*__GPIO_LPC_H__*/
diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c
deleted file mode 100644
index 241c2e3d..00000000
--- a/firmware/common/hackrf_core.c
+++ /dev/null
@@ -1,1159 +0,0 @@
-/*
- * Copyright 2012-2026 Great Scott Gadgets
- * Copyright 2012 Jared Boone
- * Copyright 2013 Benjamin Vernoux
- *
- * 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
-#include
-#include
-#include
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
- #include
-#endif
-
-#include "delay.h"
-#include "gpio.h"
-#include "hackrf_core.h"
-#include "hackrf_ui.h"
-#include "i2c_lpc.h"
-#include "max283x.h"
-#include "max5864_target.h"
-#include "platform_detect.h"
-#include "platform_gpio.h"
-#include "platform_scu.h"
-#include "spi_bus.h"
-#include "w25q80bv_target.h"
-#if defined(HACKRF_ONE) || defined(PRALINE)
- #include "portapack.h"
-#endif
-#if defined(PRALINE)
- #include "ice40_spi.h"
-#endif
-
-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_si5351c_slow_clock = {
-// .duty_cycle_count = 15,
-// };
-
-const i2c_lpc_config_t i2c_config_si5351c_fast_clock = {
- .duty_cycle_count = 255,
-};
-
-si5351c_driver_t clock_gen = {
- .bus = &i2c0,
- .i2c_address = 0x60,
-};
-
-static 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 = {};
-
-static 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,
-};
-
-spi_bus_t spi_bus_ssp1 = {
- .obj = (void*) SSP1_BASE,
- .config = &ssp_config_max5864,
- .start = spi_ssp_start,
- .stop = spi_ssp_stop,
- .transfer = spi_ssp_transfer,
- .transfer_gather = spi_ssp_transfer_gather,
-};
-
-max5864_driver_t max5864 = {
- .bus = &spi_bus_ssp1,
- .target_init = max5864_target_init,
-};
-
-ssp_config_t ssp_config_w25q80bv = {
- .data_bits = SSP_DATA_8BITS,
- .serial_clock_rate = 2,
- .clock_prescale_rate = 2,
-};
-
-spi_bus_t spi_bus_ssp0 = {
- .obj = (void*) SSP0_BASE,
- .config = &ssp_config_w25q80bv,
- .start = spi_ssp_start,
- .stop = spi_ssp_stop,
- .transfer = spi_ssp_transfer,
- .transfer_gather = spi_ssp_transfer_gather,
-};
-
-w25q80bv_driver_t spi_flash = {
- .bus = &spi_bus_ssp0,
- .target_init = w25q80bv_target_init,
-};
-
-sgpio_config_t sgpio_config = {
- .slice_mode_multislice = true,
-};
-
-#ifdef PRALINE
-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,
-};
-
-fpga_driver_t fpga = {
- .bus = &ice40,
-};
-#endif
-
-radio_t radio = {
- .sample_rate_cb = sample_rate_set,
-};
-
-rf_path_t rf_path = {
- .switchctrl = 0,
-};
-
-jtag_gpio_t jtag_gpio_cpld = {};
-
-jtag_t jtag_cpld = {
- .gpio = &jtag_gpio_cpld,
-};
-
-/*
- * Configure clock generator to produce sample clock in units of 1/(2**24) 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_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program)
-{
- const fp_40_24_t vco = 800 * FP_ONE_MHZ;
- uint64_t p1, p2, p3;
- uint64_t n, d, q;
- fp_40_24_t remainder, 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_40_24_t rate = sample_rate * 2;
-
- p1 = ((128 * vco) / rate) - 512;
- if (vco % rate) {
- /*
- * Compute numerator (p2) for denominator (p3) matching
- * fixed-point type.
- */
- n = (128 * vco) - (rate * (p1 + 512));
- d = rate / FP_ONE_HZ;
- n += (d / 2);
- p2 = n / d;
- p3 = 1 << 24;
-
- /* Reduce fraction. p3 is 1<<24, so gcd(p2, p3) is a power of 2 */
- unsigned int shift = p2 ? __builtin_ctz(p2) : 24;
- p2 >>= shift;
- p3 >>= shift;
-
- /* Convert fraction to valid denominator. */
- const uint64_t p3_max = 0xfffff;
- if (p3 > p3_max) {
- p2 *= p3_max;
- p2 += (p3 / 2);
- p2 /= p3;
- p3 = p3_max;
- }
-
- /* 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 = (vco * 128);
- d = (p1 + 512);
- n += (d / 2);
- resultant_rate = n / d;
- } else {
- const uint64_t vco_hz = vco / FP_ONE_HZ;
- n = p3 * vco_hz * 128;
- d = p3 * (p1 + 512) + p2;
- const uint64_t rate_hz = n / d;
- remainder = (n - (d * rate_hz)) * FP_ONE_HZ;
- remainder += (d / 2);
- q = remainder / d;
- resultant_rate = (rate_hz * FP_ONE_HZ) + q;
- }
-
- /* 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);
- }
-
-#ifndef PRALINE
- /* Integer mode can be enabled if p1 is even and p2 is zero. */
- if (p1 & 0x1 || p2) {
- si5351c_set_int_mode(&clock_gen, 0, 0);
- } else {
- si5351c_set_int_mode(&clock_gen, 0, 1);
- }
-
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- /*
- * On HackRF One r9 all sample clocks are externally derived
- * from MS1/CLK1 operating at twice the sample rate.
- */
- si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0);
- } else {
- /*
- * 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(&clock_gen, 0, p1, p2, p3, 1);
-
- /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */
- si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter
-
- /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */
- si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter
- }
-#else
- /* MS0/CLK0 is the source for the MAX5864 (AFE_CLK). */
- si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1);
-
- /* MS1/CLK1 is the source for the FPGA (FPGA_CLK and SCT_CLK). */
- si5351c_configure_multisynth(&clock_gen, 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(&clock_gen, 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(&clock_gen, 2, p1, p2, p3, 1);
- si5351c_set_phase(&clock_gen, 2, phase_offset);
- }
-
- /* Reset PLL to synchronize output clock phase. */
- si5351c_reset_pll(&clock_gen, SI5351C_PLL_A);
-#endif
-
- if (streaming) {
- sgpio_cpld_stream_enable(&sgpio_config);
- }
-
- return resultant_rate;
-}
-
-/*
-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;
-
- /* 2. Enable the crystal oscillator. */
- CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK;
-
- /* 3. Wait 250us. */
- delay_us_at_mhz(250, 12);
-
- /* 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;
-
- /* 9. Wait 50us. */
- delay_us_at_mhz(50, 102);
-
- /* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */
- CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK;
-}
-
-/* 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(clock_gen.bus, &i2c_config_si5351c_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);
-
-#if (defined JAWBREAKER || defined HACKRF_ONE || defined PRALINE)
- /* 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
-}
-
-void clock_gen_init(void)
-{
- i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock);
-
- si5351c_init(&clock_gen);
- si5351c_disable_all_outputs(&clock_gen);
- si5351c_disable_oeb_pin_control(&clock_gen);
- si5351c_power_down_all_clocks(&clock_gen);
- si5351c_set_crystal_configuration(&clock_gen);
- si5351c_enable_xo_and_ms_fanout(&clock_gen);
-
- /*
- * 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
- */
-
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */
- si5351c_configure_multisynth(
- &clock_gen,
- 0,
- 20 * 128 - 512,
- 0,
- 1,
- 0); /* 800/20 = 40MHz */
- } else {
- /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */
- si5351c_configure_multisynth(
- &clock_gen,
- 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(
- &clock_gen,
- 5,
- 20 * 128 - 512,
- 0,
- 1,
- 0); /* 800/20 = 40MHz */
- }
-
- /* MS6/CLK6 is unused. */
- /* MS7/CLK7 is unused. */
-
- /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */
- sample_rate_set(10ULL * FP_ONE_MHZ, true);
-
- si5351c_configure_clock_control(&clock_gen);
- si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL);
- // soft reset
- si5351c_reset_pll(&clock_gen, SI5351C_PLL_BOTH);
- si5351c_enable_clock_outputs(&clock_gen);
-}
-
-void clock_gen_shutdown(void)
-{
- i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock);
- si5351c_disable_all_outputs(&clock_gen);
- si5351c_disable_oeb_pin_control(&clock_gen);
- si5351c_power_down_all_clocks(&clock_gen);
-}
-
-clock_source_t activate_best_clock_source(void)
-{
-#if (defined HACKRF_ONE || defined PRALINE)
- /* Ensure PortaPack reference oscillator is off while checking for external clock input. */
- if (portapack_reference_oscillator && portapack()) {
- portapack_reference_oscillator(false);
- }
-#endif
-
- clock_source_t source = CLOCK_SOURCE_HACKRF;
-
- /* Check for external clock input. */
- if (si5351c_clkin_signal_valid(&clock_gen)) {
- source = CLOCK_SOURCE_EXTERNAL;
- } else {
-#if (defined HACKRF_ONE || defined PRALINE)
- /* Enable PortaPack reference oscillator (if present), and check for valid clock. */
- if (portapack_reference_oscillator && portapack()) {
- portapack_reference_oscillator(true);
- delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */
- if (si5351c_clkin_signal_valid(&clock_gen)) {
- source = CLOCK_SOURCE_PORTAPACK;
- } else {
- portapack_reference_oscillator(false);
- }
- }
-#endif
- /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */
- }
-
- si5351c_set_clock_source(
- &clock_gen,
- (source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN);
- hackrf_ui()->set_clock_source(source);
-
- return source;
-}
-
-void ssp1_set_mode_max283x(void)
-{
- spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x);
-}
-
-void ssp1_set_mode_max5864(void)
-{
- spi_bus_start(max5864.bus, &ssp_config_max5864);
-}
-
-#ifdef PRALINE
-void ssp1_set_mode_ice40(void)
-{
- spi_bus_start(&spi_bus_ssp1, &ssp_config_ice40_fpga);
-}
-#endif
-
-void pin_shutdown(void)
-{
- const platform_gpio_t* gpio = platform_gpio();
- const platform_scu_t* scu = platform_scu();
-
- /* Configure all GPIO as Input (safe state) */
- gpio_init();
-
- /* 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.
- */
-#if (defined HACKRF_ONE || defined PRALINE)
- 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);
-#ifndef 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 RAD1O
- scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4);
-#endif
-#ifdef PRALINE
- scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
-#endif
-
- /* Configure USB indicators */
-#ifdef JAWBREAKER
- scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3);
- scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3);
-#endif
-
-#ifdef 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);
-#else
- disable_1v8_power();
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- #ifdef HACKRF_ONE
- gpio_output(gpio->h1r9_1v8_enable);
- scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- #endif
- } else {
- gpio_output(gpio->gpio_1v8_enable);
- scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- }
-#endif
-
-#if (defined HACKRF_ONE || defined PRALINE)
- /* Safe state: start with VAA turned off: */
- disable_rf_power();
-
- /* Configure RF power supply (VAA) switch control signal as output */
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- #ifdef HACKRF_ONE
- gpio_output(gpio->h1r9_vaa_disable);
- #endif
- } else {
- gpio_output(gpio->vaa_disable);
- }
-#endif
-
-#ifdef 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 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->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);
-
- p2_ctrl_set(P2_SIGNAL_CLK3);
- p1_ctrl_set(P1_SIGNAL_CLKIN);
- narrowband_filter_set(0);
- clkin_ctrl_set(CLKIN_SIGNAL_P22);
-
- 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_output(gpio->aa_en);
- 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);
-#endif
-
- /* enable input on SCL and SDA pins */
- SCU_SFSI2C0 = SCU_I2C0_NOMINAL;
-}
-
-/* Run after pin_shutdown() and prior to enabling power supplies. */
-void pin_setup(void)
-{
- const platform_gpio_t* gpio = platform_gpio();
- const platform_scu_t* scu = platform_scu();
-
- led_off(0);
- led_off(1);
- led_off(2);
-#ifdef RAD1O
- led_off(3);
-#endif
-
- gpio_output(gpio->led[0]);
- gpio_output(gpio->led[1]);
- gpio_output(gpio->led[2]);
-#if (defined RAD1O || defined PRALINE)
- gpio_output(gpio->led[3]);
-#endif
-
- /* Configure drivers and driver pins */
- ssp_config_max283x.gpio_select = gpio->max283x_select;
-#if defined(PRALINE)
- ssp_config_max283x.data_bits = SSP_DATA_9BITS; // send 2 words
-#else
- ssp_config_max283x.data_bits = SSP_DATA_16BITS;
-#endif
-
- ssp_config_max5864.gpio_select = gpio->max5864_select;
-
- ssp_config_w25q80bv.gpio_select = gpio->w25q80bv_select;
- spi_flash.gpio_hold = gpio->w25q80bv_hold;
- spi_flash.gpio_wp = gpio->w25q80bv_wp;
-
- sgpio_config.gpio_q_invert = gpio->q_invert;
-#if !defined(PRALINE)
- sgpio_config.gpio_trigger_enable = gpio->trigger_enable;
-#endif
-
-#if defined(PRALINE)
- 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;
-#endif
-
- jtag_gpio_cpld.gpio_tck = gpio->cpld_tck;
-#if defined(HACKRF_ONE) || defined(RAD1O)
- 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
-#if defined(HACKRF_ONE) || defined(PRALINE)
- jtag_gpio_cpld.gpio_pp_tms = gpio->cpld_pp_tms;
- jtag_gpio_cpld.gpio_pp_tdo = gpio->cpld_pp_tdo;
-#endif
-
- ssp1_set_mode_max283x();
-
- mixer_bus_setup(&mixer);
-
-#if defined(HACKRF_ONE)
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- rf_path.gpio_rx = gpio->h1r9_rx;
- sgpio_config.gpio_trigger_enable = gpio->h1r9_trigger_enable;
- }
-#endif
-
- /* Configure RF path */
-#if defined(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,
- };
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- rf_path.gpio_rx = gpio->h1r9_rx;
- rf_path.gpio_h1r9_no_ant_pwr = gpio->h1r9_no_ant_pwr;
- }
-#elif defined(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,
- };
-#elif defined(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,
- };
- board_rev_t rev = detected_revision();
- if ((rev == BOARD_REV_PRALINE_R1_0) || (rev == BOARD_REV_GSG_PRALINE_R1_0)) {
- rf_path.gpio_mix_en_n = gpio->mix_en_n_r1_0;
- }
-#endif
- rf_path_pin_setup(&rf_path);
-
- /* Configure external clock in */
- scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1);
-
- sgpio_configure_pin_functions(&sgpio_config);
-}
-
-#ifdef PRALINE
-void enable_1v2_power(void)
-{
- gpio_set(platform_gpio()->gpio_1v2_enable);
-}
-
-void disable_1v2_power(void)
-{
- gpio_clear(platform_gpio()->gpio_1v2_enable);
-}
-
-void enable_3v3aux_power(void)
-{
- gpio_clear(platform_gpio()->gpio_3v3aux_enable_n);
-}
-
-void disable_3v3aux_power(void)
-{
- gpio_set(platform_gpio()->gpio_3v3aux_enable_n);
-}
-#else
-void enable_1v8_power(void)
-{
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- #ifdef HACKRF_ONE
- gpio_set(platform_gpio()->h1r9_1v8_enable);
- #endif
- } else {
- gpio_set(platform_gpio()->gpio_1v8_enable);
- }
-}
-
-void disable_1v8_power(void)
-{
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- #ifdef HACKRF_ONE
- gpio_clear(platform_gpio()->h1r9_1v8_enable);
- #endif
- } else {
- gpio_clear(platform_gpio()->gpio_1v8_enable);
- }
-}
-#endif
-
-#ifdef HACKRF_ONE
-void enable_rf_power(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);
- }
- }
-}
-
-void disable_rf_power(void)
-{
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- gpio_set(platform_gpio()->h1r9_vaa_disable);
- } else {
- gpio_set(platform_gpio()->vaa_disable);
- }
-}
-#endif
-
-#ifdef PRALINE
-void enable_rf_power(void)
-{
- gpio_clear(platform_gpio()->vaa_disable);
-
- /* Let the voltage stabilize */
- delay(1000000);
-}
-
-void disable_rf_power(void)
-{
- gpio_set(platform_gpio()->vaa_disable);
-}
-#endif
-
-#ifdef RAD1O
-void enable_rf_power(void)
-{
- gpio_set(platform_gpio()->vaa_enable);
-
- /* Let the voltage stabilize */
- delay(1000000);
-}
-
-void disable_rf_power(void)
-{
- gpio_clear(platform_gpio()->vaa_enable);
-}
-#endif
-
-#ifdef PRALINE
-void led_on(const led_t led)
-{
- gpio_clear(platform_gpio()->led[led]);
-}
-
-void led_off(const led_t led)
-{
- gpio_set(platform_gpio()->led[led]);
-}
-#else
-void led_on(const led_t led)
-{
- gpio_set(platform_gpio()->led[led]);
-}
-
-void led_off(const led_t led)
-{
- 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;
-#if (defined RAD1O || defined PRALINE)
- num_leds = 4;
-#endif
- for (int i = 0; i < num_leds; i++) {
-#ifdef PRALINE
- gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 0);
-#else
- gpio_write(platform_gpio()->led[i], ((state >> i) & 1) == 1);
-#endif
- }
-}
-
-void trigger_enable(const bool enable)
-{
-#ifndef PRALINE
- gpio_write(sgpio_config.gpio_trigger_enable, enable);
-#else
- fpga_set_trigger_enable(&fpga, enable);
-#endif
-}
-
-void halt_and_flash(const uint32_t duration)
-{
- /* blink LED1, LED2, and LED3 */
- while (1) {
- led_on(LED1);
- led_on(LED2);
- led_on(LED3);
- delay(duration);
- led_off(LED1);
- led_off(LED2);
- led_off(LED3);
- delay(duration);
- }
-}
-
-#ifdef PRALINE
-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 clkin_ctrl_set(const clkin_signal_t signal)
-{
- gpio_write(platform_gpio()->clkin_ctrl, signal & 1);
-}
-
-void pps_out_set(const uint8_t value)
-{
- gpio_write(platform_gpio()->pps_out, value & 1);
-}
-
-void narrowband_filter_set(const uint8_t value)
-{
- gpio_write(platform_gpio()->aa_en, value & 1);
-}
-#endif
diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h
deleted file mode 100644
index 74a171a1..00000000
--- a/firmware/common/hackrf_core.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright 2012-2022 Great Scott Gadgets
- * Copyright 2012 Benjamin Vernoux
- * Copyright 2012 Jared Boone
- *
- * 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.
- */
-
-#ifndef __HACKRF_CORE_H
-#define __HACKRF_CORE_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include
-#include
-
-#include "cpld_jtag.h"
-#include "fixed_point.h"
-#include "i2c_bus.h"
-#include "max283x.h"
-#include "max5864.h"
-#include "mixer.h"
-#include "radio.h"
-#include "rf_path.h"
-#include "sgpio.h"
-#include "si5351c.h"
-#include "spi_ssp.h"
-#include "w25q80bv.h"
-#if defined(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;
-
-#ifdef 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 PRALINE
-void ssp1_set_mode_ice40(void);
-#endif
-
-void pin_shutdown(void);
-void pin_setup(void);
-
-#ifdef PRALINE
-void enable_1v2_power(void);
-void disable_1v2_power(void);
-void enable_3v3aux_power(void);
-void disable_3v3aux_power(void);
-#else
-void enable_1v8_power(void);
-void disable_1v8_power(void);
-#endif
-
-fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program);
-
-clock_source_t activate_best_clock_source(void);
-
-#if (defined HACKRF_ONE || defined RAD1O || defined 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 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
-
-#endif /* __HACKRF_CORE_H */
diff --git a/firmware/common/hackrf_ui.c b/firmware/common/hackrf_ui.c
index dbf1cc24..ac243890 100644
--- a/firmware/common/hackrf_ui.c
+++ b/firmware/common/hackrf_ui.c
@@ -23,10 +23,11 @@
#include
#include "hackrf_ui.h"
-#if defined(PRALINE) || defined(HACKRF_ONE)
+#include "transceiver_mode.h"
+#ifdef IS_EXPANSION_COMPATIBLE
#include "ui_portapack.h"
#endif
-#if defined(RAD1O)
+#ifdef IS_RAD1O
#include "ui_rad1o.h"
#endif
@@ -81,14 +82,18 @@ const hackrf_ui_t* hackrf_ui(void)
{
/* Detect on first use. If no UI hardware is detected, use a stub function table. */
if (ui == NULL && ui_enabled) {
-#if (defined HACKRF_ONE || defined PRALINE)
- if (portapack_hackrf_ui_init) {
- ui = portapack_hackrf_ui_init();
+#ifdef IS_EXPANSION_COMPATIBLE
+ if (IS_EXPANSION_COMPATIBLE) {
+ if (portapack_hackrf_ui_init) {
+ ui = portapack_hackrf_ui_init();
+ }
}
#endif
-#ifdef RAD1O
- if (rad1o_ui_setup) {
- ui = rad1o_ui_setup();
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ if (rad1o_ui_setup) {
+ ui = rad1o_ui_setup();
+ }
}
#endif
}
diff --git a/firmware/common/hackrf_ui.h b/firmware/common/hackrf_ui.h
index 24d2fa8e..7b7d67e4 100644
--- a/firmware/common/hackrf_ui.h
+++ b/firmware/common/hackrf_ui.h
@@ -20,14 +20,14 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef HACKRF_UI_H
-#define HACKRF_UI_H
+#pragma once
#include
#include
-#include "radio.h"
+#include "clock_gen.h"
#include "rf_path.h"
+#include "transceiver_mode.h"
typedef void (*hackrf_ui_init_fn)(void);
typedef void (*hackrf_ui_deinit_fn)(void);
@@ -71,5 +71,3 @@ typedef struct {
*/
const hackrf_ui_t* hackrf_ui(void);
void hackrf_ui_set_enable(bool);
-
-#endif
diff --git a/firmware/common/i2c_bus.h b/firmware/common/i2c_bus.h
index 95431437..7519ba14 100644
--- a/firmware/common/i2c_bus.h
+++ b/firmware/common/i2c_bus.h
@@ -20,27 +20,23 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __I2C_BUS_H__
-#define __I2C_BUS_H__
+#pragma once
#include
#include
-struct i2c_bus_t; // IWYU pragma: keep - fixed in #1704
-typedef struct i2c_bus_t i2c_bus_t;
-
-struct i2c_bus_t {
+typedef struct _i2c_bus_t {
void* const obj;
- void (*start)(i2c_bus_t* const bus, const void* const config);
- void (*stop)(i2c_bus_t* const bus);
+ void (*start)(struct _i2c_bus_t* const bus, const void* const config);
+ void (*stop)(struct _i2c_bus_t* const bus);
void (*transfer)(
- i2c_bus_t* const bus,
+ struct _i2c_bus_t* const bus,
const uint_fast8_t peripheral_address,
const uint8_t* const tx,
const size_t tx_count,
uint8_t* const rx,
const size_t rx_count);
-};
+} i2c_bus_t;
void i2c_bus_start(i2c_bus_t* const bus, const void* const config);
void i2c_bus_stop(i2c_bus_t* const bus);
@@ -51,5 +47,3 @@ void i2c_bus_transfer(
const size_t tx_count,
uint8_t* const rx,
const size_t rx_count);
-
-#endif /*__I2C_BUS_H__*/
diff --git a/firmware/common/i2c_lpc.c b/firmware/common/i2c_lpc.c
index 05631f05..2c0fc5e2 100644
--- a/firmware/common/i2c_lpc.c
+++ b/firmware/common/i2c_lpc.c
@@ -23,6 +23,28 @@
#include "i2c_lpc.h"
#include
+#include
+
+#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 */
@@ -31,7 +53,9 @@ 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;
- i2c_init(port, config->duty_cycle_count);
+ const uint16_t duty_cycle_count =
+ (cpu_clock_mhz * (2000 / config->clock_khz)) / 4;
+ i2c_init(port, duty_cycle_count);
}
void i2c_lpc_stop(i2c_bus_t* const bus)
diff --git a/firmware/common/i2c_lpc.h b/firmware/common/i2c_lpc.h
index c4f3e8c3..2a2562ee 100644
--- a/firmware/common/i2c_lpc.h
+++ b/firmware/common/i2c_lpc.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __I2C_LPC_H__
-#define __I2C_LPC_H__
+#pragma once
#include
#include
@@ -29,8 +28,8 @@
#include "i2c_bus.h"
-typedef struct i2c_lpc_config_t {
- const uint16_t duty_cycle_count;
+typedef struct {
+ const uint16_t clock_khz;
} i2c_lpc_config_t;
void i2c_lpc_start(i2c_bus_t* const bus, const void* const config);
@@ -44,4 +43,7 @@ void i2c_lpc_transfer(
const size_t count_rx);
bool i2c_probe(i2c_bus_t* const bus, const uint_fast8_t device_address);
-#endif /*__I2C_LPC_H__*/
+/* Driver instances. */
+extern const i2c_lpc_config_t i2c_config_fast_clock;
+extern i2c_bus_t i2c0;
+extern i2c_bus_t i2c1;
diff --git a/firmware/common/ice40_spi.c b/firmware/common/ice40_spi.c
index c8364b8a..6c078941 100644
--- a/firmware/common/ice40_spi.c
+++ b/firmware/common/ice40_spi.c
@@ -26,12 +26,32 @@
#include
#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));
@@ -50,14 +70,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, value, 3);
+ spi_bus_transfer(drv->bus, &ssp_config_ice40_fpga, 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, value, 3);
+ spi_bus_transfer(drv->bus, &ssp_config_ice40_fpga, value, 3);
}
static void spi_ssp1_wait_for_tx_fifo_not_full(void)
@@ -90,19 +110,21 @@ 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_at_mhz(1, 204 / 4); // 250 ns.
+ delay_us(1);
// 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_at_mhz(1800, 204);
+ delay_us(1800);
// Set SPI_SS = 1, Send 8 dummy clocks.
gpio_set(drv->gpio_select);
diff --git a/firmware/common/ice40_spi.h b/firmware/common/ice40_spi.h
index 92ce6280..331814aa 100644
--- a/firmware/common/ice40_spi.h
+++ b/firmware/common/ice40_spi.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __ICE40_SPI_H
-#define __ICE40_SPI_H
+#pragma once
#include
#include
@@ -28,16 +27,14 @@
#include "gpio.h"
#include "spi_bus.h"
+#include "spi_ssp.h"
-struct ice40_spi_driver_t; // IWYU pragma: keep - fixed in #1704
-typedef struct ice40_spi_driver_t ice40_spi_driver_t;
-
-struct ice40_spi_driver_t {
+typedef struct {
spi_bus_t* const bus;
gpio_t gpio_select;
gpio_t gpio_creset;
gpio_t gpio_cdone;
-};
+} ice40_spi_driver_t;
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);
@@ -48,4 +45,6 @@ bool ice40_spi_syscfg_program(
size_t (*read_block_cb)(void* ctx),
void* read_ctx);
-#endif // __ICE40_SPI_H
+/* Driver instance. */
+extern ssp_config_t ssp_config_ice40_fpga;
+extern ice40_spi_driver_t ice40;
diff --git a/firmware/common/leds.c b/firmware/common/leds.c
new file mode 100644
index 00000000..e1fd507b
--- /dev/null
+++ b/firmware/common/leds.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 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.
+ */
+
+#include "leds.h"
+
+#include
+
+#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);
+ }
+}
diff --git a/firmware/common/leds.h b/firmware/common/leds.h
new file mode 100644
index 00000000..a552dca6
--- /dev/null
+++ b/firmware/common/leds.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 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
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+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
diff --git a/firmware/common/locking.h b/firmware/common/locking.h
index 5620f2f9..54ba7a70 100644
--- a/firmware/common/locking.h
+++ b/firmware/common/locking.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __LOCKING_H__
-#define __LOCKING_H__
+#pragma once
#include
@@ -51,5 +50,3 @@ static inline uint32_t store_exclusive(uint32_t val, volatile uint32_t* addr)
return 0;
}
#endif
-
-#endif /* __LOCKING_H__ */
diff --git a/firmware/common/lz4_blk.h b/firmware/common/lz4_blk.h
index 19c8207e..f4e25f7a 100644
--- a/firmware/common/lz4_blk.h
+++ b/firmware/common/lz4_blk.h
@@ -19,12 +19,9 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __LZ4_BLK_H
-#define __LZ4_BLK_H
+#pragma once
#include
#include
int lz4_blk_decompress(const uint8_t* src, uint8_t* dst, size_t length);
-
-#endif
\ No newline at end of file
diff --git a/firmware/common/lz4_buf.h b/firmware/common/lz4_buf.h
index b9dec589..ab073d43 100644
--- a/firmware/common/lz4_buf.h
+++ b/firmware/common/lz4_buf.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __LZ4_BUF_H
-#define __LZ4_BUF_H
+#pragma once
#include
@@ -31,5 +30,3 @@
extern uint8_t lz4_in_buf[LZ4_BUFFER_SIZE];
extern uint8_t lz4_out_buf[LZ4_BUFFER_SIZE];
-
-#endif /*__LZ4_BUF_H */
diff --git a/firmware/common/m0_state.h b/firmware/common/m0_state.h
index cd0391fc..b7c26e8b 100644
--- a/firmware/common/m0_state.h
+++ b/firmware/common/m0_state.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __M0_STATE_H__
-#define __M0_STATE_H__
+#pragma once
#include
@@ -60,5 +59,3 @@ enum m0_error {
extern volatile struct m0_state m0_state;
void m0_set_mode(enum m0_mode mode);
-
-#endif /*__M0_STATE_H__*/
diff --git a/firmware/common/max2831.c b/firmware/common/max2831.c
index efae9eb6..08e59a59 100644
--- a/firmware/common/max2831.c
+++ b/firmware/common/max2831.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Great Scott Gadgets
+ * Copyright 2025-2026 Great Scott Gadgets
*
* This file is part of HackRF.
*
@@ -27,14 +27,18 @@
* pirate commands to do the same thing.
*/
+#include "max2831.h"
+
+#include
#include
#include
-#include "max2831.h"
+#include "fixed_point.h"
#include "max2831_regs.def" // private register def macros
#include "selftest.h"
#include "adc.h"
#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
/* Default register values. */
static const uint16_t max2831_regs_default[MAX2831_NUM_REGS] = {
@@ -105,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, values, 2);
+ spi_bus_transfer(drv->bus, drv->config, values, 2);
}
uint16_t max2831_reg_read(max2831_driver_t* const drv, uint8_t r)
@@ -222,36 +226,40 @@ void max2831_stop(max2831_driver_t* const drv)
max2831_set_mode(drv, MAX2831_MODE_SHUTDOWN);
}
-void max2831_set_frequency(max2831_driver_t* const drv, uint32_t freq)
-{
- uint32_t div_frac;
- uint32_t div_int;
- uint32_t div_rem;
- uint32_t div_cmp;
- int i;
+/* Assume 40 MHz reference clock with R divider of 2. */
+#define PFD_FREQ_HZ (20ULL * (1000ULL * 1000ULL))
- /* ASSUME 40MHz PLL. Ratio = F*R/40,000,000. */
- /* TODO: fixed to R=2. Check if it's worth exploring R=1. */
- freq += (20000000 >> 21); /* round to nearest frequency */
- div_int = freq / 20000000;
- div_rem = freq % 20000000;
- div_frac = 0;
- div_cmp = 20000000;
- for (i = 0; i < 20; i++) {
- div_frac <<= 1;
- div_rem <<= 1;
- if (div_rem >= div_cmp) {
- div_frac |= 0x1;
- div_rem -= div_cmp;
- }
+#define MIN_FREQ FP_MHZ(2000)
+#define MAX_FREQ FP_MHZ(3000)
+
+fp_40_24_t max2831_set_frequency(
+ max2831_driver_t* const drv,
+ fp_40_24_t freq,
+ bool program)
+{
+ uint64_t div;
+
+ freq = MIN(freq, MAX_FREQ);
+ freq = MAX(freq, MIN_FREQ);
+
+ freq += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */
+ div = freq / PFD_FREQ_HZ;
+
+ /*
+ * Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional
+ * divider.
+ */
+ div = div >> 4;
+
+ if (program) {
+ //set_MAX2831_SYN_REF_DIV(drv, MAX2831_SYN_REF_DIV_2);
+ set_MAX2831_SYN_INT(drv, (div >> 20) & 0xff);
+ set_MAX2831_SYN_FRAC_HI(drv, (div >> 6) & 0x3fff);
+ set_MAX2831_SYN_FRAC_LO(drv, div & 0x3f);
+ max2831_regs_commit(drv);
}
- /* Write order matters? */
- //set_MAX2831_SYN_REF_DIV(drv, MAX2831_SYN_REF_DIV_2);
- set_MAX2831_SYN_INT(drv, div_int);
- set_MAX2831_SYN_FRAC_HI(drv, (div_frac >> 6) & 0x3fff);
- set_MAX2831_SYN_FRAC_LO(drv, div_frac & 0x3f);
- max2831_regs_commit(drv);
+ return PFD_FREQ_HZ * (div << 4);
}
typedef struct {
diff --git a/firmware/common/max2831.h b/firmware/common/max2831.h
index 9298247a..f1a28c54 100644
--- a/firmware/common/max2831.h
+++ b/firmware/common/max2831.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Great Scott Gadgets
+ * Copyright 2025-2026 Great Scott Gadgets
*
* This file is part of HackRF.
*
@@ -19,12 +19,12 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MAX2831_H
-#define __MAX2831_H
+#pragma once
-#include
#include
+#include
+#include "fixed_point.h"
#include "gpio.h"
#include "spi_bus.h"
@@ -48,22 +48,22 @@ typedef enum {
MAX2831_RX_HPF_600_KHZ = 3,
} max2831_rx_hpf_freq_t;
-struct max2831_driver_t; // IWYU pragma: keep - fixed in #1704
-typedef struct max2831_driver_t max2831_driver_t;
-
-struct max2831_driver_t {
+typedef struct _max2831_driver_t {
spi_bus_t* bus;
+ void* config;
gpio_t gpio_enable;
gpio_t gpio_rxtx;
gpio_t gpio_rxhp;
gpio_t gpio_ld;
- void (*target_init)(max2831_driver_t* const drv);
- void (*set_mode)(max2831_driver_t* const drv, const max2831_mode_t new_mode);
+ void (*target_init)(struct _max2831_driver_t* const drv);
+ void (*set_mode)(
+ struct _max2831_driver_t* const drv,
+ const max2831_mode_t new_mode);
max2831_mode_t mode;
uint16_t regs[MAX2831_NUM_REGS];
uint16_t regs_dirty;
uint32_t desired_lpf_bw;
-};
+} max2831_driver_t;
/* Initialize chip. */
extern void max2831_setup(max2831_driver_t* const drv);
@@ -88,9 +88,11 @@ void max2831_set_mode(max2831_driver_t* const drv, const max2831_mode_t new_mode
extern void max2831_start(max2831_driver_t* const drv);
extern void max2831_stop(max2831_driver_t* const drv);
-/* Set frequency in Hz. Frequency setting is a multi-step function
- * where order of register writes matters. */
-extern void max2831_set_frequency(max2831_driver_t* const drv, uint32_t freq);
+/* Set frequency in 1/(2**24) Hz */
+extern fp_40_24_t max2831_set_frequency(
+ max2831_driver_t* const drv,
+ fp_40_24_t freq,
+ bool program);
uint32_t max2831_set_lpf_bandwidth(
max2831_driver_t* const drv,
const max2831_mode_t mode,
@@ -108,5 +110,3 @@ extern void max2831_tx(max2831_driver_t* const drv);
extern void max2831_rx(max2831_driver_t* const drv);
extern void max2831_tx_calibration(max2831_driver_t* const drv);
extern void max2831_rx_calibration(max2831_driver_t* const drv);
-
-#endif // __MAX2831_H
diff --git a/firmware/common/max2831_regs.def b/firmware/common/max2831_regs.def
index fb8fa8a9..fd09707d 100644
--- a/firmware/common/max2831_regs.def
+++ b/firmware/common/max2831_regs.def
@@ -1,7 +1,27 @@
-/* -*- mode: c -*- */
+/* -*- mode: c -*-
+ *
+ * Copyright 2012 Michael Ossmann
+ * Copyright 2025-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.
+ */
-#ifndef __MAX2831_REGS_DEF
-#define __MAX2831_REGS_DEF
+#pragma once
/* Generate static inline accessors that operate on the global
* regs. Done this way to (1) allow defs to be scraped out and used
@@ -128,5 +148,3 @@ __MREG__(MAX2831_RXIQ_VCM,15,10,2) // RX I/Q output common mode
#define MAX2831_RXIQ_VCM_1_2 1 // 1.2V
#define MAX2831_RXIQ_VCM_1_3 2 // 1.3V
#define MAX2831_RXIQ_VCM_1_45 3 // 1.45V
-
-#endif // __MAX2831_REGS_DEF
\ No newline at end of file
diff --git a/firmware/common/max2831_target.h b/firmware/common/max2831_target.h
index 11671b73..072faf22 100644
--- a/firmware/common/max2831_target.h
+++ b/firmware/common/max2831_target.h
@@ -19,12 +19,9 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MAX2831_TARGET_H
-#define __MAX2831_TARGET_H
+#pragma once
#include "max2831.h"
void max2831_target_init(max2831_driver_t* const drv);
void max2831_target_set_mode(max2831_driver_t* const drv, const max2831_mode_t new_mode);
-
-#endif // __MAX2831_TARGET_H
diff --git a/firmware/common/max2837.c b/firmware/common/max2837.c
index a3e91498..f512c6af 100644
--- a/firmware/common/max2837.c
+++ b/firmware/common/max2837.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2012 Will Code
* Copyright 2014 Jared Boone
*
@@ -29,12 +29,18 @@
* pirate commands to do the same thing.
*/
+#include "max2837.h"
+
+#include
#include
#include
-#include "max2837.h"
+#include "fixed_point.h"
#include "max2837_regs.def" // private register def macros
#include "selftest.h"
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
/* Default register values. */
static const uint16_t max2837_regs_default[MAX2837_NUM_REGS] = {
0x150, /* 0 */
@@ -150,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, &value, 1);
+ spi_bus_transfer(drv->bus, drv->config, &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, &value, 1);
+ spi_bus_transfer(drv->bus, drv->config, &value, 1);
}
uint16_t max2837_reg_read(max2837_driver_t* const drv, uint8_t r)
@@ -228,24 +234,32 @@ void max2837_stop(max2837_driver_t* const drv)
max2837_set_mode(drv, MAX2837_MODE_SHUTDOWN);
}
-void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq)
+/* Assume 40 MHz reference clock with R divider of 1. */
+#define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL))
+
+#define MIN_FREQ FP_MHZ(2000)
+#define MAX_FREQ FP_MHZ(3000)
+
+fp_40_24_t max2837_set_frequency(
+ max2837_driver_t* const drv,
+ fp_40_24_t freq,
+ bool program)
{
uint8_t band;
uint8_t lna_band;
- uint32_t div_frac;
- uint32_t div_int;
- uint32_t div_rem;
- uint32_t div_cmp;
- int i;
+ uint64_t div;
+
+ freq = MIN(freq, MAX_FREQ);
+ freq = MAX(freq, MIN_FREQ);
/* Select band. Allow tuning outside specified bands. */
- if (freq < 2400000000U) {
+ if (freq < FP_MHZ(2400)) {
band = MAX2837_LOGEN_BSW_2_3;
lna_band = MAX2837_LNAband_2_4;
- } else if (freq < 2500000000U) {
+ } else if (freq < FP_MHZ(2500)) {
band = MAX2837_LOGEN_BSW_2_4;
lna_band = MAX2837_LNAband_2_4;
- } else if (freq < 2600000000U) {
+ } else if (freq < FP_MHZ(2600)) {
band = MAX2837_LOGEN_BSW_2_5;
lna_band = MAX2837_LNAband_2_6;
} else {
@@ -253,35 +267,34 @@ void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq)
lna_band = MAX2837_LNAband_2_6;
}
- /* ASSUME 40MHz PLL. Ratio = F*(4/3)/40,000,000 = F/30,000,000 */
- freq += (30000000 >> 21); /* round to nearest frequency */
- div_int = freq / 30000000;
- div_rem = freq % 30000000;
- div_frac = 0;
- div_cmp = 30000000;
- for (i = 0; i < 20; i++) {
- div_frac <<= 1;
- div_rem <<= 1;
- if (div_rem >= div_cmp) {
- div_frac |= 0x1;
- div_rem -= div_cmp;
- }
+ fp_40_24_t vco = (freq * 4) / 3;
+
+ vco += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */
+ div = vco / PFD_FREQ_HZ;
+
+ /*
+ * Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional
+ * divider.
+ */
+ div = div >> 4;
+
+ if (program) {
+ /* Band settings */
+ set_MAX2837_LOGEN_BSW(drv, band);
+ set_MAX2837_LNAband(drv, lna_band);
+
+ /*
+ * Write order matters here, so commit INT and FRAC_HI before
+ * committing FRAC_LO, which is the trigger for VCO auto-select.
+ */
+ set_MAX2837_SYN_INT(drv, (div >> 20) & 0xff);
+ set_MAX2837_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff);
+ max2837_regs_commit(drv);
+ set_MAX2837_SYN_FRAC_LO(drv, div & 0x3ff);
+ max2837_regs_commit(drv);
}
- /* Band settings */
- set_MAX2837_LOGEN_BSW(drv, band);
- set_MAX2837_LNAband(drv, lna_band);
-
- /* Write order matters here, so commit INT and FRAC_HI before
- * committing FRAC_LO, which is the trigger for VCO
- * auto-select. TODO - it's cleaner this way, but it would be
- * faster to explicitly commit the registers explicitly so the
- * dirty bits aren't scanned twice. */
- set_MAX2837_SYN_INT(drv, div_int);
- set_MAX2837_SYN_FRAC_HI(drv, (div_frac >> 10) & 0x3ff);
- max2837_regs_commit(drv);
- set_MAX2837_SYN_FRAC_LO(drv, div_frac & 0x3ff);
- max2837_regs_commit(drv);
+ return ((PFD_FREQ_HZ * 3) / 4) * (div << 4);
}
typedef struct {
diff --git a/firmware/common/max2837.h b/firmware/common/max2837.h
index 0c7a65c8..a33179ac 100644
--- a/firmware/common/max2837.h
+++ b/firmware/common/max2837.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2012 Will Code
* Copyright 2014 Jared Boone
*
@@ -21,12 +21,12 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MAX2837_H
-#define __MAX2837_H
+#pragma once
-#include
#include
+#include
+#include "fixed_point.h"
#include "gpio.h"
#include "spi_bus.h"
@@ -41,20 +41,20 @@ typedef enum {
MAX2837_MODE_RX
} max2837_mode_t;
-struct max2837_driver_t; // IWYU pragma: keep - fixed in #1704
-typedef struct max2837_driver_t max2837_driver_t;
-
-struct max2837_driver_t {
+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;
- void (*target_init)(max2837_driver_t* const drv);
- void (*set_mode)(max2837_driver_t* const drv, const max2837_mode_t new_mode);
+ void (*target_init)(struct _max2837_driver_t* const drv);
+ void (*set_mode)(
+ struct _max2837_driver_t* const drv,
+ const max2837_mode_t new_mode);
max2837_mode_t mode;
uint16_t regs[MAX2837_NUM_REGS];
uint32_t regs_dirty;
-};
+} max2837_driver_t;
/* Initialize chip. */
extern void max2837_setup(max2837_driver_t* const drv);
@@ -79,9 +79,11 @@ void max2837_set_mode(max2837_driver_t* const drv, const max2837_mode_t new_mode
extern void max2837_start(max2837_driver_t* const drv);
extern void max2837_stop(max2837_driver_t* const drv);
-/* Set frequency in Hz. Frequency setting is a multi-step function
- * where order of register writes matters. */
-extern void max2837_set_frequency(max2837_driver_t* const drv, uint32_t freq);
+/* Set frequency in 1/(2**24) Hz. */
+extern fp_40_24_t max2837_set_frequency(
+ max2837_driver_t* const drv,
+ fp_40_24_t freq,
+ bool program);
uint32_t max2837_set_lpf_bandwidth(
max2837_driver_t* const drv,
const uint32_t bandwidth_hz);
@@ -91,5 +93,3 @@ bool max2837_set_txvga_gain(max2837_driver_t* const drv, const uint32_t gain_db)
extern void max2837_tx(max2837_driver_t* const drv);
extern void max2837_rx(max2837_driver_t* const drv);
-
-#endif // __MAX2837_H
diff --git a/firmware/common/max2837_regs.def b/firmware/common/max2837_regs.def
index 0aa2f9e5..67eb6dd7 100644
--- a/firmware/common/max2837_regs.def
+++ b/firmware/common/max2837_regs.def
@@ -1,7 +1,26 @@
-/* -*- mode: c -*- */
+/* -*- mode: c -*- *
+ * Copyright 2012 Michael Ossmann
+ * Copyright 2025-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.
+ */
-#ifndef __MAX2837_REGS_DEF
-#define __MAX2837_REGS_DEF
+#pragma once
/* Generate static inline accessors that operate on the global
* regs. Done this way to (1) allow defs to be scraped out and used
@@ -404,5 +423,3 @@ __MREG__(MAX2837_FUSE_RTH,30,9,1)
// 0 -> 992/0uA correction, 15 -> 0/992uA correction ... if TX_DCCORR_SPI_EN
__MREG__(MAX2837_TX_DCCORR_I,31,4,5)
__MREG__(MAX2837_TX_DCCORR_Q,31,9,5)
-
-#endif // __MAX2837_REGS_DEF
diff --git a/firmware/common/max2837_target.h b/firmware/common/max2837_target.h
index 75d87698..2910791e 100644
--- a/firmware/common/max2837_target.h
+++ b/firmware/common/max2837_target.h
@@ -21,12 +21,9 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MAX2837_TARGET_H
-#define __MAX2837_TARGET_H
+#pragma once
#include "max2837.h"
void max2837_target_init(max2837_driver_t* const drv);
void max2837_target_set_mode(max2837_driver_t* const drv, const max2837_mode_t new_mode);
-
-#endif // __MAX2837_TARGET_H
diff --git a/firmware/common/max2839.c b/firmware/common/max2839.c
index 62c5561f..a938b733 100644
--- a/firmware/common/max2839.c
+++ b/firmware/common/max2839.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2012 Will Code
* Copyright 2014 Jared Boone
*
@@ -29,12 +29,18 @@
* pirate commands to do the same thing.
*/
+#include "max2839.h"
+
+#include
#include
#include
-#include "max2839.h"
+#include "fixed_point.h"
#include "max2839_regs.def" // private register def macros
#include "selftest.h"
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
static uint8_t requested_lna_gain = 0;
static uint8_t requested_vga_gain = 0;
@@ -158,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, &value, 1);
+ spi_bus_transfer(drv->bus, drv->config, &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, &value, 1);
+ spi_bus_transfer(drv->bus, drv->config, &value, 1);
}
uint16_t max2839_reg_read(max2839_driver_t* const drv, uint8_t r)
@@ -239,54 +245,61 @@ void max2839_stop(max2839_driver_t* const drv)
max2839_set_mode(drv, MAX2839_MODE_SHUTDOWN);
}
-void max2839_set_frequency(max2839_driver_t* const drv, uint32_t freq)
+/* Assume 40 MHz reference clock with R divider of 1. */
+#define PFD_FREQ_HZ (40ULL * (1000ULL * 1000ULL))
+
+#define MIN_FREQ FP_MHZ(2000)
+#define MAX_FREQ FP_MHZ(3000)
+
+fp_40_24_t max2839_set_frequency(
+ max2839_driver_t* const drv,
+ fp_40_24_t freq,
+ bool program)
{
uint8_t band;
- uint32_t div_frac;
- uint32_t div_int;
- uint32_t div_rem;
- uint32_t div_cmp;
- int i;
+ uint64_t div;
+
+ freq = MIN(freq, MAX_FREQ);
+ freq = MAX(freq, MIN_FREQ);
/* Select band. Allow tuning outside specified bands. */
- if (freq < 2400000000U) {
+ if (freq < FP_MHZ(2400)) {
band = MAX2839_LOGEN_BSW_2_3;
- } else if (freq < 2500000000U) {
+ } else if (freq < FP_MHZ(2500)) {
band = MAX2839_LOGEN_BSW_2_4;
- } else if (freq < 2600000000U) {
+ } else if (freq < FP_MHZ(2600)) {
band = MAX2839_LOGEN_BSW_2_5;
} else {
band = MAX2839_LOGEN_BSW_2_6;
}
- /* ASSUME 40MHz PLL. Ratio = F*(4/3)/40,000,000 = F/30,000,000 */
- freq += (30000000 >> 21); /* round to nearest frequency */
- div_int = freq / 30000000;
- div_rem = freq % 30000000;
- div_frac = 0;
- div_cmp = 30000000;
- for (i = 0; i < 20; i++) {
- div_frac <<= 1;
- div_rem <<= 1;
- if (div_rem >= div_cmp) {
- div_frac |= 0x1;
- div_rem -= div_cmp;
- }
+ fp_40_24_t vco = (freq * 4) / 3;
+
+ vco += ((PFD_FREQ_HZ * FP_ONE_HZ) >> 21); /* round to nearest frequency */
+ div = vco / PFD_FREQ_HZ;
+
+ /*
+ * Shift from 40.24 fixed-point to 44.20 to match 20-bit fractional
+ * divider.
+ */
+ div = div >> 4;
+
+ if (program) {
+ /* Band settings */
+ set_MAX2839_LOGEN_BSW(drv, band);
+
+ /*
+ * Write order matters here, so commit INT and FRAC_HI before
+ * committing FRAC_LO, which is the trigger for VCO auto-select.
+ */
+ set_MAX2839_SYN_INT(drv, (div >> 20) & 0xff);
+ set_MAX2839_SYN_FRAC_HI(drv, (div >> 10) & 0x3ff);
+ max2839_regs_commit(drv);
+ set_MAX2839_SYN_FRAC_LO(drv, div & 0x3ff);
+ max2839_regs_commit(drv);
}
- /* Band settings */
- set_MAX2839_LOGEN_BSW(drv, band);
-
- /* Write order matters here, so commit INT and FRAC_HI before
- * committing FRAC_LO, which is the trigger for VCO
- * auto-select. TODO - it's cleaner this way, but it would be
- * faster to explicitly commit the registers explicitly so the
- * dirty bits aren't scanned twice. */
- set_MAX2839_SYN_INT(drv, div_int);
- set_MAX2839_SYN_FRAC_HI(drv, (div_frac >> 10) & 0x3ff);
- max2839_regs_commit(drv);
- set_MAX2839_SYN_FRAC_LO(drv, div_frac & 0x3ff);
- max2839_regs_commit(drv);
+ return ((PFD_FREQ_HZ * 3) / 4) * (div << 4);
}
typedef struct {
diff --git a/firmware/common/max2839.h b/firmware/common/max2839.h
index ac238fe2..6430ce37 100644
--- a/firmware/common/max2839.h
+++ b/firmware/common/max2839.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2012 Will Code
* Copyright 2014 Jared Boone
*
@@ -21,12 +21,12 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MAX2839_H
-#define __MAX2839_H
+#pragma once
-#include
#include
+#include
+#include "fixed_point.h"
#include "gpio.h"
#include "spi_bus.h"
@@ -44,19 +44,19 @@ typedef enum {
MAX2839_MODE_CLKOUT,
} max2839_mode_t;
-struct max2839_driver_t; // IWYU pragma: keep - fixed in #1704
-typedef struct max2839_driver_t max2839_driver_t;
-
-struct max2839_driver_t {
+typedef struct _max2839_driver_t {
spi_bus_t* bus;
+ void* config;
gpio_t gpio_enable;
gpio_t gpio_rxtx;
- void (*target_init)(max2839_driver_t* const drv);
- void (*set_mode)(max2839_driver_t* const drv, const max2839_mode_t new_mode);
+ void (*target_init)(struct _max2839_driver_t* const drv);
+ void (*set_mode)(
+ struct _max2839_driver_t* const drv,
+ const max2839_mode_t new_mode);
max2839_mode_t mode;
uint16_t regs[MAX2839_NUM_REGS];
uint32_t regs_dirty;
-};
+} max2839_driver_t;
/* Initialize chip. */
extern void max2839_setup(max2839_driver_t* const drv);
@@ -81,9 +81,11 @@ void max2839_set_mode(max2839_driver_t* const drv, const max2839_mode_t new_mode
extern void max2839_start(max2839_driver_t* const drv);
extern void max2839_stop(max2839_driver_t* const drv);
-/* Set frequency in Hz. Frequency setting is a multi-step function
- * where order of register writes matters. */
-extern void max2839_set_frequency(max2839_driver_t* const drv, uint32_t freq);
+/* Set frequency in 1/(2**24) Hz. */
+extern fp_40_24_t max2839_set_frequency(
+ max2839_driver_t* const drv,
+ fp_40_24_t freq,
+ bool program);
uint32_t max2839_set_lpf_bandwidth(
max2839_driver_t* const drv,
const uint32_t bandwidth_hz);
@@ -93,5 +95,3 @@ bool max2839_set_txvga_gain(max2839_driver_t* const drv, const uint32_t gain_db)
extern void max2839_tx(max2839_driver_t* const drv);
extern void max2839_rx(max2839_driver_t* const drv);
-
-#endif // __MAX2839_H
diff --git a/firmware/common/max2839_regs.def b/firmware/common/max2839_regs.def
index bee66846..11b1d80d 100644
--- a/firmware/common/max2839_regs.def
+++ b/firmware/common/max2839_regs.def
@@ -1,7 +1,26 @@
-/* -*- mode: c -*- */
+/* -*- mode: c -*- *
+ * Copyright 2012 Michael Ossmann
+ * Copyright 2025-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.
+ */
-#ifndef __MAX2839_REGS_DEF
-#define __MAX2839_REGS_DEF
+#pragma once
/* Generate static inline accessors that operate on the global
* regs. Done this way to (1) allow defs to be scraped out and used
@@ -255,5 +274,3 @@ __MREG__(MAX2839_PA_DAC_Voltage_Mode_Output_Select,30,9,1)
__MREG__(MAX2839_TX_DC_Offset_Correction_QChannel,31,5,6)
__MREG__(MAX2839_RESERVED_31_8,31,8,3)
__MREG__(MAX2839_PA_DAC_Clk_Divide_Ratio,31,9,1)
-
-#endif // __MAX2839_REGS_DEF
diff --git a/firmware/common/max2839_target.h b/firmware/common/max2839_target.h
index 89488243..0c21e98d 100644
--- a/firmware/common/max2839_target.h
+++ b/firmware/common/max2839_target.h
@@ -21,12 +21,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MAX2839_TARGET_H
-#define __MAX2839_TARGET_H
-
#include "max2839.h"
void max2839_target_init(max2839_driver_t* const drv);
void max2839_target_set_mode(max2839_driver_t* const drv, const max2839_mode_t new_mode);
-
-#endif // __MAX2839_TARGET_H
diff --git a/firmware/common/max283x.c b/firmware/common/max283x.c
index 7c5de380..f9486921 100644
--- a/firmware/common/max283x.c
+++ b/firmware/common/max283x.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2012 Will Code
* Copyright 2014 Jared Boone
*
@@ -21,171 +21,203 @@
* Boston, MA 02110-1301, USA.
*/
-#include
-
#include "max283x.h"
+#include
+#include
+
+#include
+
+#include "fixed_point.h"
+#include "platform_detect.h"
#include "platform_gpio.h"
-#include "spi_bus.h"
-#if defined(PRALINE)
- #include "max2831.h"
+
+#ifdef IS_PRALINE
#include "max2831_target.h"
-#else
- #include "max2837.h"
+#endif
+#ifdef IS_NOT_PRALINE
#include "max2837_target.h"
- #include "max2839.h"
+#endif
+#ifdef IS_H1_R9
#include "max2839_target.h"
#endif
-extern spi_bus_t spi_bus_ssp1;
+/* 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,
+};
-#ifdef PRALINE
+max283x_driver_t max283x = {};
+
+#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,
};
-#else
+#endif
+
+#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,
};
+#endif
+#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,
};
#endif
/* Initialize chip. */
-void max283x_setup(max283x_driver_t* const drv, max283x_variant_t type)
+void max283x_setup(max283x_driver_t* const drv)
{
- drv->type = type;
-
const platform_gpio_t* gpio = platform_gpio();
/* MAX283x GPIO PinMux */
-#if defined(PRALINE)
- max2831.gpio_enable = gpio->max283x_enable;
- max2831.gpio_rxtx = gpio->max283x_rx_enable;
- max2831.gpio_rxhp = gpio->max2831_rxhp;
- max2831.gpio_ld = gpio->max2831_ld;
-#else
- max2837.gpio_enable = gpio->max283x_enable;
- max2837.gpio_rx_enable = gpio->max283x_rx_enable;
- max2837.gpio_tx_enable = gpio->max283x_tx_enable;
- max2839.gpio_enable = gpio->max283x_enable;
- max2839.gpio_rxtx = gpio->max283x_rx_enable;
-#endif
-
- switch (type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
+ 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;
+ max2831.gpio_rxhp = gpio->max2831_rxhp;
+ max2831.gpio_ld = gpio->max2831_ld;
memcpy(&drv->drv.max2831, &max2831, sizeof(max2831));
max2831_setup(&drv->drv.max2831);
- break;
-#else
- case MAX2837_VARIANT:
- memcpy(&drv->drv.max2837, &max2837, sizeof(max2837));
- max2837_setup(&drv->drv.max2837);
- break;
-
- case MAX2839_VARIANT:
- memcpy(&drv->drv.max2839, &max2839, sizeof(max2839));
- max2839_setup(&drv->drv.max2839);
- break;
-#endif
}
+#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;
+ max2839.gpio_enable = gpio->max283x_enable;
+ max2839.gpio_rxtx = gpio->max283x_rx_enable;
+ memcpy(&drv->drv.max2839, &max2839, sizeof(max2839));
+ max2839_setup(&drv->drv.max2839);
+ }
+ #endif
+ #ifdef IS_NOT_H1_R9
+ if (IS_NOT_H1_R9) {
+ drv->type = MAX2837_VARIANT;
+ max2837.gpio_enable = gpio->max283x_enable;
+ max2837.gpio_rx_enable = gpio->max283x_rx_enable;
+ max2837.gpio_tx_enable = gpio->max283x_tx_enable;
+ memcpy(&drv->drv.max2837, &max2837, sizeof(max2837));
+ max2837_setup(&drv->drv.max2837);
+ }
+ #endif
+ }
+#endif
}
+/* Macros to simplify dispatch of variant-specific operations. */
+
+/* clang-format off */
+
+#if UNIVERSAL
+ // UNIVERSAL: all variants
+ #define DISPATCH(_drv, _max2831, _max2837, _max2839) \
+ if (_drv->type == MAX2831_VARIANT) { \
+ _max2831; \
+ } else if (_drv->type == MAX2837_VARIANT) { \
+ _max2837; \
+ } else { \
+ _max2839; \
+ }
+#elif HACKRF_ONE
+ // HACKRF_ONE: MAX2837 and MAX2839 only
+ #define DISPATCH(_drv, _max2831, _max2837, _max2839) \
+ if (_drv->type == MAX2837_VARIANT) { \
+ _max2837; \
+ } else { \
+ _max2839; \
+ }
+#elif PRALINE
+ // PRALINE: MAX2831 only
+ #define DISPATCH(_drv, _max2831, _max2837, _max2839) \
+ (void) drv; \
+ _max2831
+#else
+ // JAWBREAKER, RAD1O: MAX2837 only
+ #define DISPATCH(_drv, _max2831, _max2837, _max2839) \
+ (void) drv; \
+ _max2837
+#endif
+
+#define CALL(_drv, _func, ...) \
+ DISPATCH(drv, \
+ max2831_ ## _func(&_drv->drv.max2831, ##__VA_ARGS__), \
+ max2837_ ## _func(&_drv->drv.max2837, ##__VA_ARGS__), \
+ max2839_ ## _func(&_drv->drv.max2839, ##__VA_ARGS__) \
+ ); \
+
+#define RESULT(_drv, _type, _func, ...) ({ \
+ _type _result; \
+ DISPATCH(drv, \
+ _result = max2831_ ## _func(&_drv->drv.max2831, ##__VA_ARGS__), \
+ _result = max2837_ ## _func(&_drv->drv.max2837, ##__VA_ARGS__), \
+ _result = max2839_ ## _func(&_drv->drv.max2839, ##__VA_ARGS__) \
+ ); \
+ _result; \
+})
+
+#define CONSTANT(_drv, _type, _name) ({\
+ _type value; \
+ DISPATCH(drv, \
+ value = MAX2831_ ## _name, \
+ value = MAX2837_ ## _name, \
+ value = MAX2839_ ## _name \
+ ); \
+ value; \
+})
+
+#define PRALINE_ONLY(_drv, _max2831) DISPATCH(_drv, _max2831, , )
+
+/* clang-format on */
+
/* Returns the number of registers supported by the driver. */
uint16_t max283x_num_regs(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- return MAX2831_NUM_REGS;
- break;
-#else
- case MAX2837_VARIANT:
- return MAX2837_NUM_REGS;
- break;
-
- case MAX2839_VARIANT:
- return MAX2839_NUM_REGS;
- break;
-#endif
- }
-
- return 0;
+ return CONSTANT(drv, uint16_t, NUM_REGS);
}
/* Returns the maximum data register value supported by the driver. */
uint16_t max283x_data_regs_max_value(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- return MAX2831_DATA_REGS_MAX_VALUE;
- break;
-#else
- case MAX2837_VARIANT:
- return MAX2837_DATA_REGS_MAX_VALUE;
- break;
-
- case MAX2839_VARIANT:
- return MAX2839_DATA_REGS_MAX_VALUE;
- break;
-#endif
- }
-
- return 0;
+ return CONSTANT(drv, uint16_t, DATA_REGS_MAX_VALUE);
}
/* Read a register via SPI. Save a copy to memory and return
* value. Mark clean. */
uint16_t max283x_reg_read(max283x_driver_t* const drv, uint8_t r)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- return max2831_reg_read(&drv->drv.max2831, r);
- break;
-#else
- case MAX2837_VARIANT:
- return max2837_reg_read(&drv->drv.max2837, r);
- break;
-
- case MAX2839_VARIANT:
- return max2839_reg_read(&drv->drv.max2839, r);
- break;
-#endif
- }
-
- return 0;
+ return RESULT(drv, uint16_t, reg_read, r);
}
/* Write value to register via SPI and save a copy to memory. Mark
* clean. */
void max283x_reg_write(max283x_driver_t* const drv, uint8_t r, uint16_t v)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_reg_write(&drv->drv.max2831, r, v);
- break;
-#else
- case MAX2837_VARIANT:
- max2837_reg_write(&drv->drv.max2837, r, v);
- break;
-
- case MAX2839_VARIANT:
- max2839_reg_write(&drv->drv.max2839, r, v);
- break;
-#endif
- }
+ CALL(drv, reg_write, r, v);
}
/* Write all dirty registers via SPI from memory. Mark all clean. Some
@@ -193,120 +225,45 @@ void max283x_reg_write(max283x_driver_t* const drv, uint8_t r, uint16_t v)
* provided routines for those operations. */
void max283x_regs_commit(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_regs_commit(&drv->drv.max2831);
- break;
-#else
- case MAX2837_VARIANT:
- max2837_regs_commit(&drv->drv.max2837);
- break;
-
- case MAX2839_VARIANT:
- max2839_regs_commit(&drv->drv.max2839);
- break;
-#endif
- }
+ CALL(drv, regs_commit);
}
void max283x_set_mode(max283x_driver_t* const drv, const max283x_mode_t new_mode)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_set_mode(&drv->drv.max2831, (max2831_mode_t) new_mode);
- break;
-#else
- case MAX2837_VARIANT:
- max2837_set_mode(&drv->drv.max2837, (max2837_mode_t) new_mode);
- break;
-
- case MAX2839_VARIANT:
- max2839_set_mode(&drv->drv.max2839, (max2839_mode_t) new_mode);
- break;
-#endif
- }
+ DISPATCH(
+ drv,
+ max2831_set_mode(&drv->drv.max2831, (max2831_mode_t) new_mode),
+ max2837_set_mode(&drv->drv.max2837, (max2837_mode_t) new_mode),
+ max2839_set_mode(&drv->drv.max2839, (max2839_mode_t) new_mode));
}
max283x_mode_t max283x_mode(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- return (max283x_mode_t) max2831_mode(&drv->drv.max2831);
- break;
-#else
- case MAX2837_VARIANT:
- return (max283x_mode_t) max2837_mode(&drv->drv.max2837);
- break;
-
- case MAX2839_VARIANT:
- return (max283x_mode_t) max2839_mode(&drv->drv.max2839);
- break;
-#endif
- }
- return 0;
+ DISPATCH(
+ drv,
+ return (max283x_mode_t) max2831_mode(&drv->drv.max2831),
+ return (max283x_mode_t) max2837_mode(&drv->drv.max2837),
+ return (max283x_mode_t) max2839_mode(&drv->drv.max2839));
}
/* Turn on/off all chip functions. Does not control oscillator and CLKOUT */
void max283x_start(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_start(&drv->drv.max2831);
- break;
-#else
- case MAX2837_VARIANT:
- max2837_start(&drv->drv.max2837);
- break;
-
- case MAX2839_VARIANT:
- max2839_start(&drv->drv.max2839);
- break;
-#endif
- }
+ CALL(drv, start);
}
void max283x_stop(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_stop(&drv->drv.max2831);
- break;
-#else
- case MAX2837_VARIANT:
- max2837_stop(&drv->drv.max2837);
- break;
-
- case MAX2839_VARIANT:
- max2839_stop(&drv->drv.max2839);
- break;
-#endif
- }
+ CALL(drv, stop);
}
-/* Set frequency in Hz. Frequency setting is a multi-step function
- * where order of register writes matters. */
-void max283x_set_frequency(max283x_driver_t* const drv, uint32_t freq)
+/* Set frequency in 1/(2**24) Hz. */
+fp_40_24_t max283x_set_frequency(
+ max283x_driver_t* const drv,
+ fp_40_24_t freq,
+ bool program)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_set_frequency(&drv->drv.max2831, freq);
- break;
-#else
- case MAX2837_VARIANT:
- max2837_set_frequency(&drv->drv.max2837, freq);
- break;
-
- case MAX2839_VARIANT:
- max2839_set_frequency(&drv->drv.max2839, freq);
- break;
-#endif
- }
+ return RESULT(drv, fp_40_24_t, set_frequency, freq, program);
}
uint32_t max283x_set_lpf_bandwidth(
@@ -314,194 +271,61 @@ uint32_t max283x_set_lpf_bandwidth(
const max283x_mode_t mode,
const uint32_t bandwidth_hz)
{
-#ifndef PRALINE
(void) mode;
-#endif
-
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
+ DISPATCH(
+ drv,
return max2831_set_lpf_bandwidth(
&drv->drv.max2831,
(max2831_mode_t) mode,
- bandwidth_hz);
- break;
-#else
- case MAX2837_VARIANT:
- return max2837_set_lpf_bandwidth(&drv->drv.max2837, bandwidth_hz);
- break;
-
- case MAX2839_VARIANT:
- return max2839_set_lpf_bandwidth(&drv->drv.max2839, bandwidth_hz);
- break;
-#endif
- }
-
- return 0;
+ bandwidth_hz),
+ return max2837_set_lpf_bandwidth(&drv->drv.max2837, bandwidth_hz),
+ return max2839_set_lpf_bandwidth(&drv->drv.max2839, bandwidth_hz));
}
bool max283x_set_lna_gain(max283x_driver_t* const drv, const uint32_t gain_db)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- return max2831_set_lna_gain(&drv->drv.max2831, gain_db);
- break;
-#else
- case MAX2837_VARIANT:
- return max2837_set_lna_gain(&drv->drv.max2837, gain_db);
- break;
-
- case MAX2839_VARIANT:
- return max2839_set_lna_gain(&drv->drv.max2839, gain_db);
- break;
-#endif
- }
-
- return false;
+ return RESULT(drv, bool, set_lna_gain, gain_db);
}
bool max283x_set_vga_gain(max283x_driver_t* const drv, const uint32_t gain_db)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- return max2831_set_vga_gain(&drv->drv.max2831, gain_db);
- break;
-#else
- case MAX2837_VARIANT:
- return max2837_set_vga_gain(&drv->drv.max2837, gain_db);
- break;
-
- case MAX2839_VARIANT:
- return max2839_set_vga_gain(&drv->drv.max2839, gain_db);
- break;
-#endif
- }
-
- return false;
+ return RESULT(drv, bool, set_vga_gain, gain_db);
}
bool max283x_set_txvga_gain(max283x_driver_t* const drv, const uint32_t gain_db)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- return max2831_set_txvga_gain(&drv->drv.max2831, gain_db);
- break;
-#else
- case MAX2837_VARIANT:
- return max2837_set_txvga_gain(&drv->drv.max2837, gain_db);
- break;
-
- case MAX2839_VARIANT:
- return max2839_set_txvga_gain(&drv->drv.max2839, gain_db);
- break;
-#endif
- }
-
- return false;
+ return RESULT(drv, bool, set_txvga_gain, gain_db);
}
void max283x_tx(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_tx(&drv->drv.max2831);
- break;
-#else
- case MAX2837_VARIANT:
- max2837_tx(&drv->drv.max2837);
- break;
-
- case MAX2839_VARIANT:
- max2839_tx(&drv->drv.max2839);
- break;
-#endif
- }
+ CALL(drv, tx);
}
void max283x_rx(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_rx(&drv->drv.max2831);
- break;
-#else
- case MAX2837_VARIANT:
- max2837_rx(&drv->drv.max2837);
- break;
-
- case MAX2839_VARIANT:
- max2839_rx(&drv->drv.max2839);
- break;
-#endif
- }
+ CALL(drv, rx);
}
+/* Set MAX2831 receiver high-pass filter corner frequency in Hz */
void max283x_set_rx_hpf_frequency(
max283x_driver_t* const drv,
const max283x_rx_hpf_freq_t freq)
{
-#ifndef PRALINE
(void) freq;
-#endif
-
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
+ PRALINE_ONLY(
+ drv,
max2831_set_rx_hpf_frequency(
&drv->drv.max2831,
- (max2831_rx_hpf_freq_t) freq);
- break;
-#else
- case MAX2837_VARIANT:
- // unsupported
- break;
-
- case MAX2839_VARIANT:
- // unsupported
- break;
-#endif
- }
+ (max2831_rx_hpf_freq_t) freq));
}
void max283x_tx_calibration(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_tx_calibration(&drv->drv.max2831);
- break;
-#else
- case MAX2837_VARIANT:
- // unsupported - use max283x_set_mode instead
- break;
-
- case MAX2839_VARIANT:
- // unsupported - use max283x_set_mode instead
- break;
-#endif
- }
+ PRALINE_ONLY(drv, max2831_tx_calibration(&drv->drv.max2831));
}
void max283x_rx_calibration(max283x_driver_t* const drv)
{
- switch (drv->type) {
-#ifdef PRALINE
- case MAX2831_VARIANT:
- max2831_rx_calibration(&drv->drv.max2831);
- break;
-#else
- case MAX2837_VARIANT:
- // unsupported - use max283x_set_mode instead
- break;
-
- case MAX2839_VARIANT:
- // unsupported - use max283x_set_mode instead
- break;
-#endif
- }
+ PRALINE_ONLY(drv, max2831_rx_calibration(&drv->drv.max2831));
}
diff --git a/firmware/common/max283x.h b/firmware/common/max283x.h
index e67bf8de..7c9c03dd 100644
--- a/firmware/common/max283x.h
+++ b/firmware/common/max283x.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2012 Will Code
* Copyright 2014 Jared Boone
*
@@ -21,16 +21,21 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MAX283x_H
-#define __MAX283x_H
+#pragma once
#include
#include
-#if defined(PRALINE)
+#include "fixed_point.h"
+#include "spi_ssp.h"
+
+#ifdef IS_PRALINE
#include "max2831.h"
-#else
+#endif
+#ifdef IS_NOT_PRALINE
#include "max2837.h"
+#endif
+#ifdef IS_H1_R9
#include "max2839.h"
#endif
@@ -52,10 +57,13 @@ typedef enum {
} max283x_rx_hpf_freq_t;
typedef enum {
-#ifdef PRALINE
+#ifdef IS_PRALINE
MAX2831_VARIANT,
-#else
+#endif
+#ifdef IS_NOT_PRALINE
MAX2837_VARIANT,
+#endif
+#ifdef IS_H1_R9
MAX2839_VARIANT,
#endif
} max283x_variant_t;
@@ -64,17 +72,20 @@ typedef struct {
max283x_variant_t type;
union {
-#ifdef PRALINE
+#ifdef IS_PRALINE
max2831_driver_t max2831;
-#else
+#endif
+#ifdef IS_NOT_PRALINE
max2837_driver_t max2837;
+#endif
+#ifdef IS_H1_R9
max2839_driver_t max2839;
#endif
} drv;
} max283x_driver_t;
/* Initialize chip. */
-void max283x_setup(max283x_driver_t* const drv, max283x_variant_t type);
+void max283x_setup(max283x_driver_t* const drv);
/* Returns the number of registers supported by the driver. */
uint16_t max283x_num_regs(max283x_driver_t* const drv);
@@ -102,9 +113,11 @@ void max283x_set_mode(max283x_driver_t* const drv, const max283x_mode_t new_mode
void max283x_start(max283x_driver_t* const drv);
void max283x_stop(max283x_driver_t* const drv);
-/* Set frequency in Hz. Frequency setting is a multi-step function
- * where order of register writes matters. */
-void max283x_set_frequency(max283x_driver_t* const drv, uint32_t freq);
+/* Set frequency in 1/(2**24) Hz. */
+fp_40_24_t max283x_set_frequency(
+ max283x_driver_t* const drv,
+ fp_40_24_t freq,
+ bool program);
uint32_t max283x_set_lpf_bandwidth(
max283x_driver_t* const drv,
const max283x_mode_t mode,
@@ -127,4 +140,6 @@ void max283x_set_rx_hpf_frequency(
void max283x_tx_calibration(max283x_driver_t* const drv);
void max283x_rx_calibration(max283x_driver_t* const drv);
-#endif // __MAX283x_H
+/* Driver instance. */
+extern ssp_config_t ssp_config_max283x;
+extern max283x_driver_t max283x;
diff --git a/firmware/common/max2871.c b/firmware/common/max2871.c
index 2d9b0c29..05840678 100644
--- a/firmware/common/max2871.c
+++ b/firmware/common/max2871.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2022 Great Scott Gadgets
+ * Copyright 2015-2026 Great Scott Gadgets
*
* This file is part of HackRF.
*
@@ -24,18 +24,23 @@
#include
#include
+#include
+
+#include "fixed_point.h"
#include "max2871_regs.h"
-#include "selftest.h"
#include "platform_scu.h"
+#include "selftest.h"
#if (defined DEBUG)
#include
#define LOG printf
#else
#define LOG(x, ...)
- #include
#endif
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
static uint32_t max2871_spi_read(max2871_driver_t* const drv);
static void max2871_spi_write(max2871_driver_t* const drv, uint8_t r, uint32_t v);
static void max2871_write_registers(max2871_driver_t* const drv);
@@ -132,7 +137,7 @@ void max2871_setup(max2871_driver_t* const drv)
max2871_write_registers(drv);
- max2871_set_frequency(drv, 3500);
+ max2871_set_frequency(drv, FP_MHZ(3500), true);
}
static void delay_ms(int ms)
@@ -228,9 +233,16 @@ static void max2871_write_registers(max2871_driver_t* const drv)
}
}
-/* Set frequency (MHz). */
-uint64_t max2871_set_frequency(max2871_driver_t* const drv, uint16_t mhz)
+#define MIN_LO FP_MHZ(40)
+#define MAX_LO FP_MHZ(6000)
+
+/* Set frequency in 1/(2**24) Hz, rounded to nearest 40 MHz. */
+fp_40_24_t max2871_set_frequency(max2871_driver_t* const drv, fp_40_24_t lo, bool program)
{
+ lo = MIN(lo, MAX_LO);
+ lo = MAX(lo, MIN_LO);
+
+ uint16_t mhz = lo / FP_ONE_MHZ;
int n = mhz / 40;
int diva = 0;
@@ -239,19 +251,21 @@ uint64_t max2871_set_frequency(max2871_driver_t* const drv, uint16_t mhz)
diva += 1;
}
- max2871_set_RFA_EN(0);
- max2871_write_registers(drv);
+ if (program) {
+ max2871_set_RFA_EN(0);
+ max2871_write_registers(drv);
- max2871_set_N(n);
- max2871_set_DIVA(diva);
- max2871_write_registers(drv);
+ max2871_set_N(n);
+ max2871_set_DIVA(diva);
+ max2871_write_registers(drv);
- while (max2871_spi_read(drv) & MAX2871_VASA) {}
+ while (max2871_spi_read(drv) & MAX2871_VASA) {}
- max2871_set_RFA_EN(1);
- max2871_write_registers(drv);
+ max2871_set_RFA_EN(1);
+ max2871_write_registers(drv);
+ }
- return (mhz / 40) * 40 * 1000000;
+ return (mhz / 40) * FP_MHZ(40);
}
void max2871_enable(max2871_driver_t* const drv)
diff --git a/firmware/common/max2871.h b/firmware/common/max2871.h
index 3cc9bb3a..682ee8c0 100644
--- a/firmware/common/max2871.h
+++ b/firmware/common/max2871.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Great Scott Gadgets
+ * Copyright 2017-2026 Great Scott Gadgets
*
* This file is part of HackRF.
*
@@ -19,11 +19,11 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef MAX2871_H
-#define MAX2871_H
+#pragma once
-#include
+#include
+#include "fixed_point.h"
#include "gpio.h"
typedef struct {
@@ -36,7 +36,9 @@ typedef struct {
} max2871_driver_t;
extern void max2871_setup(max2871_driver_t* const drv);
-extern uint64_t max2871_set_frequency(max2871_driver_t* const drv, uint16_t mhz);
+extern fp_40_24_t max2871_set_frequency(
+ max2871_driver_t* const drv,
+ fp_40_24_t lo,
+ bool program);
extern void max2871_enable(max2871_driver_t* const drv);
extern void max2871_disable(max2871_driver_t* const drv);
-#endif
diff --git a/firmware/common/max2871_regs.h b/firmware/common/max2871_regs.h
index a18f6217..b0dbdacf 100644
--- a/firmware/common/max2871_regs.h
+++ b/firmware/common/max2871_regs.h
@@ -19,8 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef MAX2871_REGS_H
-#define MAX2871_REGS_H
+#pragma once
+
#include
#define MAX2871_VASA (1 << 9)
@@ -75,4 +75,3 @@ void max2871_set_F01(uint32_t v);
void max2871_set_LD(uint32_t v);
void max2871_set_ADCS(uint32_t v);
void max2871_set_ADCM(uint32_t v);
-#endif
diff --git a/firmware/common/max5864.c b/firmware/common/max5864.c
index b91a1cd1..0b825fcd 100644
--- a/firmware/common/max5864.c
+++ b/firmware/common/max5864.c
@@ -24,9 +24,34 @@
#include
+#include
+
+#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, &value, 1);
+ spi_bus_transfer(drv->bus, &ssp_config_max5864, &value, 1);
}
static void max5864_init(max5864_driver_t* const drv)
@@ -36,6 +61,8 @@ 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);
}
diff --git a/firmware/common/max5864.h b/firmware/common/max5864.h
index 0ce2c6e1..0f561685 100644
--- a/firmware/common/max5864.h
+++ b/firmware/common/max5864.h
@@ -20,18 +20,15 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MAX5864_H
-#define __MAX5864_H
+#pragma once
#include "spi_bus.h"
+#include "spi_ssp.h"
-struct max5864_driver_t; // IWYU pragma: keep - fixed in #1704
-typedef struct max5864_driver_t max5864_driver_t;
-
-struct max5864_driver_t {
+typedef struct _max5864_driver_t {
spi_bus_t* const bus;
- void (*target_init)(max5864_driver_t* const drv);
-};
+ void (*target_init)(struct _max5864_driver_t* const drv);
+} max5864_driver_t;
void max5864_setup(max5864_driver_t* const drv);
@@ -42,4 +39,6 @@ void max5864_rx(max5864_driver_t* const drv);
void max5864_tx(max5864_driver_t* const drv);
void max5864_xcvr(max5864_driver_t* const drv);
-#endif // __MAX5864_H
+/* Driver instance. */
+extern ssp_config_t ssp_config_max5864;
+extern max5864_driver_t max5864;
diff --git a/firmware/common/max5864_target.h b/firmware/common/max5864_target.h
index 2ddd9ed1..a162e0ac 100644
--- a/firmware/common/max5864_target.h
+++ b/firmware/common/max5864_target.h
@@ -20,11 +20,8 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MAX5864_TARGET_H__
-#define __MAX5864_TARGET_H__
+#pragma once
#include "max5864.h"
void max5864_target_init(max5864_driver_t* const drv);
-
-#endif /*__MAX5864_TARGET_H__*/
diff --git a/firmware/common/mixer.c b/firmware/common/mixer.c
index cc598701..e38c28bf 100644
--- a/firmware/common/mixer.c
+++ b/firmware/common/mixer.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2022 Great Scott Gadgets
+ * Copyright 2017-2026 Great Scott Gadgets
*
* This file is part of HackRF.
*
@@ -21,102 +21,146 @@
#include "mixer.h"
+#include
+
+#include "fixed_point.h"
+#include "platform_detect.h"
#include "platform_gpio.h"
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
+#ifdef IS_RAD1O
+ #include "max2871.h"
+#endif
+#ifdef IS_NOT_RAD1O
#include "rffc5071.h"
#include "rffc5071_spi.h"
#include "spi_bus.h"
-#elif defined(RAD1O)
- #include "max2871.h"
#endif
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
+#ifdef IS_NOT_RAD1O
static rffc5071_spi_config_t rffc5071_spi_config;
-spi_bus_t spi_bus_rffc5071 = {
+static spi_bus_t spi_bus_rffc5071 = {
.config = &rffc5071_spi_config,
.start = rffc5071_spi_start,
.stop = rffc5071_spi_stop,
.transfer = rffc5071_spi_transfer,
.transfer_gather = rffc5071_spi_transfer_gather,
};
+#endif
mixer_driver_t mixer = {
- .bus = &spi_bus_rffc5071,
-};
-#elif defined(RAD1O)
-mixer_driver_t mixer = {};
+#ifdef IS_NOT_RAD1O
+ .rffc5071.bus = &spi_bus_rffc5071,
#endif
+};
void mixer_bus_setup(mixer_driver_t* const mixer)
{
(void) mixer;
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
- const platform_gpio_t* gpio = platform_gpio();
- rffc5071_spi_config = (rffc5071_spi_config_t){
- .gpio_select = gpio->rffc5072_select,
- .gpio_clock = gpio->rffc5072_clock,
- .gpio_data = gpio->rffc5072_data,
- };
- spi_bus_start(&spi_bus_rffc5071, &rffc5071_spi_config);
+#ifdef IS_NOT_RAD1O
+ if (IS_NOT_RAD1O) {
+ const platform_gpio_t* gpio = platform_gpio();
+
+ rffc5071_spi_config = (rffc5071_spi_config_t){
+ .gpio_select = gpio->rffc5072_select,
+ .gpio_clock = gpio->rffc5072_clock,
+ .gpio_data = gpio->rffc5072_data,
+ };
+ spi_bus_start(&spi_bus_rffc5071, &rffc5071_spi_config);
+ }
#endif
}
-void mixer_setup(mixer_driver_t* const mixer)
+void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type)
{
+ mixer->type = type;
+
const platform_gpio_t* gpio = platform_gpio();
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
- mixer->gpio_reset = gpio->rffc5072_reset;
- #if defined(PRALINE)
- mixer->gpio_ld = gpio->rffc5072_ld;
- #endif
- rffc5071_setup(mixer);
-#elif defined(RAD1O)
- mixer->gpio_vco_ce = gpio->vco_ce;
- mixer->gpio_vco_sclk = gpio->vco_sclk;
- mixer->gpio_vco_sdata = gpio->vco_sdata;
- mixer->gpio_vco_le = gpio->vco_le;
- mixer->gpio_synt_rfout_en = gpio->synt_rfout_en;
- mixer->gpio_vco_mux = gpio->vco_mux;
- max2871_setup(mixer);
+ /* Mixer GPIO serial interface PinMux */
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ mixer->rffc5071.gpio_ld = gpio->rffc5072_ld;
+ }
+#endif
+
+#ifdef IS_NOT_RAD1O
+ if (IS_NOT_RAD1O) {
+ mixer->rffc5071.gpio_reset = gpio->rffc5072_reset;
+ rffc5071_setup(&mixer->rffc5071);
+ }
+#endif
+
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ mixer->max2871.gpio_vco_ce = gpio->vco_ce;
+ mixer->max2871.gpio_vco_sclk = gpio->vco_sclk;
+ mixer->max2871.gpio_vco_sdata = gpio->vco_sdata;
+ mixer->max2871.gpio_vco_le = gpio->vco_le;
+ mixer->max2871.gpio_synt_rfout_en = gpio->synt_rfout_en;
+ mixer->max2871.gpio_vco_mux = gpio->vco_mux;
+ max2871_setup(&mixer->max2871);
+ }
#endif
}
-uint64_t mixer_set_frequency(mixer_driver_t* const mixer, uint64_t hz)
+fp_40_24_t mixer_set_frequency(mixer_driver_t* const mixer, fp_40_24_t lo, bool program)
{
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
- return rffc5071_set_frequency(mixer, hz);
-#elif defined(RAD1O)
- return max2871_set_frequency(mixer, hz / 1000000);
+#ifdef IS_NOT_RAD1O
+ if (IS_NOT_RAD1O) {
+ return rffc5071_set_frequency(&mixer->rffc5071, lo, program);
+ }
+#endif
+
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ return max2871_set_frequency(&mixer->max2871, lo, program);
+ }
#endif
}
void mixer_enable(mixer_driver_t* const mixer)
{
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
- rffc5071_enable(mixer);
-#elif defined(RAD1O)
- max2871_enable(mixer);
+#ifdef IS_NOT_RAD1O
+ if (IS_NOT_RAD1O) {
+ rffc5071_enable(&mixer->rffc5071);
+ }
+#endif
+
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ max2871_enable(&mixer->max2871);
+ }
#endif
}
void mixer_disable(mixer_driver_t* const mixer)
{
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
- rffc5071_disable(mixer);
-#elif defined(RAD1O)
- max2871_disable(mixer);
+#ifdef IS_NOT_RAD1O
+ if (IS_NOT_RAD1O) {
+ rffc5071_disable(&mixer->rffc5071);
+ }
+#endif
+
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ max2871_disable(&mixer->max2871);
+ }
#endif
}
void mixer_set_gpo(mixer_driver_t* const mixer, uint8_t gpo)
{
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
- rffc5071_set_gpo(mixer, gpo);
-#elif defined(RAD1O)
- (void) mixer;
- (void) gpo;
+#ifdef IS_NOT_RAD1O
+ if (IS_NOT_RAD1O) {
+ rffc5071_set_gpo(&mixer->rffc5071, gpo);
+ }
+#endif
+
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ (void) mixer;
+ (void) gpo;
+ }
#endif
}
diff --git a/firmware/common/mixer.h b/firmware/common/mixer.h
index bfd638bb..15afe35e 100644
--- a/firmware/common/mixer.h
+++ b/firmware/common/mixer.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2014 Jared Boone
*
* This file is part of HackRF.
@@ -20,26 +20,49 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __MIXER_H
-#define __MIXER_H
+#pragma once
-#if defined(JAWBREAKER) || defined(HACKRF_ONE) || defined(PRALINE)
- #include "rffc5071.h"
-typedef rffc5071_driver_t mixer_driver_t;
-#elif defined(RAD1O)
+#include
+#include
+
+#include "fixed_point.h"
+#ifdef IS_RAD1O
#include "max2871.h"
-typedef max2871_driver_t mixer_driver_t;
+#endif
+#ifdef IS_NOT_RAD1O
+ #include "rffc5071.h"
#endif
-#include
+typedef enum {
+ RFFC5071_VARIANT,
+ MAX2871_VARIANT,
+} mixer_variant_t;
+
+typedef struct {
+ mixer_variant_t type;
+
+ union {
+#ifdef IS_RAD1O
+ max2871_driver_t max2871;
+#endif
+#ifdef IS_NOT_RAD1O
+ rffc5071_driver_t rffc5071;
+#endif
+ };
+} mixer_driver_t;
+
extern void mixer_bus_setup(mixer_driver_t* const mixer);
-extern void mixer_setup(mixer_driver_t* const mixer);
+extern void mixer_setup(mixer_driver_t* const mixer, mixer_variant_t type);
/* Set frequency (Hz). */
-extern uint64_t mixer_set_frequency(mixer_driver_t* const mixer, uint64_t hz);
+extern fp_40_24_t mixer_set_frequency(
+ mixer_driver_t* const mixer,
+ fp_40_24_t lo,
+ bool program);
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);
-#endif // __MIXER_H
+/* Mixer instance. */
+extern mixer_driver_t mixer;
diff --git a/firmware/common/operacake.c b/firmware/common/operacake.c
index 61d78512..bce51b76 100644
--- a/firmware/common/operacake.c
+++ b/firmware/common/operacake.c
@@ -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"
diff --git a/firmware/common/operacake.h b/firmware/common/operacake.h
index 39da6ef0..b604c513 100644
--- a/firmware/common/operacake.h
+++ b/firmware/common/operacake.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __OPERACAKE_H
-#define __OPERACAKE_H
+#pragma once
#ifdef __cplusplus
extern "C" {
@@ -57,5 +56,3 @@ uint16_t gpio_test(uint8_t address);
#ifdef __cplusplus
}
#endif
-
-#endif /* __OPERACAKE_H */
diff --git a/firmware/common/operacake_sctimer.c b/firmware/common/operacake_sctimer.c
index 9ae37567..c5ec8a3b 100644
--- a/firmware/common/operacake_sctimer.c
+++ b/firmware/common/operacake_sctimer.c
@@ -25,14 +25,16 @@
#include
#include
#include
-#if !defined(PRALINE)
+
+#include "delay.h"
+#include "platform_detect.h"
+#include "platform_scu.h"
+#include "sct.h"
+#ifdef IS_NOT_PRALINE
#include
#include
#endif
-#include "delay.h"
-#include "sct.h"
-
#define U1CTRL_SET SCT_OUT14_SET
#define U1CTRL_CLR SCT_OUT14_CLR
#define U2CTRL0_SET SCT_OUT13_SET
@@ -60,6 +62,8 @@ 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;
@@ -71,48 +75,45 @@ 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(8);
+ delay_us(1);
// Pin definitions for the HackRF
// U2CTRL0
- scu_pinmux(
- P7_4,
- SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
+ scu_pinmux(scu->CTOUT_13, scu->CTOUT_PINCFG);
// U2CTRL1
- scu_pinmux(
- P7_5,
- SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
+ scu_pinmux(scu->CTOUT_12, scu->CTOUT_PINCFG);
// U3CTRL0
- scu_pinmux(
- P7_6,
- SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
+ scu_pinmux(scu->CTOUT_11, scu->CTOUT_PINCFG);
// U3CTRL1
- scu_pinmux(
- P7_7,
- SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
+ scu_pinmux(scu->CTOUT_8, scu->CTOUT_PINCFG);
// U1CTRL
- scu_pinmux(
- P7_0,
- SCU_CONF_EPUN_DIS_PULLUP | SCU_CONF_EHS_FAST | SCU_CONF_FUNCTION1);
+ scu_pinmux(scu->CTOUT_14, scu->CTOUT_PINCFG);
-#ifndef PRALINE
- // Configure the SGPIO to output the clock (f=2 * sample clock) on pin 12
- SGPIO_OUT_MUX_CFG12 = SGPIO_OUT_MUX_CFG_P_OUT_CFG(0x08) | // clkout output mode
- SGPIO_OUT_MUX_CFG_P_OE_CFG(0); // gpio_oe
- SGPIO_GPIO_OENREG |= BIT12;
+ uint8_t sct_clock_input;
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ // Configure the SGPIO to output the clock (f=2 * sample clock) on pin 12
+ SGPIO_OUT_MUX_CFG12 =
+ SGPIO_OUT_MUX_CFG_P_OUT_CFG(0x08) | // clkout output mode
+ SGPIO_OUT_MUX_CFG_P_OE_CFG(0); // gpio_oe
+ SGPIO_GPIO_OENREG |= BIT12;
- // Use the GIMA to connect the SGPIO clock to the SCTimer
- GIMA_CTIN_1_IN = 0x2 << 4; // Route SGPIO12 to SCTIN1
+ // Use the GIMA to connect the SGPIO clock to the SCTimer
+ GIMA_CTIN_1_IN = 0x2 << 4; // Route SGPIO12 to SCTIN1
- uint8_t sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_1;
-#else
- // Configure pin P6_4 as SCT_IN_6
- scu_pinmux(P6_4, SCU_CLK_IN | SCU_CONF_FUNCTION1);
+ sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_1;
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ // Configure pin P6_4 as CTIN_6
+ scu_pinmux(scu->SCT_CLK, scu->SCT_CLK_PINCFG);
- // Use the GIMA to connect MS0/CLK1 (SCT_CLK) on pin P6_4 to the SCTimer
- GIMA_CTIN_6_IN = 0x0 << 4;
+ // Use the GIMA to connect MS0/CLK1 (SCT_CLK) on pin P6_4 to the SCTimer
+ GIMA_CTIN_6_IN = 0x0 << 4;
- uint8_t sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_6;
+ sct_clock_input = SCT_CONFIG_CKSEL_RISING_EDGES_ON_INPUT_6;
+ }
#endif
// We configure this register first, because the user manual says to
diff --git a/firmware/common/operacake_sctimer.h b/firmware/common/operacake_sctimer.h
index 805b3cf9..04c86e92 100644
--- a/firmware/common/operacake_sctimer.h
+++ b/firmware/common/operacake_sctimer.h
@@ -21,8 +21,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __OPERACAKE_SCTIMER_H
-#define __OPERACAKE_SCTIMER_H
+#pragma once
#include
#include
@@ -37,5 +36,3 @@ void operacake_sctimer_enable(bool enable);
void operacake_sctimer_set_dwell_times(struct operacake_dwell_times* times, int n);
void operacake_sctimer_stop(void);
void operacake_sctimer_reset_state(void);
-
-#endif /* __OPERACAKE_SCTIMER_H */
diff --git a/firmware/common/picoprintf.c b/firmware/common/picoprintf.c
new file mode 100644
index 00000000..1959edb5
--- /dev/null
+++ b/firmware/common/picoprintf.c
@@ -0,0 +1,433 @@
+/* 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 // llabs()
+
+#ifdef PICOFORMAT_HANDLE_FLOATS
+ #include // 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;
+}
diff --git a/firmware/common/picoprintf.h b/firmware/common/picoprintf.h
new file mode 100644
index 00000000..313ee91d
--- /dev/null
+++ b/firmware/common/picoprintf.h
@@ -0,0 +1,62 @@
+/*
+ * 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 // size_t
+#include // 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
diff --git a/firmware/common/pins.c b/firmware/common/pins.c
new file mode 100644
index 00000000..51cfec0f
--- /dev/null
+++ b/firmware/common/pins.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2012-2026 Great Scott Gadgets
+ * Copyright 2012 Jared Boone
+ * Copyright 2013 Benjamin Vernoux
+ *
+ * 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
+
+#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);
+}
diff --git a/firmware/common/pins.h b/firmware/common/pins.h
new file mode 100644
index 00000000..bc109e5b
--- /dev/null
+++ b/firmware/common/pins.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012-2026 Great Scott Gadgets
+ * Copyright 2012 Benjamin Vernoux
+ * Copyright 2012 Jared Boone
+ *
+ * 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
diff --git a/firmware/common/platform_detect.c b/firmware/common/platform_detect.c
index b3903035..475ad1e1 100644
--- a/firmware/common/platform_detect.c
+++ b/firmware/common/platform_detect.c
@@ -26,7 +26,7 @@
#include "firmware_info.h"
#include "gpio.h"
#include "gpio_lpc.h"
-#include "hackrf_core.h"
+#include "leds.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_at_mhz(4, 96);
+ delay_us(4);
/* 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_at_mhz(4, 96);
+ delay_us(4);
/* 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_at_mhz(4, 96);
+ delay_us(4);
/* 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_at_mhz(4, 96);
+ delay_us(4);
/* 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(3000000);
+ halt_and_flash(500);
}
platform = BOARD_ID_JAWBREAKER;
return;
case RAD1O_RESISTORS:
if (!(supported_platform() & PLATFORM_RAD1O)) {
- halt_and_flash(3000000);
+ halt_and_flash(500);
}
platform = BOARD_ID_RAD1O;
return;
case HACKRF1_OG_RESISTORS:
if (!(supported_platform() & PLATFORM_HACKRF1_OG)) {
- halt_and_flash(3000000);
+ halt_and_flash(500);
}
platform = BOARD_ID_HACKRF1_OG;
break;
case HACKRF1_R9_RESISTORS:
if (!(supported_platform() & PLATFORM_HACKRF1_R9)) {
- halt_and_flash(3000000);
+ halt_and_flash(500);
}
platform = BOARD_ID_HACKRF1_R9;
break;
case PRALINE_RESISTORS:
if (!(supported_platform() & PLATFORM_PRALINE)) {
- halt_and_flash(3000000);
+ halt_and_flash(500);
}
platform = BOARD_ID_PRALINE;
break;
default:
platform = BOARD_ID_UNRECOGNIZED;
- halt_and_flash(1000000);
+ halt_and_flash(150);
}
uint32_t adc0_3 = check_pin_strap(3);
diff --git a/firmware/common/platform_detect.h b/firmware/common/platform_detect.h
index f02e733b..4f0f0f43 100644
--- a/firmware/common/platform_detect.h
+++ b/firmware/common/platform_detect.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __PLATFORM_DETECT_H__
-#define __PLATFORM_DETECT_H__
+#pragma once
#include
@@ -76,5 +75,3 @@ void detect_hardware_platform(void);
board_id_t detected_platform(void);
board_rev_t detected_revision(void);
uint32_t supported_platform(void);
-
-#endif //__PLATFORM_DETECT_H__
diff --git a/firmware/common/platform_gpio.c b/firmware/common/platform_gpio.c
index df07fc2a..eeecd90e 100644
--- a/firmware/common/platform_gpio.c
+++ b/firmware/common/platform_gpio.c
@@ -23,9 +23,7 @@
#include
-#if defined(HACKRF_ONE)
- #include "platform_detect.h"
-#endif
+#include "platform_detect.h"
// clang-format off
@@ -42,184 +40,250 @@ const platform_gpio_t* platform_gpio(void)
gpio.led[0] = &GPIO2_1;
gpio.led[1] = &GPIO2_2;
gpio.led[2] = &GPIO2_8;
-#if defined(RAD1O)
- gpio.led[3] = &GPIO5_26;
-#elif defined(PRALINE)
- gpio.led[3] = &GPIO4_6;
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ gpio.led[3] = &GPIO5_26;
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ gpio.led[3] = &GPIO4_6;
+ }
#endif
/* Power Supply Control */
-#if defined(PRALINE)
- gpio.gpio_1v2_enable = &GPIO4_7;
- gpio.gpio_3v3aux_enable_n = &GPIO5_15;
-#else
- gpio.gpio_1v8_enable = &GPIO3_6;
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ gpio.gpio_1v2_enable = &GPIO4_7;
+ gpio.gpio_3v3aux_enable_n = &GPIO5_15;
+ }
+#endif
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ gpio.gpio_1v8_enable = &GPIO3_6;
+ }
#endif
/* MAX283x GPIO (XCVR_CTL / CS_XCVR) PinMux */
-#if defined(PRALINE)
- gpio.max283x_select = &GPIO6_28;
- gpio.max283x_enable = &GPIO7_1;
- gpio.max283x_rx_enable = &GPIO7_2;
- gpio.max2831_rxhp = &GPIO6_29;
- gpio.max2831_ld = &GPIO4_11;
-#else
- gpio.max283x_select = &GPIO0_15;
- gpio.max283x_enable = &GPIO2_6;
- gpio.max283x_rx_enable = &GPIO2_5;
- gpio.max283x_tx_enable = &GPIO2_4;
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ gpio.max283x_select = &GPIO6_28;
+ gpio.max283x_enable = &GPIO7_1;
+ gpio.max283x_rx_enable = &GPIO7_2;
+ gpio.max2831_rxhp = &GPIO6_29;
+ gpio.max2831_ld = &GPIO4_11;
+ }
+#endif
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ gpio.max283x_select = &GPIO0_15;
+ gpio.max283x_enable = &GPIO2_6;
+ gpio.max283x_rx_enable = &GPIO2_5;
+ gpio.max283x_tx_enable = &GPIO2_4;
+ }
#endif
/* MAX5864 SPI chip select (AD_CS / CS_AD) GPIO PinMux */
-#if defined(PRALINE)
- gpio.max5864_select = &GPIO6_30;
-#else
- gpio.max5864_select = &GPIO2_7;
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ gpio.max5864_select = &GPIO6_30;
+ }
+#endif
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ gpio.max5864_select = &GPIO2_7;
+ }
#endif
/* RF supply (VAA) control */
-#if defined(HACKRF_ONE)
- gpio.vaa_disable = &GPIO2_9;
-#elif defined(PRALINE)
- gpio.vaa_disable = &GPIO4_1;
-#elif defined(RAD1O)
- gpio.vaa_enable = &GPIO2_9;
+#ifdef IS_HACKRF_ONE
+ if (IS_HACKRF_ONE) {
+ gpio.vaa_disable = &GPIO2_9;
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ gpio.vaa_disable = &GPIO4_1;
+ }
+#endif
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ gpio.vaa_enable = &GPIO2_9;
+ }
#endif
/* W25Q80BV Flash */
- gpio.w25q80bv_hold = &GPIO1_14;
- gpio.w25q80bv_wp = &GPIO1_15;
- gpio.w25q80bv_select = &GPIO5_11;
+ gpio.w25q80bv_hold = &GPIO1_14;
+ gpio.w25q80bv_wp = &GPIO1_15;
+ gpio.w25q80bv_select = &GPIO5_11;
/* RF switch control */
-#if defined(HACKRF_ONE)
- gpio.hp = &GPIO2_0;
- gpio.lp = &GPIO2_10;
- gpio.tx_mix_bp = &GPIO2_11;
- gpio.no_mix_bypass = &GPIO1_0;
- gpio.rx_mix_bp = &GPIO2_12;
- gpio.tx_amp = &GPIO2_15;
- gpio.tx = &GPIO5_15;
- gpio.mix_bypass = &GPIO5_16;
- gpio.rx = &GPIO5_5;
- gpio.no_tx_amp_pwr = &GPIO3_5;
- gpio.amp_bypass = &GPIO0_14;
- gpio.rx_amp = &GPIO1_11;
- gpio.no_rx_amp_pwr = &GPIO1_12;
- // HackRF One rev 9
- gpio.h1r9_rx = &GPIO0_7;
- gpio.h1r9_no_ant_pwr = &GPIO2_4;
-#elif defined(RAD1O)
- gpio.tx_rx_n = &GPIO1_11;
- gpio.tx_rx = &GPIO0_14;
- gpio.by_mix = &GPIO1_12;
- gpio.by_mix_n = &GPIO2_10;
- gpio.by_amp = &GPIO1_0;
- gpio.by_amp_n = &GPIO5_5;
- gpio.mixer_en = &GPIO5_16;
- gpio.low_high_filt = &GPIO2_11;
- gpio.low_high_filt_n = &GPIO2_12;
- gpio.tx_amp = &GPIO2_15;
- gpio.rx_lna = &GPIO5_15;
-#elif defined(PRALINE)
- gpio.tx_en = &GPIO3_4;
- gpio.mix_en_n = &GPIO3_2;
- gpio.mix_en_n_r1_0 = &GPIO5_6;
- gpio.lpf_en = &GPIO4_8;
- gpio.rf_amp_en = &GPIO4_9;
- gpio.ant_bias_en_n = &GPIO1_12;
+#ifdef IS_HACKRF_ONE
+ if (IS_HACKRF_ONE) {
+ gpio.hp = &GPIO2_0;
+ gpio.lp = &GPIO2_10;
+ gpio.tx_mix_bp = &GPIO2_11;
+ gpio.no_mix_bypass = &GPIO1_0;
+ gpio.rx_mix_bp = &GPIO2_12;
+ gpio.tx_amp = &GPIO2_15;
+ gpio.tx = &GPIO5_15;
+ gpio.mix_bypass = &GPIO5_16;
+ gpio.rx = &GPIO5_5;
+ gpio.no_tx_amp_pwr = &GPIO3_5;
+ gpio.amp_bypass = &GPIO0_14;
+ gpio.rx_amp = &GPIO1_11;
+ gpio.no_rx_amp_pwr = &GPIO1_12;
+#ifdef IS_H1_R9
+ if (IS_H1_R9) {
+ gpio.h1r9_rx = &GPIO0_7;
+ gpio.h1r9_no_ant_pwr = &GPIO2_4;
+ }
+#endif
+ }
+#endif
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ gpio.tx_rx_n = &GPIO1_11;
+ gpio.tx_rx = &GPIO0_14;
+ gpio.by_mix = &GPIO1_12;
+ gpio.by_mix_n = &GPIO2_10;
+ gpio.by_amp = &GPIO1_0;
+ gpio.by_amp_n = &GPIO5_5;
+ gpio.mixer_en = &GPIO5_16;
+ gpio.low_high_filt = &GPIO2_11;
+ gpio.low_high_filt_n = &GPIO2_12;
+ gpio.tx_amp = &GPIO2_15;
+ gpio.rx_lna = &GPIO5_15;
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ gpio.tx_en = &GPIO3_4;
+ gpio.mix_en_n = &GPIO3_2;
+ gpio.mix_en_n_r1_0 = &GPIO5_6;
+ gpio.lpf_en = &GPIO4_8;
+ gpio.rf_amp_en = &GPIO4_9;
+ gpio.ant_bias_en_n = &GPIO1_12;
+ }
#endif
/* CPLD JTAG interface GPIO pins_FPGA config pins in Praline */
- gpio.cpld_tck = &GPIO3_0;
-#if defined(PRALINE)
- gpio.fpga_cfg_creset = &GPIO2_11;
- gpio.fpga_cfg_cdone = &GPIO5_14;
- gpio.fpga_cfg_spi_cs = &GPIO2_10;
+ gpio.cpld_tck = &GPIO3_0;
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ gpio.fpga_cfg_creset = &GPIO2_11;
+ gpio.fpga_cfg_cdone = &GPIO5_14;
+ gpio.fpga_cfg_spi_cs = &GPIO2_10;
+ }
#endif
-#if defined(HACKRF_ONE) || defined(RAD1O)
- gpio.cpld_tdo = &GPIO5_18;
- gpio.cpld_tms = &GPIO3_4;
- gpio.cpld_tdi = &GPIO3_1;
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ gpio.cpld_tdo = &GPIO5_18;
+ }
#endif
-#if defined(HACKRF_ONE) || defined(PRALINE)
- gpio.cpld_pp_tms = &GPIO1_1;
- gpio.cpld_pp_tdo = &GPIO1_8;
+#ifdef IS_H1_OR_RAD1O
+ if (IS_H1_OR_RAD1O) {
+ gpio.cpld_tms = &GPIO3_4;
+ gpio.cpld_tdi = &GPIO3_1;
+ }
+#endif
+#ifdef IS_JAWBREAKER
+ if (IS_JAWBREAKER) {
+ gpio.cpld_tms = &GPIO3_1;
+ gpio.cpld_tdi = &GPIO3_4;
+ }
+#endif
+#ifdef IS_EXPANSION_COMPATIBLE
+ if (IS_EXPANSION_COMPATIBLE) {
+ gpio.cpld_pp_tms = &GPIO1_1;
+ gpio.cpld_pp_tdo = &GPIO1_8;
+ }
#endif
/* Other CPLD interface GPIO pins */
-#if !defined(PRALINE)
- gpio.trigger_enable = &GPIO5_12;
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ gpio.trigger_enable = &GPIO5_12;
+ }
#endif
- gpio.q_invert = &GPIO0_13;
+ gpio.q_invert = &GPIO0_13;
/* RFFC5071 GPIO serial interface PinMux */
-#if defined(JAWBREAKER) || defined(HACKRF_ONE)
- gpio.rffc5072_select = &GPIO2_13;
- gpio.rffc5072_clock = &GPIO5_6;
- gpio.rffc5072_data = &GPIO3_3;
- gpio.rffc5072_reset = &GPIO2_14;
-#elif defined(RAD1O)
- gpio.vco_ce = &GPIO2_13;
- gpio.vco_sclk = &GPIO5_6;
- gpio.vco_sdata = &GPIO3_3;
- gpio.vco_le = &GPIO2_14;
- gpio.vco_mux = &GPIO5_25;
- gpio.synt_rfout_en = &GPIO3_5;
-#elif defined(PRALINE)
- gpio.rffc5072_select = &GPIO2_13;
- gpio.rffc5072_clock = &GPIO5_18;
- gpio.rffc5072_data = &GPIO4_14;
- gpio.rffc5072_reset = &GPIO2_14;
- gpio.rffc5072_ld = &GPIO6_25;
+#ifdef IS_NOT_RAD1O
+ if (IS_NOT_RAD1O) {
+ gpio.rffc5072_select = &GPIO2_13;
+ gpio.rffc5072_clock = &GPIO5_6;
+ gpio.rffc5072_data = &GPIO3_3;
+ gpio.rffc5072_reset = &GPIO2_14;
+ }
+#endif
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ gpio.vco_ce = &GPIO2_13;
+ gpio.vco_sclk = &GPIO5_6;
+ gpio.vco_sdata = &GPIO3_3;
+ gpio.vco_le = &GPIO2_14;
+ gpio.vco_mux = &GPIO5_25;
+ gpio.synt_rfout_en = &GPIO3_5;
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ gpio.rffc5072_select = &GPIO2_13;
+ gpio.rffc5072_clock = &GPIO5_18;
+ gpio.rffc5072_data = &GPIO4_14;
+ gpio.rffc5072_reset = &GPIO2_14;
+ gpio.rffc5072_ld = &GPIO6_25;
+ gpio.rffc5072_enbl = &GPIO4_12;
+ }
#endif
/* Praline */
-#if defined(PRALINE)
- gpio.p2_ctrl0 = &GPIO7_3;
- gpio.p2_ctrl1 = &GPIO7_4;
- gpio.p1_ctrl0 = &GPIO0_14;
- gpio.p1_ctrl1 = &GPIO5_16;
- gpio.p1_ctrl2 = &GPIO3_5;
- gpio.clkin_ctrl = &GPIO0_15;
- gpio.aa_en = &GPIO1_7;
- gpio.trigger_in = &GPIO6_26;
- gpio.trigger_out = &GPIO5_6;
- gpio.pps_out = &GPIO5_5;
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ gpio.p2_ctrl0 = &GPIO7_3;
+ gpio.p2_ctrl1 = &GPIO7_4;
+ gpio.p1_ctrl0 = &GPIO0_14;
+ gpio.p1_ctrl1 = &GPIO5_16;
+ gpio.p1_ctrl2 = &GPIO3_5;
+ gpio.clkin_ctrl = &GPIO0_15;
+ gpio.aa_en = &GPIO1_7;
+ gpio.trigger_in = &GPIO6_26;
+ gpio.trigger_out = &GPIO5_6;
+ gpio.pps_out = &GPIO5_5;
+ }
#endif
- /* HackRF One r9 */
-#if defined(HACKRF_ONE)
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- /* HackRF One r9 clock control */
+ /* HackRF One r9 clock control */
+#ifdef IS_H1_R9
+ if (IS_H1_R9) {
gpio.h1r9_clkin_en = &GPIO5_15;
gpio.h1r9_clkout_en = &GPIO0_9;
gpio.h1r9_mcu_clk_en = &GPIO0_8;
-
- /* HackRF One r9 power control */
- gpio.h1r9_1v8_enable = &GPIO2_9;
- gpio.h1r9_vaa_disable = &GPIO3_6;
-
+ gpio.h1r9_1v8_enable = &GPIO2_9;
+ gpio.h1r9_vaa_disable = &GPIO3_6;
gpio.h1r9_trigger_enable = &GPIO5_5;
}
#endif
- /* RAD1O */
-#if defined(RAD1O)
- gpio.lcd_cs = &GPIO4_12; /* P9_0 */
- gpio.lcd_bl_en = &GPIO0_8; /* P1_1 */
- gpio.lcd_reset = &GPIO5_17; /* P9_4 */
+ /* rad1o LCD */
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ gpio.lcd_cs = &GPIO4_12; /* P9_0 */
+ gpio.lcd_bl_en = &GPIO0_8; /* P1_1 */
+ gpio.lcd_reset = &GPIO5_17; /* P9_4 */
+ }
#endif
/* Portapack */
-#if defined(HACKRF_ONE) || defined(PRALINE)
- gpio.io_stbx = &GPIO5_0; /* P2_0 */
- gpio.addr = &GPIO5_1; /* P2_1 */
- gpio.lcd_te = &GPIO5_3; /* P2_3 */
- gpio.unused = &GPIO5_7; /* P2_8 */
- gpio.lcd_rdx = &GPIO5_4; /* P2_4 */
- gpio.lcd_wrx = &GPIO1_10; /* P2_9 */
- gpio.dir = &GPIO1_13; /* P2_13 */
+#ifdef IS_EXPANSION_COMPATIBLE
+ if (IS_EXPANSION_COMPATIBLE) {
+ gpio.io_stbx = &GPIO5_0; /* P2_0 */
+ gpio.addr = &GPIO5_1; /* P2_1 */
+ gpio.lcd_rdx = &GPIO5_4; /* P2_4 */
+ gpio.lcd_wrx = &GPIO1_10; /* P2_9 */
+ gpio.dir = &GPIO1_13; /* P2_13 */
+ }
#endif
_platform_gpio = &gpio;
diff --git a/firmware/common/platform_gpio.h b/firmware/common/platform_gpio.h
index e327e4ba..c57df96e 100644
--- a/firmware/common/platform_gpio.h
+++ b/firmware/common/platform_gpio.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __PLATFORM_GPIO_H
-#define __PLATFORM_GPIO_H
+#pragma once
#ifdef __cplusplus
extern "C" {
@@ -31,7 +30,7 @@ extern "C" {
typedef struct {
/* LEDs */
-#if defined(PRALINE) || defined(RAD1O)
+#ifdef IS_FOUR_LEDS
gpio_t led[4];
#else
gpio_t led[3];
@@ -39,7 +38,7 @@ typedef struct {
/* Power Supply Control */
gpio_t gpio_1v8_enable;
-#if defined(PRALINE)
+#ifdef IS_PRALINE
gpio_t gpio_1v2_enable;
gpio_t gpio_3v3aux_enable_n;
#endif
@@ -49,7 +48,7 @@ typedef struct {
gpio_t max283x_enable;
gpio_t max283x_rx_enable;
gpio_t max283x_tx_enable;
-#if defined(PRALINE)
+#ifdef IS_PRALINE
gpio_t max2831_rxhp;
gpio_t max2831_ld;
#endif
@@ -59,7 +58,7 @@ typedef struct {
/* RF supply (VAA) control */
gpio_t vaa_disable;
-#if defined(RAD1O)
+#ifdef IS_RAD1O
gpio_t vaa_enable;
#endif
@@ -69,7 +68,7 @@ typedef struct {
gpio_t w25q80bv_select;
/* RF switch control */
-#if defined(HACKRF_ONE)
+#ifdef IS_HACKRF_ONE
gpio_t hp;
gpio_t lp;
gpio_t tx_mix_bp;
@@ -86,7 +85,7 @@ typedef struct {
gpio_t h1r9_no_ant_pwr; // HACKRF_ONE r9
gpio_t h1r9_rx; // HACKRF_ONE r9
#endif
-#if defined(RAD1O)
+#ifdef IS_RAD1O
gpio_t tx_rx_n;
gpio_t tx_rx;
gpio_t by_mix;
@@ -99,7 +98,7 @@ typedef struct {
gpio_t tx_amp;
gpio_t rx_lna;
#endif
-#if defined(PRALINE)
+#ifdef IS_PRALINE
gpio_t tx_en;
gpio_t mix_en_n;
gpio_t mix_en_n_r1_0;
@@ -110,23 +109,23 @@ typedef struct {
/* CPLD JTAG interface GPIO pins, FPGA config pins in Praline */
gpio_t cpld_tck;
-#if defined(HACKRF_ONE) || defined(RAD1O)
+#ifdef IS_NOT_PRALINE
gpio_t cpld_tdo;
gpio_t cpld_tms;
gpio_t cpld_tdi;
#endif
-#if defined(HACKRF_ONE) || defined(PRALINE)
+#ifdef IS_EXPANSION_COMPATIBLE
gpio_t cpld_pp_tms;
gpio_t cpld_pp_tdo;
#endif
-#if defined(PRALINE)
+#ifdef IS_PRALINE
gpio_t fpga_cfg_creset;
gpio_t fpga_cfg_cdone;
gpio_t fpga_cfg_spi_cs;
#endif
/* Other CPLD interface GPIO pins */
-#if !defined(PRALINE)
+#ifdef IS_NOT_PRALINE
gpio_t trigger_enable;
#endif
gpio_t q_invert;
@@ -137,6 +136,7 @@ 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
@@ -145,7 +145,7 @@ typedef struct {
gpio_t synt_rfout_en; // RAD1O
/* Praline */
-#if defined(PRALINE)
+#ifdef IS_PRALINE
gpio_t p2_ctrl0;
gpio_t p2_ctrl1;
gpio_t p1_ctrl0;
@@ -158,8 +158,7 @@ typedef struct {
gpio_t pps_out;
#endif
- /* HackRF One r9 */
-#if defined(HACKRF_ONE)
+#ifdef IS_HACKRF_ONE
/* HackRF One r9 clock control */
gpio_t h1r9_clkin_en;
gpio_t h1r9_clkout_en;
@@ -171,18 +170,16 @@ typedef struct {
#endif
/* RAD1O */
-#if defined(RAD1O)
+#ifdef IS_RAD1O
gpio_t lcd_cs;
gpio_t lcd_bl_en;
gpio_t lcd_reset;
#endif
/* Portapack */
-#if defined(HACKRF_ONE) || defined(PRALINE)
+#ifdef IS_EXPANSION_COMPATIBLE
gpio_t io_stbx;
gpio_t addr;
- __attribute__((unused)) gpio_t lcd_te;
- __attribute__((unused)) gpio_t unused;
gpio_t lcd_rdx;
gpio_t lcd_wrx;
gpio_t dir;
@@ -369,5 +366,3 @@ static const struct gpio GPIO7_25 = GPIO(7, 25);
#ifdef __cplusplus
}
#endif
-
-#endif /* __PLATFORM_GPIO_H */
diff --git a/firmware/common/platform_scu.c b/firmware/common/platform_scu.c
index df611bd2..c9e468e9 100644
--- a/firmware/common/platform_scu.c
+++ b/firmware/common/platform_scu.c
@@ -25,6 +25,8 @@
#include
+#include "platform_detect.h"
+
// clang-format off
const platform_scu_t* platform_scu(void)
@@ -40,37 +42,43 @@ const platform_scu_t* platform_scu(void)
scu.PINMUX_LED1 = SCU_PINMUX_LED1; /* GPIO2[1] on P4_1 */
scu.PINMUX_LED2 = SCU_PINMUX_LED2; /* GPIO2[2] on P4_2 */
scu.PINMUX_LED3 = SCU_PINMUX_LED3; /* GPIO2[8] on P6_12 */
-#if defined(PRALINE)
- scu.PINMUX_LED4 = (P8_6); /* GPIO4[6] on P8_6 */
-#elif defined(RAD1O)
- scu.PINMUX_LED4 = (PB_6); /* GPIO5[26] on PB_6 */
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ scu.PINMUX_LED4 = (PB_6); /* GPIO5[26] on PB_6 */
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.PINMUX_LED4 = (P8_6); /* GPIO4[6] on P8_6 */
+ }
#endif
/* Power Supply PinMux */
- scu.PINMUX_EN1V8 = (P6_10); /* GPIO3[6] on P6_10 */
- scu.PINMUX_EN1V2 = (P8_7); /* GPIO4[7] on P8_7 */
-#if defined(PRALINE)
- scu.PINMUX_EN3V3_AUX_N = (P6_7); /* GPIO5[15] on P6_7 */
- scu.PINMUX_EN3V3_OC_N = (P6_11); /* GPIO3[7] on P6_11 */
+ scu.PINMUX_EN1V8 = (P6_10); /* GPIO3[6] on P6_10 */
+ scu.PINMUX_EN1V2 = (P8_7); /* GPIO4[7] on P8_7 */
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.PINMUX_EN3V3_AUX_N = (P6_7); /* GPIO5[15] on P6_7 */
+ scu.PINMUX_EN3V3_OC_N = (P6_11); /* GPIO3[7] on P6_11 */
+ }
#endif
/* GPIO Input PinMux */
scu.PINMUX_BOOT0 = (P1_1); /* GPIO0[8] on P1_1 */
scu.PINMUX_BOOT1 = (P1_2); /* GPIO0[9] on P1_2 */
-#if !defined(HACKRF_ONE)
- scu.PINMUX_BOOT2 = (P2_8); /* GPIO5[7] on P2_8 */
- scu.PINMUX_BOOT3 = (P2_9); /* GPIO1[10] on P2_9 */
+#ifdef IS_NOT_HACKRF_ONE
+ if (IS_NOT_HACKRF_ONE) {
+ scu.PINMUX_BOOT2 = (P2_8); /* GPIO5[7] on P2_8 */
+ scu.PINMUX_BOOT3 = (P2_9); /* GPIO1[10] on P2_9 */
+ }
#endif
- scu.PINMUX_PP_LCD_TE = (P2_3); /* GPIO5[3] on P2_3 */
- scu.PINMUX_PP_LCD_RDX = (P2_4); /* GPIO5[4] on P2_4 */
- scu.PINMUX_PP_UNUSED = (P2_8); /* GPIO5[7] on P2_8 */
- scu.PINMUX_PP_LCD_WRX = (P2_9); /* GPIO1[10] on P2_9 */
- scu.PINMUX_PP_DIR = (P2_13); /* GPIO1[13] on P2_13 */
/* USB peripheral */
-#if defined(JAWBREAKER)
- scu.PINMUX_USB_LED0 = (P6_8);
- scu.PINMUX_USB_LED1 = (P6_7);
+#ifdef IS_JAWBREAKER
+ if (IS_JAWBREAKER) {
+ scu.PINMUX_USB_LED0 = (P6_8);
+ scu.PINMUX_USB_LED1 = (P6_7);
+ }
#endif
/* SSP1 Peripheral PinMux */
@@ -80,168 +88,218 @@ const platform_scu_t* platform_scu(void)
scu.SSP1_CS = (P1_20); /* P1_20 */
/* CPLD JTAG interface */
- scu.PINMUX_CPLD_TCK = (P6_1); /* GPIO3[ 0] */
-#if defined(PRALINE)
- scu.PINMUX_FPGA_CRESET = (P5_2); /* GPIO2[11] on P5_2 */
- scu.PINMUX_FPGA_CDONE = (P4_10); /* GPIO5[14] */
- scu.PINMUX_FPGA_SPI_CS = (P5_1); /* GPIO2[10] */
-#elif defined(RAD1O) || defined(HACKRF_ONE)
- scu.PINMUX_CPLD_TMS = (P6_5); /* GPIO3[ 4] */
- scu.PINMUX_CPLD_TDI = (P6_2); /* GPIO3[ 1] */
- scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */
-#else
- scu.PINMUX_CPLD_TMS = (P6_2); /* GPIO3[ 1] */
- scu.PINMUX_CPLD_TDI = (P6_5); /* GPIO3[ 4] */
+ scu.PINMUX_CPLD_TCK = (P6_1); /* GPIO3[ 0] */
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.PINMUX_FPGA_CRESET = (P5_2); /* GPIO2[11] on P5_2 */
+ scu.PINMUX_FPGA_CDONE = (P4_10); /* GPIO5[14] */
+ scu.PINMUX_FPGA_SPI_CS = (P5_1); /* GPIO2[10] */
+ }
+#endif
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ scu.PINMUX_CPLD_TDO = (P9_5); /* GPIO5[18] */
+ }
+#endif
+#ifdef IS_H1_OR_RAD1O
+ if (IS_H1_OR_RAD1O) {
+ scu.PINMUX_CPLD_TMS = (P6_5); /* GPIO3[ 4] */
+ scu.PINMUX_CPLD_TDI = (P6_2); /* GPIO3[ 1] */
+ }
+#endif
+#ifdef IS_JAWBREAKER
+ if (IS_JAWBREAKER) {
+ scu.PINMUX_CPLD_TMS = (P6_2); /* GPIO3[ 1] */
+ scu.PINMUX_CPLD_TDI = (P6_5); /* GPIO3[ 4] */
+ }
#endif
/* CPLD SGPIO interface */
-#if defined(PRALINE)
- scu.PINMUX_SGPIO0 = (P0_0);
- scu.PINMUX_SGPIO1 = (P0_1);
- scu.PINMUX_SGPIO2 = (P1_15);
- scu.PINMUX_SGPIO3 = (P1_16);
- scu.PINMUX_SGPIO4 = (P9_4);
- scu.PINMUX_SGPIO5 = (P6_6);
- scu.PINMUX_SGPIO6 = (P2_2);
- scu.PINMUX_SGPIO7 = (P1_0);
- scu.PINMUX_SGPIO8 = (P8_0);
- scu.PINMUX_SGPIO9 = (P9_3);
- scu.PINMUX_SGPIO10 = (P8_2);
- scu.PINMUX_SGPIO11 = (P1_17);
- scu.PINMUX_SGPIO12 = (P1_18);
- scu.PINMUX_SGPIO14 = (P1_18);
- scu.PINMUX_SGPIO15 = (P1_18);
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.PINMUX_SGPIO0 = (P0_0);
+ scu.PINMUX_SGPIO1 = (P0_1);
+ scu.PINMUX_SGPIO2 = (P1_15);
+ scu.PINMUX_SGPIO3 = (P1_16);
+ scu.PINMUX_SGPIO4 = (P9_4);
+ scu.PINMUX_SGPIO5 = (P6_6);
+ scu.PINMUX_SGPIO6 = (P2_2);
+ scu.PINMUX_SGPIO7 = (P1_0);
+ scu.PINMUX_SGPIO8 = (P8_0);
+ scu.PINMUX_SGPIO9 = (P9_3);
+ scu.PINMUX_SGPIO10 = (P8_2);
+ scu.PINMUX_SGPIO11 = (P1_17);
+ scu.PINMUX_SGPIO12 = (P1_18);
+ scu.PINMUX_SGPIO14 = (P1_18);
+ scu.PINMUX_SGPIO15 = (P1_18);
- scu.PINMUX_SGPIO0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
- scu.PINMUX_SGPIO1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
- scu.PINMUX_SGPIO2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
- scu.PINMUX_SGPIO3_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
- scu.PINMUX_SGPIO4_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
- scu.PINMUX_SGPIO5_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
- scu.PINMUX_SGPIO6_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu.PINMUX_SGPIO7_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
- scu.PINMUX_SGPIO8_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu.PINMUX_SGPIO9_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
- scu.PINMUX_SGPIO10_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu.PINMUX_SGPIO11_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
- scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
-#else
- scu.PINMUX_SGPIO0 = (P0_0);
- scu.PINMUX_SGPIO1 = (P0_1);
- scu.PINMUX_SGPIO2 = (P1_15);
- scu.PINMUX_SGPIO3 = (P1_16);
- scu.PINMUX_SGPIO4 = (P6_3);
- scu.PINMUX_SGPIO5 = (P6_6);
- scu.PINMUX_SGPIO6 = (P2_2);
- scu.PINMUX_SGPIO7 = (P1_0);
- scu.PINMUX_SGPIO8 = (P9_6);
- scu.PINMUX_SGPIO9 = (P4_3);
- scu.PINMUX_SGPIO10 = (P1_14);
- scu.PINMUX_SGPIO11 = (P1_17);
- scu.PINMUX_SGPIO12 = (P1_18);
- scu.PINMUX_SGPIO14 = (P4_9);
- scu.PINMUX_SGPIO15 = (P4_10);
+ scu.PINMUX_SGPIO0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
+ scu.PINMUX_SGPIO1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
+ scu.PINMUX_SGPIO2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
+ scu.PINMUX_SGPIO3_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
+ scu.PINMUX_SGPIO4_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
+ scu.PINMUX_SGPIO5_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
+ scu.PINMUX_SGPIO6_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu.PINMUX_SGPIO7_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
+ scu.PINMUX_SGPIO8_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.PINMUX_SGPIO9_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
+ scu.PINMUX_SGPIO10_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.PINMUX_SGPIO11_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
+ scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ }
+#endif
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ scu.PINMUX_SGPIO0 = (P0_0);
+ scu.PINMUX_SGPIO1 = (P0_1);
+ scu.PINMUX_SGPIO2 = (P1_15);
+ scu.PINMUX_SGPIO3 = (P1_16);
+ scu.PINMUX_SGPIO4 = (P6_3);
+ scu.PINMUX_SGPIO5 = (P6_6);
+ scu.PINMUX_SGPIO6 = (P2_2);
+ scu.PINMUX_SGPIO7 = (P1_0);
+ scu.PINMUX_SGPIO8 = (P9_6);
+ scu.PINMUX_SGPIO9 = (P4_3);
+ scu.PINMUX_SGPIO10 = (P1_14);
+ scu.PINMUX_SGPIO11 = (P1_17);
+ scu.PINMUX_SGPIO12 = (P1_18);
+ scu.PINMUX_SGPIO14 = (P4_9);
+ scu.PINMUX_SGPIO15 = (P4_10);
- scu.PINMUX_SGPIO0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
- scu.PINMUX_SGPIO1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
- scu.PINMUX_SGPIO2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
- scu.PINMUX_SGPIO3_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
- scu.PINMUX_SGPIO4_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
- scu.PINMUX_SGPIO5_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
- scu.PINMUX_SGPIO6_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu.PINMUX_SGPIO7_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
- scu.PINMUX_SGPIO8_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
- scu.PINMUX_SGPIO9_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION7);
- scu.PINMUX_SGPIO10_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
- scu.PINMUX_SGPIO11_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
- scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.PINMUX_SGPIO0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
+ scu.PINMUX_SGPIO1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
+ scu.PINMUX_SGPIO2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
+ scu.PINMUX_SGPIO3_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
+ scu.PINMUX_SGPIO4_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
+ scu.PINMUX_SGPIO5_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
+ scu.PINMUX_SGPIO6_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu.PINMUX_SGPIO7_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
+ scu.PINMUX_SGPIO8_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
+ scu.PINMUX_SGPIO9_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION7);
+ scu.PINMUX_SGPIO10_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
+ scu.PINMUX_SGPIO11_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
+ scu.PINMUX_SGPIO12_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu.PINMUX_SGPIO14_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.PINMUX_SGPIO15_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ }
#endif
scu.TRIGGER_EN = (P4_8); /* GPIO5[12] on P4_8 */
/* MAX283x GPIO (XCVR_CTL) PinMux */
-#if defined(PRALINE)
- scu.XCVR_ENABLE = PE_1; /* GPIO7[1] on PE_1 */
- scu.XCVR_RXENABLE = PE_2; /* GPIO7[2] on PE_2 */
- scu.XCVR_CS = PD_14; /* GPIO6[28] on PD_14 */
- scu.XCVR_RXHP = PD_15; /* GPIO6[29] on PD_15 */
- scu.XCVR_LD = P9_6; /* GPIO4[11] on P9_6 */
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ scu.XCVR_RXHP = P8_1; /* GPIO[] on P8_1 */
+ scu.XCVR_B6 = P8_2; /* GPIO[] on P8_2 */
+ scu.XCVR_B7 = P9_3; /* GPIO[] on P9_3 */
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.XCVR_ENABLE = PE_1; /* GPIO7[1] on PE_1 */
+ scu.XCVR_RXENABLE = PE_2; /* GPIO7[2] on PE_2 */
+ scu.XCVR_CS = PD_14; /* GPIO6[28] on PD_14 */
+ scu.XCVR_RXHP = PD_15; /* GPIO6[29] on PD_15 */
+ scu.XCVR_LD = P9_6; /* GPIO4[11] on P9_6 */
- scu.XCVR_ENABLE_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu.XCVR_RXHP_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu.XCVR_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0 |
- SCU_CONF_EPD_EN_PULLDOWN | SCU_CONF_EPUN_DIS_PULLUP);
-#elif defined(JAWBREAKER) || defined(HACKRF_ONE)
- scu.XCVR_ENABLE = P4_6; /* GPIO2[6] on P4_6 */
- scu.XCVR_RXENABLE = P4_5; /* GPIO2[5] on P4_5 */
- scu.XCVR_TXENABLE = P4_4; /* GPIO2[4] on P4_4 */
- scu.XCVR_CS = P1_20; /* GPIO0[15] on P1_20 */
+ scu.XCVR_ENABLE_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.XCVR_RXHP_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.XCVR_LD_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0 |
+ SCU_CONF_EPD_EN_PULLDOWN | SCU_CONF_EPUN_DIS_PULLUP);
+ }
+#endif
+#ifdef IS_H1_OR_JAWBREAKER
+ if (IS_H1_OR_JAWBREAKER) {
+ scu.XCVR_ENABLE = P4_6; /* GPIO2[6] on P4_6 */
+ scu.XCVR_RXENABLE = P4_5; /* GPIO2[5] on P4_5 */
+ scu.XCVR_TXENABLE = P4_4; /* GPIO2[4] on P4_4 */
+ scu.XCVR_CS = P1_20; /* GPIO0[15] on P1_20 */
- scu.XCVR_ENABLE_PINCFG = (SCU_GPIO_FAST);
- scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST);
- scu.XCVR_TXENABLE_PINCFG = (SCU_GPIO_FAST);
- scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST);
-#elif defined(RAD1O)
- scu.XCVR_RXHP = P8_1; /* GPIO[] on P8_1 */
- scu.XCVR_B6 = P8_2; /* GPIO[] on P8_2 */
- scu.XCVR_B7 = P9_3; /* GPIO[] on P9_3 */
+ scu.XCVR_ENABLE_PINCFG = (SCU_GPIO_FAST);
+ scu.XCVR_RXENABLE_PINCFG = (SCU_GPIO_FAST);
+ scu.XCVR_TXENABLE_PINCFG = (SCU_GPIO_FAST);
+ scu.XCVR_CS_PINCFG = (SCU_GPIO_FAST);
+ }
#endif
/* MAX5864 SPI chip select (AD_CS) GPIO PinMux */
-#if defined(PRALINE)
- scu.AD_CS = (PD_16); /* GPIO6[30] on PD_16 */
- scu.AD_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
-#else
- scu.AD_CS = (P5_7); /* GPIO2[7] on P5_7 */
- scu.AD_CS_PINCFG = (SCU_GPIO_FAST);
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.AD_CS = (PD_16); /* GPIO6[30] on PD_16 */
+ scu.AD_CS_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ }
+#endif
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ scu.AD_CS = (P5_7); /* GPIO2[7] on P5_7 */
+ scu.AD_CS_PINCFG = (SCU_GPIO_FAST);
+ }
#endif
/* RFFC5071 GPIO serial interface PinMux */
-#if defined(PRALINE)
- scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */
- scu.MIXER_SCLK = (P9_5); /* GPIO5[18] on P9_5 */
- 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 */
+#ifdef IS_H1_OR_JAWBREAKER
+ if (IS_H1_OR_JAWBREAKER) {
+ scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */
+ scu.MIXER_SCLK = (P2_6); /* GPIO5[6] on P2_6 */
+ scu.MIXER_SDATA = (P6_4); /* GPIO3[3] on P6_4 */
+ scu.MIXER_RESETX = (P5_5); /* GPIO2[14] on P5_5 */
- 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);
-#elif defined(JAWBREAKER) || defined(HACKRF_ONE)
- scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */
- scu.MIXER_SCLK = (P2_6); /* GPIO5[6] on P2_6 */
- scu.MIXER_SDATA = (P6_4); /* GPIO3[3] on P6_4 */
- scu.MIXER_RESETX = (P5_5); /* GPIO2[14] on P5_5 */
+ scu.MIXER_SCLK_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.MIXER_SDATA_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.MIXER_ENX = (P5_4); /* GPIO2[13] on P5_4 */
+ scu.MIXER_SCLK = (P9_5); /* GPIO5[18] on P9_5 */
+ 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);
-#elif defined(RAD1O)
- scu.VCO_CE = (P5_4); /* GPIO2[13] on P5_4 */
- scu.VCO_SCLK = (P2_6); /* GPIO5[6] on P2_6 */
- scu.VCO_SDATA = (P6_4); /* GPIO3[3] on P6_4 */
- scu.VCO_LE = (P5_5); /* GPIO2[14] on P5_5 */
- scu.VCO_MUX = (PB_5); /* GPIO5[25] on PB_5 */
- scu.MIXER_EN = (P6_8); /* GPIO5[16] on P6_8 */
- scu.SYNT_RFOUT_EN = (P6_9); /* GPIO3[5] on P6_9 */
+ 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
+ if (IS_RAD1O) {
+ scu.VCO_CE = (P5_4); /* GPIO2[13] on P5_4 */
+ scu.VCO_SCLK = (P2_6); /* GPIO5[6] on P2_6 */
+ scu.VCO_SDATA = (P6_4); /* GPIO3[3] on P6_4 */
+ scu.VCO_LE = (P5_5); /* GPIO2[14] on P5_5 */
+ scu.VCO_MUX = (PB_5); /* GPIO5[25] on PB_5 */
+ scu.MIXER_EN = (P6_8); /* GPIO5[16] on P6_8 */
+ scu.SYNT_RFOUT_EN = (P6_9); /* GPIO3[5] on P6_9 */
+ }
#endif
/* RF LDO control */
-#if defined(JAWBREAKER)
- scu.RF_LDO_ENABLE = (P5_0); /* GPIO2[9] on P5_0 */
+#ifdef IS_JAWBREAKER
+ if (IS_JAWBREAKER) {
+ scu.RF_LDO_ENABLE = (P5_0); /* GPIO2[9] on P5_0 */
+ }
#endif
/* RF supply (VAA) control */
-#if defined(PRALINE)
- scu.NO_VAA_ENABLE = P8_1; /* GPIO4[1] on P8_1 */
-#elif defined(HACKRF_ONE)
- scu.NO_VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */
-#elif defined(RAD1O)
- scu.VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */
+#ifdef IS_HACKRF_ONE
+ if (IS_HACKRF_ONE) {
+ scu.NO_VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.NO_VAA_ENABLE = P8_1; /* GPIO4[1] on P8_1 */
+ }
+#endif
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ scu.VAA_ENABLE = P5_0; /* GPIO2[9] on P5_0 */
+ }
#endif
/* SPI flash */
@@ -253,114 +311,144 @@ const platform_scu_t* platform_scu(void)
scu.FLASH_WP = (P3_5); /* GPIO1[15] on P3_5 */
/* RF switch control */
-#if defined(PRALINE)
- scu.TX_EN = P6_5; /* GPIO3[4] on P6_5 */
- scu.MIX_EN_N = P6_3; /* GPIO3[2] on P6_3 */
- scu.MIX_EN_N_R1_0 = P2_6; /* GPIO5[6] on P2_6 */
- scu.LPF_EN = PA_1; /* GPIO4[8] on PA_1 */
- scu.RF_AMP_EN = PA_2; /* GPIO4[9] on PA_2 */
- scu.ANT_BIAS_EN_N = P2_12; /* GPIO1[12] on P2_12 */
- scu.ANT_BIAS_OC_N = P2_11; /* GPIO1[11] on P2_11 */
-#elif defined(HACKRF_ONE)
- scu.HP = P4_0; /* GPIO2[0] on P4_0 */
- scu.LP = P5_1; /* GPIO2[10] on P5_1 */
- scu.TX_MIX_BP = P5_2; /* GPIO2[11] on P5_2 */
- scu.NO_MIX_BYPASS = P1_7; /* GPIO1[0] on P1_7 */
- scu.RX_MIX_BP = P5_3; /* GPIO2[12] on P5_3 */
- scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */
- scu.TX = P6_7; /* GPIO5[15] on P6_7 */
- scu.MIX_BYPASS = P6_8; /* GPIO5[16] on P6_8 */
- scu.RX = P2_5; /* GPIO5[5] on P2_5 */
- scu.NO_TX_AMP_PWR = P6_9; /* GPIO3[5] on P6_9 */
- scu.AMP_BYPASS = P2_10; /* GPIO0[14] on P2_10 */
- scu.RX_AMP = P2_11; /* GPIO1[11] on P2_11 */
- scu.NO_RX_AMP_PWR = P2_12; /* GPIO1[12] on P2_12 */
-#elif defined(RAD1O)
- scu.BY_AMP = P1_7; /* GPIO1[0] on P1_7 */
- scu.BY_AMP_N = P2_5; /* GPIO5[5] on P2_5 */
- scu.TX_RX = P2_10; /* GPIO0[14] on P2_10 */
- scu.TX_RX_N = P2_11; /* GPIO1[11] on P2_11 */
- scu.BY_MIX = P2_12; /* GPIO1[12] on P2_12 */
- scu.BY_MIX_N = P5_1; /* GPIO2[10] on P5_1 */
- scu.LOW_HIGH_FILT = P5_2; /* GPIO2[11] on P5_2 */
- scu.LOW_HIGH_FILT_N = P5_3; /* GPIO2[12] on P5_3 */
- scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */
- scu.RX_LNA = P6_7; /* GPIO5[15] on P6_7 */
+#ifdef IS_HACKRF_ONE
+ if (IS_HACKRF_ONE) {
+ scu.HP = P4_0; /* GPIO2[0] on P4_0 */
+ scu.LP = P5_1; /* GPIO2[10] on P5_1 */
+ scu.TX_MIX_BP = P5_2; /* GPIO2[11] on P5_2 */
+ scu.NO_MIX_BYPASS = P1_7; /* GPIO1[0] on P1_7 */
+ scu.RX_MIX_BP = P5_3; /* GPIO2[12] on P5_3 */
+ scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */
+ scu.TX = P6_7; /* GPIO5[15] on P6_7 */
+ scu.MIX_BYPASS = P6_8; /* GPIO5[16] on P6_8 */
+ scu.RX = P2_5; /* GPIO5[5] on P2_5 */
+ scu.NO_TX_AMP_PWR = P6_9; /* GPIO3[5] on P6_9 */
+ scu.AMP_BYPASS = P2_10; /* GPIO0[14] on P2_10 */
+ scu.RX_AMP = P2_11; /* GPIO1[11] on P2_11 */
+ scu.NO_RX_AMP_PWR = P2_12; /* GPIO1[12] on P2_12 */
+ }
+#endif
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ scu.BY_AMP = P1_7; /* GPIO1[0] on P1_7 */
+ scu.BY_AMP_N = P2_5; /* GPIO5[5] on P2_5 */
+ scu.TX_RX = P2_10; /* GPIO0[14] on P2_10 */
+ scu.TX_RX_N = P2_11; /* GPIO1[11] on P2_11 */
+ scu.BY_MIX = P2_12; /* GPIO1[12] on P2_12 */
+ scu.BY_MIX_N = P5_1; /* GPIO2[10] on P5_1 */
+ scu.LOW_HIGH_FILT = P5_2; /* GPIO2[11] on P5_2 */
+ scu.LOW_HIGH_FILT_N = P5_3; /* GPIO2[12] on P5_3 */
+ scu.TX_AMP = P5_6; /* GPIO2[15] on P5_6 */
+ scu.RX_LNA = P6_7; /* GPIO5[15] on P6_7 */
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.TX_EN = P6_5; /* GPIO3[4] on P6_5 */
+ scu.MIX_EN_N = P6_3; /* GPIO3[2] on P6_3 */
+ scu.MIX_EN_N_R1_0 = P2_6; /* GPIO5[6] on P2_6 */
+ scu.LPF_EN = PA_1; /* GPIO4[8] on PA_1 */
+ scu.RF_AMP_EN = PA_2; /* GPIO4[9] on PA_2 */
+ scu.ANT_BIAS_EN_N = P2_12; /* GPIO1[12] on P2_12 */
+ scu.ANT_BIAS_OC_N = P2_11; /* GPIO1[11] on P2_11 */
+ }
#endif
/* Praline */
-#if defined(PRALINE)
- scu.P2_CTRL0 = (PE_3); /* GPIO7[3] on PE_3 */
- scu.P2_CTRL1 = (PE_4); /* GPIO7[4] on PE_4 */
- scu.P1_CTRL0 = (P2_10); /* GPIO0[14] on P2_10 */
- scu.P1_CTRL1 = (P6_8); /* GPIO5[16] on P6_8 */
- scu.P1_CTRL2 = (P6_9); /* GPIO3[5] on P6_9 */
- scu.CLKIN_CTRL = (P1_20); /* GPIO0[15] on P1_20 */
- scu.AA_EN = (P1_14); /* GPIO1[7] on P1_14 */
- 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 */
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ scu.P2_CTRL0 = (PE_3); /* GPIO7[3] on PE_3 */
+ scu.P2_CTRL1 = (PE_4); /* GPIO7[4] on PE_4 */
+ scu.P1_CTRL0 = (P2_10); /* GPIO0[14] on P2_10 */
+ scu.P1_CTRL1 = (P6_8); /* GPIO5[16] on P6_8 */
+ scu.P1_CTRL2 = (P6_9); /* GPIO3[5] on P6_9 */
+ scu.CLKIN_CTRL = (P1_20); /* GPIO0[15] on P1_20 */
+ scu.AA_EN = (P1_14); /* GPIO1[7] on P1_14 */
+ 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);
- scu.P1_CTRL0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu.P1_CTRL1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu.P1_CTRL2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu.CLKIN_CTRL_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu.AA_EN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- 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.P2_CTRL0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.P2_CTRL1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.P1_CTRL0_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu.P1_CTRL1_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu.P1_CTRL2_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu.CLKIN_CTRL_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu.AA_EN_PINCFG = (SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ 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
/* HackRF One r9 */
-#if defined(HACKRF_ONE)
- scu.H1R9_CLKIN_EN = P6_7; /* GPIO5[15] on P6_7 */
- scu.H1R9_CLKOUT_EN = P1_2; /* GPIO0[9] on P1_2 = has boot pull-down; */
- scu.H1R9_MCU_CLK_EN = P1_1; /* GPIO0[8] on P1_1 = has boot pull-up; */
- scu.H1R9_RX = P2_7; /* GPIO0[7] on P4_4 = has boot pull-up; */
- scu.H1R9_NO_ANT_PWR = P4_4; /* GPIO2[4] on P4_4 */
- scu.H1R9_EN1V8 = P5_0; /* GPIO2[9] on P5_0 */
- scu.H1R9_NO_VAA_EN = P6_10; /* GPIO3[6] on P6_10 */
- scu.H1R9_TRIGGER_EN = P2_5; /* GPIO5[5] on P2_5 */
+#ifdef IS_H1_R9
+ if (IS_H1_R9) {
+ scu.H1R9_CLKIN_EN = P6_7; /* GPIO5[15] on P6_7 */
+ scu.H1R9_CLKOUT_EN = P1_2; /* GPIO0[9] on P1_2 = has boot pull-down; */
+ scu.H1R9_MCU_CLK_EN = P1_1; /* GPIO0[8] on P1_1 = has boot pull-up; */
+ scu.H1R9_RX = P2_7; /* GPIO0[7] on P4_4 = has boot pull-up; */
+ scu.H1R9_NO_ANT_PWR = P4_4; /* GPIO2[4] on P4_4 */
+ scu.H1R9_EN1V8 = P5_0; /* GPIO2[9] on P5_0 */
+ scu.H1R9_NO_VAA_EN = P6_10; /* GPIO3[6] on P6_10 */
+ scu.H1R9_TRIGGER_EN = P2_5; /* GPIO5[5] on P2_5 */
+ }
#endif
- /* Miscellaneous */
- scu.PINMUX_PP_D0 = (P7_0); /* GPIO3[8] */
- scu.PINMUX_PP_D1 = (P7_1); /* GPIO3[9] */
- scu.PINMUX_PP_D2 = (P7_2); /* GPIO3[10] */
- scu.PINMUX_PP_D3 = (P7_3); /* GPIO3[11] */
- scu.PINMUX_PP_D4 = (P7_4); /* GPIO3[12] */
- scu.PINMUX_PP_D5 = (P7_5); /* GPIO3[13] */
- scu.PINMUX_PP_D6 = (P7_6); /* GPIO3[14] */
- scu.PINMUX_PP_D7 = (P7_7); /* GPIO3[15] */
+ /* Expansion headers */
+#ifdef IS_EXPANSION_COMPATIBLE
+ if (IS_EXPANSION_COMPATIBLE) {
+ scu.PINMUX_PP_D0 = (P7_0); /* GPIO3[8] */
+ scu.PINMUX_PP_D1 = (P7_1); /* GPIO3[9] */
+ scu.PINMUX_PP_D2 = (P7_2); /* GPIO3[10] */
+ scu.PINMUX_PP_D3 = (P7_3); /* GPIO3[11] */
+ scu.PINMUX_PP_D4 = (P7_4); /* GPIO3[12] */
+ scu.PINMUX_PP_D5 = (P7_5); /* GPIO3[13] */
+ scu.PINMUX_PP_D6 = (P7_6); /* GPIO3[14] */
+ scu.PINMUX_PP_D7 = (P7_7); /* GPIO3[15] */
- /* TODO add other Pins */
+ scu.PINMUX_PP_LCD_TE = (P2_3); /* GPIO5[3] on P2_3 */
+ scu.PINMUX_PP_LCD_RDX = (P2_4); /* GPIO5[4] on P2_4 */
+ scu.PINMUX_PP_UNUSED = (P2_8); /* GPIO5[7] on P2_8 */
+ scu.PINMUX_PP_LCD_WRX = (P2_9); /* GPIO1[10] on P2_9 */
+ scu.PINMUX_PP_DIR = (P2_13); /* GPIO1[13] on P2_13 */
- scu.PINMUX_GPIO3_8 = (P7_0); /* GPIO3[8] */
- scu.PINMUX_GPIO3_9 = (P7_1); /* GPIO3[9] */
- scu.PINMUX_GPIO3_10 = (P7_2); /* GPIO3[10] */
- scu.PINMUX_GPIO3_11 = (P7_3); /* GPIO3[11] */
- scu.PINMUX_GPIO3_12 = (P7_4); /* GPIO3[12] */
- scu.PINMUX_GPIO3_13 = (P7_5); /* GPIO3[13] */
- scu.PINMUX_GPIO3_14 = (P7_6); /* GPIO3[14] */
- scu.PINMUX_GPIO3_15 = (P7_7); /* GPIO3[15] */
+ scu.PINMUX_PP_TDO = (P1_5); /* GPIO1[8] */
+ scu.PINMUX_SD_POW = (P1_5); /* GPIO1[8] */
+ scu.PINMUX_SD_CMD = (P1_6); /* GPIO1[9] */
+ scu.PINMUX_PP_TMS = (P1_8); /* GPIO1[1] */
+ scu.PINMUX_SD_VOLT0 = (P1_8); /* GPIO1[1] */
+ scu.PINMUX_SD_DAT0 = (P1_9); /* GPIO1[2] */
+ scu.PINMUX_SD_DAT1 = (P1_10); /* GPIO1[3] */
+ scu.PINMUX_SD_DAT2 = (P1_11); /* GPIO1[4] */
+ scu.PINMUX_SD_DAT3 = (P1_12); /* GPIO1[5] */
+ scu.PINMUX_SD_CD = (P1_13); /* GPIO1[6] */
- scu.PINMUX_PP_TDO = (P1_5); /* GPIO1[8] */
- scu.PINMUX_SD_POW = (P1_5); /* GPIO1[8] */
- scu.PINMUX_SD_CMD = (P1_6); /* GPIO1[9] */
- scu.PINMUX_PP_TMS = (P1_8); /* GPIO1[1] */
- scu.PINMUX_SD_VOLT0 = (P1_8); /* GPIO1[1] */
- scu.PINMUX_SD_DAT0 = (P1_9); /* GPIO1[2] */
- scu.PINMUX_SD_DAT1 = (P1_10); /* GPIO1[3] */
- scu.PINMUX_SD_DAT2 = (P1_11); /* GPIO1[4] */
- scu.PINMUX_SD_DAT3 = (P1_12); /* GPIO1[5] */
- scu.PINMUX_SD_CD = (P1_13); /* GPIO1[6] */
+ scu.PINMUX_PP_IO_STBX = (P2_0); /* GPIO5[0] */
+ scu.PINMUX_PP_ADDR = (P2_1); /* GPIO5[1] */
+ scu.PINMUX_U0_TXD = (P2_0); /* GPIO5[0] */
+ scu.PINMUX_U0_RXD = (P2_1); /* GPIO5[1] */
- scu.PINMUX_PP_IO_STBX = (P2_0); /* GPIO5[0] */
- scu.PINMUX_PP_ADDR = (P2_1); /* GPIO5[1] */
- scu.PINMUX_U0_TXD = (P2_0); /* GPIO5[0] */
- scu.PINMUX_U0_RXD = (P2_1); /* GPIO5[1] */
+ scu.PINMUX_GPIO3_8 = (P7_0); /* GPIO3[8] */
+ scu.PINMUX_GPIO3_9 = (P7_1); /* GPIO3[9] */
+ scu.PINMUX_GPIO3_10 = (P7_2); /* GPIO3[10] */
+ scu.PINMUX_GPIO3_11 = (P7_3); /* GPIO3[11] */
+ scu.PINMUX_GPIO3_12 = (P7_4); /* GPIO3[12] */
+ scu.PINMUX_GPIO3_13 = (P7_5); /* GPIO3[13] */
+ scu.PINMUX_GPIO3_14 = (P7_6); /* GPIO3[14] */
+ scu.PINMUX_GPIO3_15 = (P7_7); /* GPIO3[15] */
+ }
+#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] */
diff --git a/firmware/common/platform_scu.h b/firmware/common/platform_scu.h
index 793eefa5..a3702bf7 100644
--- a/firmware/common/platform_scu.h
+++ b/firmware/common/platform_scu.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __PLATFORM_SCU_H
-#define __PLATFORM_SCU_H
+#pragma once
#ifdef __cplusplus
extern "C" {
@@ -35,18 +34,17 @@ extern "C" {
*/
typedef struct {
- /* LED PinMux */
+ /* GPIO Output PinMux */
scu_grp_pin_t PINMUX_LED1;
scu_grp_pin_t PINMUX_LED2;
scu_grp_pin_t PINMUX_LED3;
-#if defined(RAD1O) || defined(PRALINE)
+#ifdef IS_FOUR_LEDS
scu_grp_pin_t PINMUX_LED4;
#endif
- /* Power Supply PinMux */
scu_grp_pin_t PINMUX_EN1V8;
scu_grp_pin_t PINMUX_EN1V2;
-#if defined(PRALINE)
+#ifdef IS_PRALINE
scu_grp_pin_t PINMUX_EN3V3_AUX_N;
scu_grp_pin_t PINMUX_EN3V3_OC_N;
#endif
@@ -54,7 +52,7 @@ typedef struct {
/* GPIO Input PinMux */
scu_grp_pin_t PINMUX_BOOT0;
scu_grp_pin_t PINMUX_BOOT1;
-#if !defined(HACKRF_ONE)
+#ifdef IS_NOT_HACKRF_ONE
scu_grp_pin_t PINMUX_BOOT2;
scu_grp_pin_t PINMUX_BOOT3;
#endif
@@ -65,7 +63,7 @@ typedef struct {
scu_grp_pin_t PINMUX_PP_DIR;
/* USB peripheral */
-#if defined(JAWBREAKER)
+#ifdef IS_JAWBREAKER
scu_grp_pin_t PINMUX_USB_LED0;
scu_grp_pin_t PINMUX_USB_LED1;
#endif
@@ -77,16 +75,15 @@ typedef struct {
scu_grp_pin_t SSP1_CS;
/* CPLD JTAG interface */
-#if defined(PRALINE)
+#ifdef IS_PRALINE
scu_grp_pin_t PINMUX_FPGA_CRESET;
scu_grp_pin_t PINMUX_FPGA_CDONE;
scu_grp_pin_t PINMUX_FPGA_SPI_CS;
-#else
+#endif
scu_grp_pin_t PINMUX_CPLD_TDO;
+ scu_grp_pin_t PINMUX_CPLD_TCK;
scu_grp_pin_t PINMUX_CPLD_TMS;
scu_grp_pin_t PINMUX_CPLD_TDI;
-#endif
- scu_grp_pin_t PINMUX_CPLD_TCK;
/* CPLD SGPIO interface */
scu_grp_pin_t PINMUX_SGPIO0;
@@ -122,36 +119,47 @@ typedef struct {
scu_grp_pin_t TRIGGER_EN;
/* MAX283x GPIO (XCVR_CTL) PinMux */
- scu_grp_pin_t XCVR_RXHP; // RAD1O, PRALINE
- scu_grp_pin_t XCVR_B6; // RAD1O
- scu_grp_pin_t XCVR_B7; // RAD1O
- scu_grp_pin_t XCVR_ENABLE; // PRALINE, HACKRF_ONE
- scu_grp_pin_t XCVR_RXENABLE; // PRALINE, HACKRF_ONE
- scu_grp_pin_t XCVR_CS; // PRALINE, HACKRF_ONE
- scu_grp_pin_t XCVR_LD; // PRALINE
- uint32_t XCVR_ENABLE_PINCFG; // PRALINE, HACKRF_ONE
- uint32_t XCVR_RXENABLE_PINCFG; // PRALINE, HACKRF_ONE
- uint32_t XCVR_CS_PINCFG; // PRALINE, HACKRF_ONE
- uint32_t XCVR_RXHP_PINCFG; // PRALINE
- uint32_t XCVR_LD_PINCFG; // PRALINE
- scu_grp_pin_t XCVR_TXENABLE; // HACKRF_ONE
- uint32_t XCVR_TXENABLE_PINCFG; // HACKRF_ONE
+ scu_grp_pin_t XCVR_ENABLE;
+ scu_grp_pin_t XCVR_RXENABLE;
+ scu_grp_pin_t XCVR_TXENABLE;
+ scu_grp_pin_t XCVR_CS;
+ uint32_t XCVR_ENABLE_PINCFG;
+ uint32_t XCVR_RXENABLE_PINCFG;
+ uint32_t XCVR_TXENABLE_PINCFG;
+ uint32_t XCVR_CS_PINCFG;
+#ifdef IS_PRALINE
+ scu_grp_pin_t XCVR_LD;
+ uint32_t XCVR_LD_PINCFG;
+ uint32_t XCVR_RXHP_PINCFG;
+#endif
+#if defined(IS_RAD1O) || defined(IS_PRALINE)
+ scu_grp_pin_t XCVR_RXHP;
+#endif
+#ifdef IS_RAD1O
+ scu_grp_pin_t XCVR_B6;
+ scu_grp_pin_t XCVR_B7;
+#endif
/* MAX5864 SPI chip select (AD_CS) GPIO PinMux */
scu_grp_pin_t AD_CS;
scu_grp_pin_t AD_CS_PINCFG;
/* RFFC5071 GPIO serial interface PinMux */
+#ifdef IS_NOT_RAD1O
scu_grp_pin_t MIXER_ENX;
scu_grp_pin_t MIXER_SCLK;
scu_grp_pin_t MIXER_SDATA;
scu_grp_pin_t MIXER_RESETX;
uint32_t MIXER_SCLK_PINCFG;
uint32_t MIXER_SDATA_PINCFG;
-#if defined(PRALINE)
+#endif
+#ifdef IS_PRALINE
scu_grp_pin_t MIXER_LD;
+ scu_grp_pin_t MIXER_ENBL;
uint32_t MIXER_LD_PINCFG;
-#elif defined(RAD1O)
+ uint32_t MIXER_ENBL_PINCFG;
+#endif
+#ifdef IS_RAD1O
scu_grp_pin_t VCO_CE;
scu_grp_pin_t VCO_SCLK;
scu_grp_pin_t VCO_SDATA;
@@ -162,14 +170,15 @@ typedef struct {
#endif
/* RF LDO control */
-#if defined(JAWBREAKER)
+#ifdef IS_JAWBREAKER
scu_grp_pin_t RF_LDO_ENABLE;
#endif
/* RF supply (VAA) control */
-#if defined(PRALINE) || defined(HACKRF_ONE)
+#if defined(IS_PRALINE) || defined(IS_HACKRF_ONE)
scu_grp_pin_t NO_VAA_ENABLE;
-#elif defined(RAD1O)
+#endif
+#ifdef IS_RAD1O
scu_grp_pin_t VAA_ENABLE;
#endif
@@ -182,7 +191,7 @@ typedef struct {
scu_grp_pin_t FLASH_WP;
/* RF switch control */
-#if defined(PRALINE)
+#ifdef IS_PRALINE
scu_grp_pin_t TX_EN;
scu_grp_pin_t MIX_EN_N;
scu_grp_pin_t MIX_EN_N_R1_0;
@@ -190,7 +199,8 @@ typedef struct {
scu_grp_pin_t RF_AMP_EN;
scu_grp_pin_t ANT_BIAS_EN_N;
scu_grp_pin_t ANT_BIAS_OC_N;
-#elif defined(HACKRF_ONE)
+#endif
+#ifdef IS_HACKRF_ONE
scu_grp_pin_t HP;
scu_grp_pin_t LP;
scu_grp_pin_t TX_MIX_BP;
@@ -204,7 +214,8 @@ typedef struct {
scu_grp_pin_t AMP_BYPASS;
scu_grp_pin_t RX_AMP;
scu_grp_pin_t NO_RX_AMP_PWR;
-#elif defined(RAD1O)
+#endif
+#ifdef IS_RAD1O
scu_grp_pin_t BY_AMP;
scu_grp_pin_t BY_AMP_N;
scu_grp_pin_t TX_RX;
@@ -218,7 +229,7 @@ typedef struct {
#endif
/* Praline */
-#if defined(PRALINE)
+#ifdef IS_PRALINE
scu_grp_pin_t P2_CTRL0;
scu_grp_pin_t P2_CTRL1;
scu_grp_pin_t P1_CTRL0;
@@ -229,6 +240,7 @@ 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;
@@ -240,10 +252,11 @@ 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 */
-#if defined(HACKRF_ONE)
+#ifdef IS_HACKRF_ONE
scu_grp_pin_t H1R9_CLKIN_EN;
scu_grp_pin_t H1R9_CLKOUT_EN;
scu_grp_pin_t H1R9_MCU_CLK_EN;
@@ -263,9 +276,7 @@ typedef struct {
scu_grp_pin_t PINMUX_PP_D5;
scu_grp_pin_t PINMUX_PP_D6;
scu_grp_pin_t PINMUX_PP_D7;
-
/* TODO add other Pins */
-
scu_grp_pin_t PINMUX_GPIO3_8;
scu_grp_pin_t PINMUX_GPIO3_9;
scu_grp_pin_t PINMUX_GPIO3_10;
@@ -291,6 +302,14 @@ 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;
@@ -307,5 +326,3 @@ const platform_scu_t* platform_scu(void);
#ifdef __cplusplus
}
#endif
-
-#endif /* __PLATFORM_SCU_H */
diff --git a/firmware/common/portapack.c b/firmware/common/portapack.c
index fefa2f11..b84bf03c 100644
--- a/firmware/common/portapack.c
+++ b/firmware/common/portapack.c
@@ -28,17 +28,10 @@
#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 portapack_if_t {
+typedef struct {
gpio_t gpio_dir;
gpio_t gpio_lcd_rdx;
gpio_t gpio_lcd_wrx;
@@ -245,7 +238,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."
- portapack_sleep_milliseconds(120);
+ delay_ms(120);
}
static void portapack_lcd_display_on(void)
@@ -311,11 +304,11 @@ static void portapack_lcd_wake(void)
static void portapack_lcd_reset(void)
{
portapack_lcd_reset_state(false);
- portapack_sleep_milliseconds(1);
+ delay_ms(1);
portapack_lcd_reset_state(true);
- portapack_sleep_milliseconds(10);
+ delay_ms(10);
portapack_lcd_reset_state(false);
- portapack_sleep_milliseconds(120);
+ delay_ms(120);
}
static void portapack_lcd_init(void)
@@ -617,23 +610,23 @@ static bool portapack_detect(void)
return idcode == 0x020A50DD || idcode == 0x00025610;
}
-static const portapack_t portapack_instance = {};
+static bool portapack_detected = false;
-static const portapack_t* portapack_pointer = NULL;
-
-const portapack_t* portapack(void)
+bool portapack_present(void)
{
- return portapack_pointer;
+ return portapack_detected;
}
-void portapack_init(void)
+bool portapack_init(void)
{
if (portapack_detect()) {
portapack_if_init();
portapack_lcd_reset();
portapack_lcd_init();
- portapack_pointer = &portapack_instance;
+ portapack_detected = true;
} else {
- portapack_pointer = NULL;
+ portapack_detected = false;
}
+
+ return portapack_detected;
}
diff --git a/firmware/common/portapack.h b/firmware/common/portapack.h
index d4dda66e..3926bc3f 100644
--- a/firmware/common/portapack.h
+++ b/firmware/common/portapack.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __PORTAPACK_H__
-#define __PORTAPACK_H__
+#pragma once
#include
#include
@@ -29,31 +28,31 @@
#define ARRAY_SIZEOF(x) (sizeof(x) / sizeof(x[0]))
-typedef struct ui_color_t {
+typedef struct {
uint16_t v;
} ui_color_t;
-typedef struct ui_point_t {
+typedef struct {
int16_t x;
int16_t y;
} ui_point_t;
-typedef struct ui_size_t {
+typedef struct {
int16_t width;
int16_t height;
} ui_size_t;
-typedef struct ui_rect_t {
+typedef struct {
ui_point_t point;
ui_size_t size;
} ui_rect_t;
-typedef struct ui_bitmap_t {
+typedef struct {
ui_size_t size;
const uint8_t* const data;
} ui_bitmap_t;
-typedef struct ui_font_t {
+typedef struct {
const ui_size_t glyph_size;
const uint8_t* const data;
char c_start;
@@ -61,18 +60,16 @@ typedef struct ui_font_t {
size_t data_stride;
} ui_font_t;
-typedef struct portapack_t {
+typedef struct {
} portapack_t;
-void portapack_init(void);
+bool portapack_init(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));
+bool portapack_present(void);
void portapack_backlight(const bool on);
-void portapack_reference_oscillator(const bool on) __attribute__((weak));
+void portapack_reference_oscillator(const bool on);
void portapack_fill_rectangle(const ui_rect_t rect, const ui_color_t color);
@@ -85,5 +82,3 @@ void portapack_draw_bitmap(
const ui_color_t background);
ui_bitmap_t portapack_font_glyph(const ui_font_t* const font, const char c);
-
-#endif /*__PORTAPACK_H__*/
diff --git a/firmware/common/power.c b/firmware/common/power.c
new file mode 100644
index 00000000..70c8c0f1
--- /dev/null
+++ b/firmware/common/power.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright 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.
+ */
+
+#include "power.h"
+
+#if defined(IS_HACKRF_ONE)
+ #include
+#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
+}
diff --git a/firmware/common/power.h b/firmware/common/power.h
new file mode 100644
index 00000000..fc8af67a
--- /dev/null
+++ b/firmware/common/power.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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
+
+#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
diff --git a/firmware/common/rad1o/decoder.h b/firmware/common/rad1o/decoder.h
index 5f1bcda4..5575ef93 100644
--- a/firmware/common/rad1o/decoder.h
+++ b/firmware/common/rad1o/decoder.h
@@ -1,8 +1,5 @@
-#ifndef __RAD1O_DECODER_H__
-#define __RAD1O_DECODER_H__
+#pragma once
#include
uint8_t* rad1o_pk_decode(const uint8_t* data, int* len);
-
-#endif
diff --git a/firmware/common/rad1o/display.c b/firmware/common/rad1o/display.c
index 91813aab..c0c090b3 100644
--- a/firmware/common/rad1o/display.c
+++ b/firmware/common/rad1o/display.c
@@ -10,12 +10,6 @@
#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 */
@@ -85,9 +79,9 @@ void rad1o_lcdInit(void)
// Reset the display
gpio_clear(&gpio_lcd_reset);
- delayms(100);
+ delay_ms(100);
gpio_set(&gpio_lcd_reset);
- delayms(100);
+ delay_ms(100);
select();
@@ -115,7 +109,7 @@ void rad1o_lcdInit(void)
(1 << 12) | (0 << 13) | (0 << 14) | 0);
write(0, 0x01); /* most color displays need the pause */
- delayms(10);
+ delay_ms(10);
size_t i = 0;
while (i < sizeof(initseq_d)) {
diff --git a/firmware/common/rad1o/display.h b/firmware/common/rad1o/display.h
index 63711dfc..af5da5e1 100644
--- a/firmware/common/rad1o/display.h
+++ b/firmware/common/rad1o/display.h
@@ -1,5 +1,4 @@
-#ifndef __RAD1O_DISPLAY_H__
-#define __RAD1O_DISPLAY_H__
+#pragma once
#include
@@ -27,5 +26,3 @@ void rad1o_lcdFill(uint8_t f);
void rad1o_lcdDisplay(void);
void rad1o_lcdSetPixel(uint8_t x, uint8_t y, uint8_t f);
uint8_t* rad1o_lcdGetBuffer(void);
-
-#endif
diff --git a/firmware/common/rad1o/draw.h b/firmware/common/rad1o/draw.h
index c8f6708b..dfdaaf68 100644
--- a/firmware/common/rad1o/draw.h
+++ b/firmware/common/rad1o/draw.h
@@ -1,9 +1,6 @@
-#ifndef __RAD1O_DRAW_H__
-#define __RAD1O_DRAW_H__
+#pragma once
#include
void rad1o_drawHLine(uint8_t y, uint8_t x1, uint8_t x2, uint8_t color);
void rad1o_drawVLine(uint8_t x, uint8_t y1, uint8_t y2, uint8_t color);
-
-#endif
diff --git a/firmware/common/rad1o/fonts.h b/firmware/common/rad1o/fonts.h
index 98e0a7ac..bd1ce0b5 100644
--- a/firmware/common/rad1o/fonts.h
+++ b/firmware/common/rad1o/fonts.h
@@ -1,5 +1,4 @@
-#ifndef __RAD1O_FONTS_H__
-#define __RAD1O_FONTS_H__
+#pragma once
#include
@@ -31,5 +30,3 @@ typedef const struct FONT_DEF* FONT;
#define FONT_DEFAULT 0
#define FONT_INTERNAL 1
#define FONT_EXTERNAL 2
-
-#endif
diff --git a/firmware/common/rad1o/print.c b/firmware/common/rad1o/print.c
index d279f4e7..345af5e0 100644
--- a/firmware/common/rad1o/print.c
+++ b/firmware/common/rad1o/print.c
@@ -1,5 +1,11 @@
#include "print.h"
+#include
+#include
+#include
+
+#include
+
#include "display.h"
#include "render.h"
#include "smallfonts.h"
@@ -7,9 +13,17 @@
static int32_t x = 0;
static int32_t y = 0;
-void rad1o_lcdPrint(const char* string)
+void rad1o_lcdPrint(const char* fmt, ...)
{
- x = rad1o_DoString(x, y, 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);
}
void rad1o_lcdNl(void)
diff --git a/firmware/common/rad1o/print.h b/firmware/common/rad1o/print.h
index 19ef1631..858f1b22 100644
--- a/firmware/common/rad1o/print.h
+++ b/firmware/common/rad1o/print.h
@@ -1,13 +1,10 @@
-#ifndef __RAD1O_PRINT_H__
-#define __RAD1O_PRINT_H__
+#pragma once
#include
-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);
void rad1o_lcdSetCrsr(int32_t dx, int32_t dy);
void rad1o_setSystemFont(void);
-
-#endif
diff --git a/firmware/common/rad1o/render.h b/firmware/common/rad1o/render.h
index 1c623381..535ee471 100644
--- a/firmware/common/rad1o/render.h
+++ b/firmware/common/rad1o/render.h
@@ -1,5 +1,4 @@
-#ifndef __RENDER_H_
-#define __RENDER_H_
+#pragma once
#include
@@ -10,4 +9,3 @@ void rad1o_setIntFont(const struct FONT_DEF* font);
int rad1o_getFontHeight(void);
int rad1o_DoString(int sx, int sy, const char* s);
int rad1o_DoChar(int sx, int sy, int c);
-#endif
diff --git a/firmware/common/rad1o/smallfonts.h b/firmware/common/rad1o/smallfonts.h
index af69bf27..3204e62a 100644
--- a/firmware/common/rad1o/smallfonts.h
+++ b/firmware/common/rad1o/smallfonts.h
@@ -35,12 +35,10 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
-#ifndef __RAD1O_SMALLFONTS_H__
-#define __RAD1O_SMALLFONTS_H__
+
+#pragma once
/* Partially based on original code for the KS0108 by Stephane Rey */
/* Current version by Kevin Townsend */
extern const struct FONT_DEF Font_7x8;
-
-#endif
diff --git a/firmware/common/rad1o/ubuntu18.h b/firmware/common/rad1o/ubuntu18.h
index 38b58e76..b8d28b07 100644
--- a/firmware/common/rad1o/ubuntu18.h
+++ b/firmware/common/rad1o/ubuntu18.h
@@ -1,6 +1,3 @@
-#ifndef __RAD1O_UBUNTU18_H__
-#define __RAD1O_UBUNTU18_H__
+#pragma once
extern const struct FONT_DEF Font_Ubuntu18pt;
-
-#endif
diff --git a/firmware/common/radio.c b/firmware/common/radio.c
index cb441a64..36f65cad 100644
--- a/firmware/common/radio.c
+++ b/firmware/common/radio.c
@@ -19,20 +19,23 @@
* Boston, MA 02110-1301, USA.
*/
+#include "radio.h"
+
#include
#include
-#include
+#include "clock_gen.h"
+#include "clock_io.h"
#include "fixed_point.h"
-#include "hackrf_core.h"
-#include "hackrf_ui.h"
#include "max283x.h"
+#include "mixer.h"
#include "platform_detect.h"
-#include "radio.h"
#include "rf_path.h"
+#include "transceiver_mode.h"
#include "tuning.h"
-#if defined(PRALINE)
+#include "u128.h"
+#ifdef IS_PRALINE
#include "fpga.h"
#include "tune_config.h"
#endif
@@ -40,6 +43,9 @@
#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++) {
@@ -48,10 +54,11 @@ void radio_init(radio_t* const radio)
}
}
radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
- radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
+ radio->config[RADIO_BANK_REQUESTED][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;
}
@@ -71,7 +78,7 @@ radio_error_t radio_reg_write(
}
switch (bank) {
- case RADIO_BANK_ACTIVE:
+ case RADIO_BANK_REQUESTED:
mark_dirty(radio, reg);
/* fall through */
case RADIO_BANK_IDLE:
@@ -100,12 +107,12 @@ uint64_t radio_reg_read(
return radio->config[bank][reg];
}
-static bool radio_update_direction(radio_t* const radio, uint64_t* bank)
+static uint32_t radio_update_direction(radio_t* const radio, uint64_t* bank)
{
const uint64_t requested = bank[RADIO_OPMODE];
if (requested == RADIO_UNSET) {
- return false;
+ return 0;
}
rf_path_direction_t direction;
@@ -123,50 +130,132 @@ static bool 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 true;
+ return (1 << RADIO_OPMODE);
}
-#define MAX_AFE_RATE_HZ (40000000UL)
+/* 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)
static inline uint8_t compute_resample_log(
- const uint32_t sample_rate_hz,
+ const fp_28_36_t sample_rate,
const uint64_t requested_n)
{
if (detected_platform() != BOARD_ID_PRALINE) {
return 0;
}
- const uint8_t min_n = 1;
- uint8_t max_n = 5;
+ uint8_t n = 0; // resampling ratio is 2**n
+ const uint8_t max_n = 5;
+ fp_28_36_t afe_rate = 0;
- 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++;
+ 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;
+ }
}
return n;
}
-#define MIN_MCU_RATE (200000ULL * FP_ONE_HZ)
-#define MAX_MCU_RATE (21800000ULL * FP_ONE_HZ)
-#define DEFAULT_MCU_RATE (10000000ULL * FP_ONE_HZ)
+#define MIN_MCU_RATE SR_FP_KHZ(200)
+#define MAX_MCU_RATE SR_FP_KHZ(21800)
+#define DEFAULT_MCU_RATE SR_FP_KHZ(10000)
-static fp_40_24_t applied_afe_rate = RADIO_UNSET;
-
-static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
+static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
{
- fp_40_24_t rate, afe_rate;
- uint64_t previous_n;
+ fp_28_36_t rate, afe_rate, previous_rate, previous_afe_rate;
+ uint64_t previous_n, requested_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_n = bank[RADIO_RESAMPLE_RX];
+ const uint64_t requested_correction = bank[RADIO_CLOCK_CORRECTION];
+
+ uint64_t correction = restrict_correction(requested_correction);
if (requested_rate != RADIO_UNSET) {
rate = MIN(requested_rate, MAX_MCU_RATE);
@@ -194,190 +283,383 @@ static bool radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
switch (opmode) {
case TRANSCEIVER_MODE_TX:
case TRANSCEIVER_MODE_SS:
- n = compute_resample_log(rate / FP_ONE_HZ, requested_n);
+ requested_n = bank[RADIO_RESAMPLE_TX];
+ n = compute_resample_log(rate, requested_n);
if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX]) {
-#ifdef PRALINE
- fpga_set_tx_interpolation_ratio(&fpga, n);
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ fpga_set_tx_interpolation_ratio(&fpga, n);
+ }
#endif
radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX] = n;
}
break;
default:
- n = compute_resample_log(rate / FP_ONE_HZ, requested_n);
+ requested_n = bank[RADIO_RESAMPLE_RX];
+ n = compute_resample_log(rate, requested_n);
if (n != radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX]) {
-#ifdef PRALINE
- fpga_set_rx_decimation_ratio(&fpga, n);
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ fpga_set_rx_decimation_ratio(&fpga, n);
+ }
#endif
radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX] = n;
}
}
new_n = (n != previous_n);
- if (radio->sample_rate_cb) {
- afe_rate = rate << n;
- afe_rate = radio->sample_rate_cb(afe_rate, false);
- new_afe_rate = (afe_rate != applied_afe_rate);
- if (new_afe_rate) {
- afe_rate = radio->sample_rate_cb(afe_rate, true);
- applied_afe_rate = afe_rate;
- }
+ 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;
} else {
- return false;
+ previous_afe_rate = previous_rate << previous_n;
}
+ 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 != radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE]);
+ 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 + (FP_ONE_HZ >> 1)) / FP_ONE_HZ;
- hackrf_ui()->set_sample_rate(rate_hz);
- }
}
- return (new_afe_rate || new_rate || new_n);
+ return ((new_afe_rate || new_rate || new_n) << RADIO_SAMPLE_RATE);
}
-#define DEFAULT_RF (2450ULL * FP_ONE_MHZ)
-
-static fp_40_24_t applied_offset = RADIO_UNSET;
-
-#ifdef PRALINE
-static const tune_config_t* select_tune_config(uint64_t opmode)
+/*
+ * Compute frequency offset in 1/(2**24) Hz for a given rotation. This is an
+ * unsigned value that does not indicate direction of frequency shift.
+ */
+static fp_40_24_t compute_offset(radio_t* const radio, uint64_t opmode, uint64_t rotation)
{
+ uint64_t n;
+ fp_40_24_t offset = 0;
+ const fp_28_36_t mcu_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE];
+
switch (opmode) {
case TRANSCEIVER_MODE_TX:
case TRANSCEIVER_MODE_SS:
- return praline_tune_config_tx;
- case TRANSCEIVER_MODE_RX_SWEEP:
- return praline_tune_config_rx_sweep;
+ n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_TX];
+ break;
default:
- return praline_tune_config_rx;
+ n = radio->config[RADIO_BANK_APPLIED][RADIO_RESAMPLE_RX];
}
+ if ((rotation != 0) && (rotation != RADIO_UNSET) && (mcu_rate != RADIO_UNSET) &&
+ (n != RADIO_UNSET)) {
+ const fp_40_24_t afe_rate = mcu_rate >> (12 - n);
+ if (rotation & 0x80000000) {
+ rotation = 0x100000000 - rotation;
+ }
+ offset = (afe_rate * (rotation >> 24)) >> 8;
+ }
+ return offset;
}
-#endif
-static bool radio_update_frequency(radio_t* const radio, uint64_t* bank)
+/*
+ * Convert center frequency of analog baseband (as seen by ADC/DAC) to center
+ * frequency of digital baseband (as seen by MCU).
+ */
+static fp_40_24_t digital_from_analog_rf(
+ radio_t* const radio,
+ const fp_40_24_t arf,
+ uint64_t opmode,
+ uint64_t rotation)
{
- uint8_t rotation = 0;
- uint64_t requested_rf = bank[RADIO_FREQUENCY_RF];
- const uint64_t requested_if = bank[RADIO_FREQUENCY_IF];
- const uint64_t requested_lo = bank[RADIO_FREQUENCY_LO];
- const uint64_t requested_img_reject = bank[RADIO_IMAGE_REJECT];
-
- bool set_if = (requested_if != RADIO_UNSET);
- bool set_lo = (requested_lo != RADIO_UNSET);
- bool set_img_reject = (requested_img_reject != RADIO_UNSET);
-
- if (set_if && set_lo && set_img_reject) {
- bool new_if =
- (requested_if !=
- radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF]);
- bool new_lo =
- (requested_lo !=
- radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO]);
- bool new_img_reject =
- (requested_img_reject !=
- radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT]);
- if (new_if || new_lo || new_img_reject) {
- set_freq_explicit(
- requested_if / FP_ONE_HZ,
- requested_lo / FP_ONE_HZ,
- requested_img_reject);
- radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] =
- requested_if;
- radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] =
- requested_lo;
- radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT] =
- requested_img_reject;
- radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] =
- RADIO_UNSET;
- }
-#ifdef PRALINE
- const uint64_t requested_rotation = bank[RADIO_ROTATION];
- if (requested_rotation != RADIO_UNSET) {
- rotation = requested_rotation;
- } else if (radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] != RADIO_UNSET) {
- return true;
- }
- fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 6);
-#endif
- radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = rotation;
- return true;
- }
-
- if (requested_rf == RADIO_UNSET) {
- if (radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] ==
- RADIO_UNSET) {
- requested_rf = DEFAULT_RF;
+ fp_40_24_t offset = compute_offset(radio, opmode, rotation);
+ fp_40_24_t drf = arf;
+ if (rotation > 0x80000000) {
+ if (offset > arf) {
+ drf = offset - arf;
} else {
- return false;
+ drf = arf - offset;
+ }
+ } else if ((rotation & 0x7fffffff) > 0) {
+ drf = arf + offset;
+ }
+ return drf;
+}
+
+#ifdef IS_PRALINE
+/*
+ * Convert center frequency of digital baseband (as seen by MCU) to center
+ * frequency of analog baseband (as seen by ADC/DAC).
+ */
+static fp_40_24_t analog_from_digital_rf(
+ radio_t* const radio,
+ const fp_40_24_t drf,
+ uint64_t opmode,
+ uint64_t rotation)
+{
+ fp_40_24_t offset = compute_offset(radio, opmode, rotation);
+ fp_40_24_t arf = drf;
+ if (rotation > 0x80000000) {
+ arf = drf + offset;
+ } else if ((rotation & 0x7fffffff) > 0) {
+ if (offset > drf) {
+ arf = offset - drf;
+ } else {
+ arf = drf - offset;
}
}
+ return arf;
+}
- bool new_rf =
- (radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] != requested_rf);
- uint64_t requested_rf_hz = requested_rf / FP_ONE_HZ;
-#ifdef PRALINE
- if (applied_afe_rate == RADIO_UNSET) {
- return false;
+static const tune_config_t* select_tune_config(uint64_t opmode, fp_40_24_t freq_rf)
+{
+ const tune_config_t* tune_config;
+
+ switch (opmode) {
+ case TRANSCEIVER_MODE_TX:
+ case TRANSCEIVER_MODE_SS:
+ tune_config = praline_tune_config_tx;
+ break;
+ case TRANSCEIVER_MODE_RX_SWEEP:
+ tune_config = praline_tune_config_rx_sweep;
+ break;
+ default:
+ tune_config = praline_tune_config_rx;
}
- uint64_t opmode = bank[RADIO_OPMODE];
- if (opmode == RADIO_UNSET) {
- opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE];
- }
- const tune_config_t* tune_config = select_tune_config(opmode);
- const tune_config_t* applied_tune_config =
- select_tune_config(radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE]);
- bool new_config = (applied_tune_config != tune_config);
while ((tune_config->rf_range_end_mhz != 0) || (tune_config->if_mhz != 0)) {
- if ((requested_rf_hz == 0) ||
- (tune_config->rf_range_end_mhz > (requested_rf_hz / FREQ_ONE_MHZ))) {
+ if ((freq_rf == 0) ||
+ (tune_config->rf_range_end_mhz > (freq_rf / FP_ONE_MHZ))) {
break;
}
tune_config++;
}
- bool new_rotation =
- (radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] !=
- ((uint64_t) tune_config->shift << 6));
- if (new_rotation) {
- fpga_set_rx_quarter_shift_mode(&fpga, tune_config->shift);
- radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] =
- tune_config->shift << 6;
+ return tune_config;
+}
+#endif
+
+static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
+{
+ uint32_t changed = 0;
+ bool high_lo = false;
+
+ const uint64_t requested_rf = bank[RADIO_FREQUENCY_RF];
+ const uint64_t requested_if = bank[RADIO_FREQUENCY_IF];
+ const uint64_t requested_lo = bank[RADIO_FREQUENCY_LO];
+ const uint64_t requested_img_reject = bank[RADIO_IMAGE_REJECT];
+#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];
+ const uint64_t applied_lo = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO];
+ const uint64_t applied_img_reject =
+ 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;
+ uint64_t freq_if = applied_if;
+ 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) {
+ opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE];
}
- fp_40_24_t offset = applied_afe_rate / 4;
- bool new_offset = (applied_offset != offset);
- if (new_rotation || new_offset || new_config || new_rf) {
- tuning_set_frequency(tune_config, requested_rf_hz, offset / FP_ONE_HZ);
- applied_offset = offset;
+
+ /* Restrict requested settings to valid ranges. */
+ if (requested_img_reject != RADIO_UNSET) {
+ switch (requested_img_reject) {
+ case RF_PATH_FILTER_LOW_PASS:
+ case RF_PATH_FILTER_HIGH_PASS:
+ img_reject = requested_img_reject;
+ break;
+ default:
+ img_reject = RF_PATH_FILTER_BYPASS;
+ }
}
-#else
- if (new_rf) {
- set_freq(requested_rf_hz);
+ correction = restrict_correction(requested_correction);
+ if (requested_if != RADIO_UNSET) {
+ freq_if = set_if(requested_if, correction, false);
+ }
+ if (requested_lo != RADIO_UNSET) {
+ freq_lo = set_lo(requested_lo, correction, false);
+ }
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ if (requested_rotation != RADIO_UNSET) {
+ rotation = requested_rotation;
+ }
+ switch (opmode) {
+ case TRANSCEIVER_MODE_RX:
+ case TRANSCEIVER_MODE_RX_SWEEP:
+ /* Round to nearest supported rotation. */
+ rotation = (rotation + 0x20000000) & 0xc0000000;
+ if (rotation == 0x80000000) {
+ rotation = 0;
+ }
+ break;
+ default:
+ rotation = 0;
+ }
}
#endif
- radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] = requested_rf;
- radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = RADIO_UNSET;
- radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = RADIO_UNSET;
- radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT] = RADIO_UNSET;
- return true;
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ rotation = 0;
+ }
+#endif
+
+ /* Handle requested RF (auto-tune). */
+ if (requested_rf != RADIO_UNSET) {
+ if (requested_img_reject == RADIO_UNSET) {
+ img_reject = select_img_reject(requested_rf);
+ }
+ freq_rf = restrict_rf(requested_rf, img_reject);
+
+ /* Look up settings appropriate for requested RF. */
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ const tune_config_t* tune_config =
+ select_tune_config(opmode, freq_rf);
+ if (requested_rotation == RADIO_UNSET) {
+ rotation = (uint64_t) (tune_config->shift) << 30;
+ }
+ analog_rf =
+ analog_from_digital_rf(radio, freq_rf, opmode, rotation);
+ if (requested_if == RADIO_UNSET) {
+ if (tune_config->if_mhz == 0) {
+ freq_if = analog_rf;
+ } else {
+ freq_if = tune_config->if_mhz * FP_ONE_MHZ;
+ }
+ }
+ high_lo = tune_config->high_lo;
+ }
+#endif
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ /* Use graduated or fixed IF on older platforms. */
+ if (requested_if == RADIO_UNSET) {
+ freq_if = select_graduated_if(freq_rf, img_reject);
+ }
+ high_lo = (img_reject == RF_PATH_FILTER_LOW_PASS);
+ analog_rf = freq_rf;
+ }
+#endif
+
+ /* Compute precise LO. This is done first because the mixer LO is set in coarse steps. */
+ if (requested_lo == RADIO_UNSET) {
+ switch (img_reject) {
+ case RF_PATH_FILTER_LOW_PASS:
+ if (high_lo) {
+ freq_lo = freq_if + analog_rf;
+ } else {
+ freq_lo = freq_if - analog_rf;
+ }
+ break;
+ case RF_PATH_FILTER_HIGH_PASS:
+ freq_lo = analog_rf - freq_if;
+ break;
+ default:
+ freq_lo = RADIO_UNSET;
+ }
+ if (freq_lo != RADIO_UNSET) {
+ freq_lo = set_lo(freq_lo, correction, false);
+ }
+ }
+
+ /* Recompute IF, compensating for rounding of mixer LO. */
+ if (requested_if == RADIO_UNSET) {
+ switch (img_reject) {
+ case RF_PATH_FILTER_LOW_PASS:
+ if (high_lo) {
+ freq_if = freq_lo - analog_rf;
+ } else {
+ freq_if = freq_lo + analog_rf;
+ }
+ break;
+ case RF_PATH_FILTER_HIGH_PASS:
+ freq_if = analog_rf - freq_lo;
+ }
+ }
+ }
+
+ /* Apply settings. */
+ if ((freq_if != applied_if) && (freq_if != RADIO_UNSET)) {
+ freq_if = set_if(freq_if, correction, true);
+ radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = freq_if;
+ changed |= (1 << RADIO_FREQUENCY_IF);
+ }
+ if ((freq_lo != applied_lo) && (freq_lo != RADIO_UNSET)) {
+ freq_lo = set_lo(freq_lo, correction, true);
+ radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = freq_lo;
+ changed |= (1 << RADIO_FREQUENCY_LO);
+ }
+ if ((img_reject != applied_img_reject) && (img_reject != RADIO_UNSET)) {
+ rf_path_set_filter(&rf_path, img_reject);
+ radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT] = img_reject;
+ changed |= (1 << RADIO_IMAGE_REJECT);
+ }
+ if ((rotation != applied_rotation) && (rotation != RADIO_UNSET)) {
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ fpga_set_rx_quarter_shift_mode(&fpga, rotation >> 30);
+ }
+#endif
+ radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION] = rotation;
+ changed |= (1 << RADIO_ROTATION);
+ }
+
+ /* Compute precise RF. */
+ if ((img_reject != RADIO_UNSET) && (freq_if != RADIO_UNSET) &&
+ (rotation != RADIO_UNSET) &&
+ ((freq_lo != RADIO_UNSET) || (img_reject == RF_PATH_FILTER_BYPASS))) {
+ switch (img_reject) {
+ case RF_PATH_FILTER_LOW_PASS:
+ if (freq_lo > freq_if) {
+ analog_rf = freq_lo - freq_if;
+ } else {
+ analog_rf = freq_if - freq_lo;
+ }
+ break;
+ case RF_PATH_FILTER_HIGH_PASS:
+ analog_rf = freq_if + freq_lo;
+ break;
+ default:
+ analog_rf = freq_if;
+ }
+ freq_rf = digital_from_analog_rf(radio, analog_rf, opmode, rotation);
+ }
+ 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);
+ }
+
+ return changed;
}
-static uint32_t auto_bandwidth(radio_t* const radio)
+static uint32_t auto_bandwidth(radio_t* const radio, uint64_t opmode)
{
+ uint32_t offset_hz = 0;
uint64_t rotation = radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION];
- uint32_t sample_rate_hz = DEFAULT_MCU_RATE / FP_ONE_HZ;
+ uint32_t sample_rate_hz = DEFAULT_MCU_RATE / SR_FP_ONE_HZ;
if (radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] != RADIO_UNSET) {
- sample_rate_hz =
- radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] / FP_ONE_HZ;
+ sample_rate_hz = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE] /
+ SR_FP_ONE_HZ;
}
- uint32_t offset_hz = 0;
- if ((rotation != 0) && (rotation != RADIO_UNSET) &&
- (applied_offset != RADIO_UNSET)) {
- offset_hz = applied_offset / FP_ONE_HZ;
- }
+ const fp_40_24_t offset = compute_offset(radio, opmode, rotation);
+ offset_hz = offset >> 24;
const uint32_t bb_bandwidth = (sample_rate_hz * 3) / 4;
const uint32_t lpf_bandwidth = bb_bandwidth + offset_hz * 2;
@@ -388,83 +670,117 @@ static uint32_t auto_bandwidth(radio_t* const radio)
return lpf_bandwidth;
}
-static bool radio_update_bandwidth(radio_t* const radio, uint64_t* bank)
+static uint32_t radio_update_bandwidth(radio_t* const radio, uint64_t* bank)
{
- bool new_bw = false;
+ uint32_t changed = 0;
-#ifdef PRALINE
- /* Praline legacy mode always sets baseband bandwidth automatically. */
- (void) bank;
- uint32_t lpf_bandwidth = auto_bandwidth(radio);
-
- if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) {
- max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth);
- radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth;
- new_bw = true;
- }
- if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] != lpf_bandwidth) {
- max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_RX, lpf_bandwidth);
- radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth;
- new_bw = true;
- }
- bool narrow_lpf_enable = false;
- bool applied_narrow_lpf_enable =
- radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF];
- if (lpf_bandwidth <= 1750000) {
- narrow_lpf_enable = true;
- }
- if (applied_narrow_lpf_enable != narrow_lpf_enable) {
- narrowband_filter_set(narrow_lpf_enable);
- radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF] =
- narrow_lpf_enable;
- 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;
- if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] != hpf_bandwidth) {
- max283x_set_rx_hpf_frequency(&max283x, hpf_bandwidth);
- radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] = hpf_bandwidth;
- new_bw = true;
- }
-#else
- uint64_t lpf_bandwidth;
- lpf_bandwidth = bank[RADIO_XCVR_TX_LPF];
- if (lpf_bandwidth == RADIO_UNSET) {
- lpf_bandwidth = bank[RADIO_XCVR_RX_LPF];
- }
- if (lpf_bandwidth == RADIO_UNSET) {
- lpf_bandwidth = bank[RADIO_BB_BANDWIDTH_TX];
- }
- if (lpf_bandwidth == RADIO_UNSET) {
- lpf_bandwidth = bank[RADIO_BB_BANDWIDTH_RX];
- }
- if (lpf_bandwidth == RADIO_UNSET) {
- lpf_bandwidth = radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF];
- }
- if (lpf_bandwidth == RADIO_UNSET) {
- lpf_bandwidth = auto_bandwidth(radio);
+ uint64_t opmode = bank[RADIO_OPMODE];
+ if (opmode == RADIO_UNSET) {
+ opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE];
}
- if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] != lpf_bandwidth) {
- max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_TX, lpf_bandwidth);
- radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_RX] = lpf_bandwidth;
- radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_TX] = lpf_bandwidth;
- radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] = lpf_bandwidth;
- radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] = lpf_bandwidth;
- new_bw = true;
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ /* Praline legacy mode always sets baseband bandwidth automatically. */
+ (void) bank;
+ uint32_t lpf_bandwidth = auto_bandwidth(radio, opmode);
+
+ if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] !=
+ lpf_bandwidth) {
+ max283x_set_lpf_bandwidth(
+ &max283x,
+ MAX283x_MODE_TX,
+ lpf_bandwidth);
+ radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] =
+ lpf_bandwidth;
+ changed |= (1 << RADIO_XCVR_TX_LPF);
+ }
+ if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] !=
+ lpf_bandwidth) {
+ max283x_set_lpf_bandwidth(
+ &max283x,
+ MAX283x_MODE_RX,
+ lpf_bandwidth);
+ radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_LPF] =
+ lpf_bandwidth;
+ changed |= (1 << RADIO_XCVR_RX_LPF);
+ }
+ bool narrow_lpf_enable = false;
+ bool applied_narrow_lpf_enable =
+ radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF];
+ if (lpf_bandwidth <= 1750000) {
+ narrow_lpf_enable = true;
+ }
+ if (applied_narrow_lpf_enable != narrow_lpf_enable) {
+ narrowband_filter_set(narrow_lpf_enable);
+ radio->config[RADIO_BANK_APPLIED][RADIO_RX_NARROW_LPF] =
+ narrow_lpf_enable;
+ changed |= (1 << RADIO_RX_NARROW_LPF);
+ }
+ /* Always set HPF bandwidth to 30 kHz for now. */
+ const max283x_rx_hpf_freq_t hpf_bandwidth = MAX283x_RX_HPF_30_KHZ;
+ if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_RX_HPF] !=
+ hpf_bandwidth) {
+ 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);
+ }
}
#endif
- return new_bw;
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ uint64_t lpf_bandwidth;
+ lpf_bandwidth = bank[RADIO_XCVR_TX_LPF];
+ if (lpf_bandwidth == RADIO_UNSET) {
+ lpf_bandwidth = bank[RADIO_XCVR_RX_LPF];
+ }
+ if (lpf_bandwidth == RADIO_UNSET) {
+ lpf_bandwidth = bank[RADIO_BB_BANDWIDTH_TX];
+ }
+ if (lpf_bandwidth == RADIO_UNSET) {
+ lpf_bandwidth = bank[RADIO_BB_BANDWIDTH_RX];
+ }
+ if (lpf_bandwidth == RADIO_UNSET) {
+ lpf_bandwidth =
+ radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF];
+ }
+ if (lpf_bandwidth == RADIO_UNSET) {
+ lpf_bandwidth = auto_bandwidth(radio, opmode);
+ }
+
+ if (radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] !=
+ lpf_bandwidth) {
+ max283x_set_lpf_bandwidth(
+ &max283x,
+ MAX283x_MODE_TX,
+ lpf_bandwidth);
+ radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_RX] =
+ lpf_bandwidth;
+ radio->config[RADIO_BANK_APPLIED][RADIO_BB_BANDWIDTH_TX] =
+ lpf_bandwidth;
+ radio->config[RADIO_BANK_APPLIED][RADIO_XCVR_TX_LPF] =
+ 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));
+ }
+ }
+#endif
+ return changed;
}
#define DEFAULT_GAIN_RF (0)
#define DEFAULT_GAIN_IF (16)
#define DEFAULT_GAIN_BB (12)
-static bool radio_update_gain(radio_t* const radio, uint64_t* bank)
+static uint32_t radio_update_gain(radio_t* const radio, uint64_t* bank)
{
- bool new_gain = false;
- uint64_t gain;
+ uint32_t changed = 0;
+ uint64_t gain, tx_rf_gain, rx_rf_gain;
uint64_t opmode = bank[RADIO_OPMODE];
if (opmode == RADIO_UNSET) {
opmode = radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE];
@@ -478,34 +794,34 @@ static bool radio_update_gain(radio_t* const radio, uint64_t* bank)
switch (opmode) {
case TRANSCEIVER_MODE_TX:
case TRANSCEIVER_MODE_SS:
- gain = bank[RADIO_GAIN_TX_RF];
- if (gain == RADIO_UNSET) {
- gain = DEFAULT_GAIN_RF;
+ tx_rf_gain = bank[RADIO_GAIN_TX_RF];
+ if (tx_rf_gain == RADIO_UNSET) {
+ tx_rf_gain = DEFAULT_GAIN_RF;
}
- 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;
+ rf_path_set_lna(&rf_path, tx_rf_gain);
+ rx_rf_gain = 0;
break;
case TRANSCEIVER_MODE_RX:
case TRANSCEIVER_MODE_RX_SWEEP:
- gain = bank[RADIO_GAIN_RX_RF];
- if (gain == RADIO_UNSET) {
- gain = DEFAULT_GAIN_RF;
+ rx_rf_gain = bank[RADIO_GAIN_RX_RF];
+ if (rx_rf_gain == RADIO_UNSET) {
+ rx_rf_gain = DEFAULT_GAIN_RF;
}
- 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;
+ rf_path_set_lna(&rf_path, rx_rf_gain);
+ tx_rf_gain = 0;
break;
default:
rf_path_set_lna(&rf_path, 0);
- radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_RF] = 0;
- radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_RF] = 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);
}
gain = bank[RADIO_GAIN_TX_IF];
@@ -513,23 +829,23 @@ static bool 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;
- new_gain = true;
+ changed |= (1 << RADIO_GAIN_TX_IF);
} 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;
- new_gain = true;
+ changed |= (1 << RADIO_GAIN_TX_IF);
}
gain = bank[RADIO_GAIN_RX_IF];
if ((gain != RADIO_UNSET) &&
- (gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF])) {
+ (gain != radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF])) {
max283x_set_lna_gain(&max283x, gain);
- radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] = gain;
- new_gain = true;
- } else if (radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_TX_IF] == RADIO_UNSET) {
+ radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] = gain;
+ changed |= (1 << RADIO_GAIN_RX_IF);
+ } 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_TX_IF] = DEFAULT_GAIN_IF;
- new_gain = true;
+ radio->config[RADIO_BANK_APPLIED][RADIO_GAIN_RX_IF] = DEFAULT_GAIN_IF;
+ changed |= (1 << RADIO_GAIN_RX_IF);
}
gain = bank[RADIO_GAIN_RX_BB];
@@ -537,39 +853,39 @@ static bool 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;
- new_gain = true;
+ changed |= (1 << RADIO_GAIN_RX_BB);
} 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;
- new_gain = true;
+ changed |= (1 << RADIO_GAIN_RX_BB);
}
- return new_gain;
+ return changed;
}
-static bool radio_update_bias_tee(radio_t* const radio, uint64_t* bank)
+static uint32_t radio_update_bias_tee(radio_t* const radio, uint64_t* bank)
{
if (detected_platform() == BOARD_ID_JAWBREAKER) {
- return false;
+ return 0;
}
const uint64_t requested = bank[RADIO_BIAS_TEE];
const bool enable = requested;
if (requested == RADIO_UNSET) {
- return false;
+ return 0;
}
if (radio->config[RADIO_BANK_APPLIED][RADIO_BIAS_TEE] == (uint64_t) enable) {
- return false;
+ return 0;
}
rf_path_set_antenna(&rf_path, enable);
radio->config[RADIO_BANK_APPLIED][RADIO_BIAS_TEE] = enable;
- return true;
+ return (1 << RADIO_BIAS_TEE);
}
-static bool radio_update_trigger(radio_t* const radio, uint64_t* bank)
+static uint32_t radio_update_trigger(radio_t* const radio, uint64_t* bank)
{
const uint64_t requested = bank[RADIO_TRIGGER];
bool enable = requested;
@@ -579,36 +895,35 @@ static bool radio_update_trigger(radio_t* const radio, uint64_t* bank)
}
if (radio->config[RADIO_BANK_APPLIED][RADIO_TRIGGER] == (uint64_t) enable) {
- return false;
+ return 0;
}
trigger_enable(enable);
radio->config[RADIO_BANK_APPLIED][RADIO_TRIGGER] = enable;
- return true;
+ return (1 << RADIO_TRIGGER);
}
-static bool radio_update_dc_block(radio_t* const radio, uint64_t* bank)
+static uint32_t radio_update_dc_block(radio_t* const radio, uint64_t* bank)
{
-#ifndef PRALINE
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ const uint64_t requested = bank[RADIO_DC_BLOCK];
+ bool enable = requested;
+
+ if (radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] ==
+ (uint64_t) enable) {
+ return 0;
+ }
+
+ fpga_set_rx_dc_block_enable(&fpga, enable);
+ radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] = enable;
+ return (1 << RADIO_DC_BLOCK);
+ }
+#endif
+
(void) radio;
(void) bank;
return false;
-#else
- 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 false;
- }
-
- fpga_set_rx_dc_block_enable(&fpga, enable);
- radio->config[RADIO_BANK_APPLIED][RADIO_DC_BLOCK] = enable;
- return true;
-#endif
}
bool radio_update(radio_t* const radio)
@@ -616,64 +931,50 @@ 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_ACTIVE][0]), sizeof(tmp_bank));
+ memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_REQUESTED][0]), sizeof(tmp_bank));
nvic_enable_irq(NVIC_USB0_IRQ);
- 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))) ||
+ if ((dirty & RADIO_REG_GROUP_RATE) ||
((detected_platform() == BOARD_ID_PRALINE) &&
(dirty & (1 << RADIO_OPMODE)))) {
- rate = radio_update_sample_rate(radio, &tmp_bank[0]);
+ changed |= radio_update_sample_rate(radio, &tmp_bank[0]);
}
- if ((dirty &
- ((1 << RADIO_FREQUENCY_RF) | (1 << RADIO_FREQUENCY_IF) |
- (1 << RADIO_FREQUENCY_LO) | (1 << RADIO_IMAGE_REJECT) |
- (1 << RADIO_ROTATION))) ||
+ if ((dirty & RADIO_REG_GROUP_FREQ) ||
((detected_platform() == BOARD_ID_PRALINE) &&
- (rate || (dirty & (1 << RADIO_OPMODE))))) {
- freq = radio_update_frequency(radio, &tmp_bank[0]);
+ ((changed & RADIO_REG_GROUP_RATE) || (dirty & (1 << RADIO_OPMODE))))) {
+ changed |= radio_update_frequency(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_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_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 & (RADIO_REG_GROUP_GAIN | (1 << RADIO_OPMODE))) {
+ changed |= radio_update_gain(radio, &tmp_bank[0]);
}
if (dirty & ((1 << RADIO_BIAS_TEE) | (1 << RADIO_OPMODE))) {
- bias = radio_update_bias_tee(radio, &tmp_bank[0]);
+ changed |= radio_update_bias_tee(radio, &tmp_bank[0]);
}
if (dirty & (1 << RADIO_TRIGGER)) {
- trig = radio_update_trigger(radio, &tmp_bank[0]);
+ changed |= radio_update_trigger(radio, &tmp_bank[0]);
}
if (dirty & (1 << RADIO_DC_BLOCK)) {
- dc = radio_update_dc_block(radio, &tmp_bank[0]);
+ changed |= radio_update_dc_block(radio, &tmp_bank[0]);
}
if (dirty & (1 << RADIO_OPMODE)) {
- dir = radio_update_direction(radio, &tmp_bank[0]);
+ changed |= radio_update_direction(radio, &tmp_bank[0]);
}
- return trig || dir || rate || freq || bw || gain || bias || dc;
+ if (radio->update_cb) {
+ radio->update_cb(changed);
+ }
+ return (changed != 0);
}
void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode)
@@ -697,13 +998,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_ACTIVE][reg];
+ previous = radio->config[RADIO_BANK_REQUESTED][reg];
if ((value != RADIO_UNSET) && (value != previous)) {
- radio->config[RADIO_BANK_ACTIVE][reg] = value;
+ radio->config[RADIO_BANK_REQUESTED][reg] = value;
mark_dirty(radio, reg);
}
}
+ radio->config[RADIO_BANK_REQUESTED][RADIO_OPMODE] = mode;
mark_dirty(radio, RADIO_OPMODE);
nvic_enable_irq(NVIC_USB0_IRQ);
radio_update(radio);
diff --git a/firmware/common/radio.h b/firmware/common/radio.h
index 7b526a9d..8e12471a 100644
--- a/firmware/common/radio.h
+++ b/firmware/common/radio.h
@@ -21,13 +21,13 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __RF_CONFIG_H__
-#define __RF_CONFIG_H__
+#pragma once
#include
#include
#include "fixed_point.h"
+#include "transceiver_mode.h"
typedef enum {
RADIO_OK = 1,
@@ -54,22 +54,6 @@ 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;
-
-typedef enum {
- TRANSCEIVER_MODE_OFF = 0,
- TRANSCEIVER_MODE_RX = 1,
- TRANSCEIVER_MODE_TX = 2,
- TRANSCEIVER_MODE_SS = 3,
- TRANSCEIVER_MODE_CPLD_UPDATE = 4,
- TRANSCEIVER_MODE_RX_SWEEP = 5,
-} transceiver_mode_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
@@ -100,12 +84,12 @@ typedef enum {
*/
RADIO_IMAGE_REJECT = 4,
/**
- * Digital frequency conversion of type uint8_t in tau/256 steps with
+ * Digital frequency conversion of type uint8_t in tau/(2**32) steps with
* respect to AFE clock.
*/
RADIO_ROTATION = 5,
/**
- * Sample rate (as seen by MCU/host) in 1/(2**24) Hz.
+ * Sample rate (as seen by MCU/host) in 1/(2**36) Hz.
*/
RADIO_SAMPLE_RATE = 6,
/**
@@ -181,13 +165,33 @@ 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 (23)
+#define RADIO_NUM_REGS (24)
#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_ACTIVE stores the active configuration. Active
+ * Register bank RADIO_BANK_REQUESTED 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
@@ -198,7 +202,7 @@ typedef enum {
*/
typedef enum {
RADIO_BANK_APPLIED = 0,
- RADIO_BANK_ACTIVE = 1,
+ RADIO_BANK_REQUESTED = 1,
RADIO_BANK_IDLE = 2,
RADIO_BANK_RX = 3,
RADIO_BANK_TX = 4,
@@ -214,19 +218,26 @@ typedef enum {
* execute a dry run, returning the sample rate without configuring clock
* generation.
*/
-typedef fp_40_24_t (*sample_rate_fn)(const fp_40_24_t sample_rate, const bool program);
+typedef fp_28_36_t (*sample_rate_fn)(const fp_28_36_t sample_rate, const bool program);
-typedef struct radio_t {
+/**
+ * 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;
- sample_rate_fn sample_rate_cb;
+ update_fn update_cb;
} radio_t;
void radio_init(radio_t* const radio);
/**
- * Write to one or more registers. Writes to RADIO_BANK_ACTIVE are applied at
+ * Write to one or more registers. Writes to RADIO_BANK_REQUESTED are applied at
* the next radio_update(). Writes to RADIO_BANK_APPLIED are not supported.
*/
radio_error_t radio_reg_write(
@@ -244,7 +255,7 @@ uint64_t radio_reg_read(
const radio_register_t reg);
/**
- * Apply changes requested in RADIO_BANK_ACTIVE.
+ * Apply changes requested in RADIO_BANK_REQUESTED.
* Return true if any changes were applied.
*/
bool radio_update(radio_t* const radio);
@@ -255,4 +266,7 @@ bool radio_update(radio_t* const radio);
*/
void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode);
-#endif /*__RF_CONFIG_H__*/
+/**
+ * Driver instance.
+ */
+extern radio_t radio;
diff --git a/firmware/common/rf_path.c b/firmware/common/rf_path.c
index 30c8c718..51488c16 100644
--- a/firmware/common/rf_path.c
+++ b/firmware/common/rf_path.c
@@ -23,21 +23,21 @@
#include "rf_path.h"
-#if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE)
- #include
-#endif
-
-#include "hackrf_core.h"
-#include "hackrf_ui.h"
#include "max283x.h"
#include "max5864.h"
#include "mixer.h"
#include "platform_detect.h"
-#include "sgpio.h"
-#if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE)
+#ifdef IS_NOT_JAWBREAKER
+ #include
#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.
@@ -94,17 +94,18 @@
#define SWITCHCTRL_ANT_PWR (1 << 6) /* turn on antenna port power */
-#ifdef HACKRF_ONE
-
+#ifdef IS_HACKRF_ONE
static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl)
{
+ board_id_t board_id = detected_platform();
+
if (ctrl & SWITCHCTRL_TX) {
- if (detected_platform() != BOARD_ID_HACKRF1_R9) {
+ if (board_id != BOARD_ID_HACKRF1_R9) {
gpio_set(rf_path->gpio_tx);
}
gpio_clear(rf_path->gpio_rx);
} else {
- if (detected_platform() != BOARD_ID_HACKRF1_R9) {
+ if (board_id != BOARD_ID_HACKRF1_R9) {
gpio_clear(rf_path->gpio_tx);
}
gpio_set(rf_path->gpio_rx);
@@ -167,7 +168,7 @@ static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl)
gpio_set(rf_path->gpio_no_rx_amp_pwr);
}
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
+ if (board_id == BOARD_ID_HACKRF1_R9) {
if (ctrl & SWITCHCTRL_ANT_PWR) {
gpio_clear(rf_path->gpio_h1r9_no_ant_pwr);
} else {
@@ -187,7 +188,7 @@ static void switchctrl_set_hackrf_one(rf_path_t* const rf_path, uint8_t ctrl)
}
#endif
-#ifdef PRALINE
+#ifdef IS_PRALINE
static void switchctrl_set_praline(rf_path_t* const rf_path, uint8_t ctrl)
{
if (ctrl & SWITCHCTRL_TX) {
@@ -228,7 +229,7 @@ static void switchctrl_set_praline(rf_path_t* const rf_path, uint8_t ctrl)
}
#endif
-#ifdef RAD1O
+#ifdef IS_RAD1O
static void switchctrl_set_rad1o(rf_path_t* const rf_path, uint8_t ctrl)
{
if (ctrl & SWITCHCTRL_TX) {
@@ -295,166 +296,293 @@ static void switchctrl_set_rad1o(rf_path_t* const rf_path, uint8_t ctrl)
static void switchctrl_set(rf_path_t* const rf_path, const uint8_t gpo)
{
-#ifdef JAWBREAKER
- (void) rf_path; /* silence unused param warning */
- mixer_set_gpo(&mixer, gpo);
-#elif HACKRF_ONE
- switchctrl_set_hackrf_one(rf_path, gpo);
-#elif PRALINE
- switchctrl_set_praline(rf_path, gpo);
-#elif RAD1O
- switchctrl_set_rad1o(rf_path, gpo);
-#else
- (void) gpo;
+#ifdef IS_JAWBREAKER
+ if (IS_JAWBREAKER) {
+ (void) rf_path;
+ mixer_set_gpo(&mixer, gpo);
+ }
+#endif
+#ifdef IS_HACKRF_ONE
+ if (IS_HACKRF_ONE) {
+ switchctrl_set_hackrf_one(rf_path, gpo);
+ }
+#endif
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ switchctrl_set_praline(rf_path, gpo);
+ }
+#endif
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ switchctrl_set_rad1o(rf_path, 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)
{
-#if !defined(JAWBREAKER)
+#ifdef IS_JAWBREAKER
+ (void) rf_path;
+#else
+ const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
#endif
-#ifdef HACKRF_ONE
- /* Configure RF switch control signals */
- // clang-format off
- scu_pinmux(scu->HP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->LP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->TX_MIX_BP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->NO_MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->RX_MIX_BP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->TX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu_pinmux(scu->NO_TX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->AMP_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->RX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->NO_RX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- // clang-format on
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- scu_pinmux(scu->H1R9_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->H1R9_NO_ANT_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- gpio_clear(rf_path->gpio_h1r9_no_ant_pwr);
- gpio_output(rf_path->gpio_h1r9_no_ant_pwr);
- scu_pinmux(scu->H1R9_NO_VAA_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- } else {
- scu_pinmux(scu->TX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu_pinmux(scu->RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- gpio_output(rf_path->gpio_tx);
+ // 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 */
+ // clang-format off
+ scu_pinmux(scu->HP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->LP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->TX_MIX_BP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->NO_MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->RX_MIX_BP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->TX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->MIX_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu_pinmux(scu->NO_TX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->AMP_BYPASS, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->RX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->NO_RX_AMP_PWR, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ // clang-format on
+ #ifdef IS_H1_R9
+ if (IS_H1_R9) {
+ scu_pinmux(scu->H1R9_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(
+ scu->H1R9_NO_ANT_PWR,
+ SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ gpio_clear(rf_path->gpio_h1r9_no_ant_pwr);
+ gpio_output(rf_path->gpio_h1r9_no_ant_pwr);
+ scu_pinmux(
+ scu->H1R9_NO_VAA_EN,
+ SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ }
+ #endif
+ #ifdef IS_NOT_H1_R9
+ if (IS_NOT_H1_R9) {
+ scu_pinmux(scu->TX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu_pinmux(scu->RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ gpio_output(rf_path->gpio_tx);
+ scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ }
+ #endif
+
+ /*
+ * Safe (initial) switch settings turn off both amplifiers and antenna port
+ * power and enable both amp bypass and mixer bypass.
+ */
+ switchctrl_set(rf_path, SWITCHCTRL_SAFE);
+
+ /* Configure RF switch control signals as outputs */
+ gpio_output(rf_path->gpio_amp_bypass);
+ gpio_output(rf_path->gpio_no_mix_bypass);
+ gpio_output(rf_path->gpio_rx_amp);
+ gpio_output(rf_path->gpio_no_rx_amp_pwr);
+ gpio_output(rf_path->gpio_hp);
+ gpio_output(rf_path->gpio_lp);
+ gpio_output(rf_path->gpio_tx_mix_bp);
+ gpio_output(rf_path->gpio_rx_mix_bp);
+ gpio_output(rf_path->gpio_tx_amp);
+ gpio_output(rf_path->gpio_no_tx_amp_pwr);
+ gpio_output(rf_path->gpio_mix_bypass);
+ gpio_output(rf_path->gpio_rx);
+ }
+#endif
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ /* Configure RF switch control signals */
+ // clang-format off
+ scu_pinmux(scu->BY_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->BY_AMP_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu_pinmux(scu->TX_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->TX_RX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->BY_MIX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->BY_MIX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->LOW_HIGH_FILT, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->LOW_HIGH_FILT_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->TX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->RX_LNA, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ scu_pinmux(scu->MIXER_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
+ // clang-format on
+
+ /* Configure RF power supply (VAA) switch */
+ scu_pinmux(scu->VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+
+ /*
+ * Safe (initial) switch settings turn off both amplifiers and antenna port
+ * power and enable both amp bypass and mixer bypass.
+ */
+ switchctrl_set(rf_path, SWITCHCTRL_SAFE);
+
+ /* Configure RF switch control signals as outputs */
+ gpio_output(rf_path->gpio_tx_rx_n);
+ gpio_output(rf_path->gpio_tx_rx);
+ gpio_output(rf_path->gpio_by_mix);
+ gpio_output(rf_path->gpio_by_mix_n);
+ gpio_output(rf_path->gpio_by_amp);
+ gpio_output(rf_path->gpio_by_amp_n);
+ gpio_output(rf_path->gpio_mixer_en);
+ gpio_output(rf_path->gpio_low_high_filt);
+ gpio_output(rf_path->gpio_low_high_filt_n);
+ gpio_output(rf_path->gpio_tx_amp);
+ gpio_output(rf_path->gpio_rx_lna);
+ }
+#endif
+
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ /* Configure RF switch control signals */
+ scu_pinmux(scu->TX_EN, SCU_GPIO_FAST | 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_FAST | SCU_CONF_FUNCTION4);
+ } else {
+ scu_pinmux(scu->MIX_EN_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ }
+ scu_pinmux(scu->LPF_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+ scu_pinmux(scu->RF_AMP_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+
+ /* Configure antenna port power control signal */
+ scu_pinmux(scu->ANT_BIAS_EN_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+
+ /* Configure RF power supply (VAA) switch */
scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
+
+ /*
+ * Safe (initial) switch settings turn off both amplifiers and antenna port
+ * power and enable both amp bypass and mixer bypass.
+ */
+ switchctrl_set(rf_path, SWITCHCTRL_SAFE);
+
+ /* Configure RF switch control signals as outputs */
+ gpio_output(rf_path->gpio_ant_bias_en_n);
+ gpio_output(rf_path->gpio_tx_en);
+ gpio_output(rf_path->gpio_mix_en_n);
+ gpio_output(rf_path->gpio_lpf_en);
+ gpio_output(rf_path->gpio_rf_amp_en);
}
-
- /*
- * Safe (initial) switch settings turn off both amplifiers and antenna port
- * power and enable both amp bypass and mixer bypass.
- */
- switchctrl_set(rf_path, SWITCHCTRL_SAFE);
-
- /* Configure RF switch control signals as outputs */
- gpio_output(rf_path->gpio_amp_bypass);
- gpio_output(rf_path->gpio_no_mix_bypass);
- gpio_output(rf_path->gpio_rx_amp);
- gpio_output(rf_path->gpio_no_rx_amp_pwr);
- gpio_output(rf_path->gpio_hp);
- gpio_output(rf_path->gpio_lp);
- gpio_output(rf_path->gpio_tx_mix_bp);
- gpio_output(rf_path->gpio_rx_mix_bp);
- gpio_output(rf_path->gpio_tx_amp);
- gpio_output(rf_path->gpio_no_tx_amp_pwr);
- gpio_output(rf_path->gpio_mix_bypass);
- gpio_output(rf_path->gpio_rx);
-#elif RAD1O
- /* Configure RF switch control signals */
- // clang-format off
- scu_pinmux(scu->BY_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->BY_AMP_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu_pinmux(scu->TX_RX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->TX_RX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->BY_MIX, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->BY_MIX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->LOW_HIGH_FILT, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->LOW_HIGH_FILT_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->TX_AMP, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->RX_LNA, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- scu_pinmux(scu->MIXER_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
- // clang-format on
-
- /* Configure RF power supply (VAA) switch */
- scu_pinmux(scu->VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
-
- /*
- * Safe (initial) switch settings turn off both amplifiers and antenna port
- * power and enable both amp bypass and mixer bypass.
- */
- switchctrl_set(rf_path, SWITCHCTRL_SAFE);
-
- /* Configure RF switch control signals as outputs */
- gpio_output(rf_path->gpio_tx_rx_n);
- gpio_output(rf_path->gpio_tx_rx);
- gpio_output(rf_path->gpio_by_mix);
- gpio_output(rf_path->gpio_by_mix_n);
- gpio_output(rf_path->gpio_by_amp);
- gpio_output(rf_path->gpio_by_amp_n);
- gpio_output(rf_path->gpio_mixer_en);
- gpio_output(rf_path->gpio_low_high_filt);
- gpio_output(rf_path->gpio_low_high_filt_n);
- gpio_output(rf_path->gpio_tx_amp);
- gpio_output(rf_path->gpio_rx_lna);
-#elif PRALINE
- /* Configure RF switch control signals */
- scu_pinmux(scu->TX_EN, SCU_GPIO_FAST | 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_FAST | SCU_CONF_FUNCTION4);
- } else {
- scu_pinmux(scu->MIX_EN_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- }
- scu_pinmux(scu->LPF_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
- scu_pinmux(scu->RF_AMP_EN, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
-
- /* Configure antenna port power control signal */
- scu_pinmux(scu->ANT_BIAS_EN_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
-
- /* Configure RF power supply (VAA) switch */
- scu_pinmux(scu->NO_VAA_ENABLE, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
-
- /*
- * Safe (initial) switch settings turn off both amplifiers and antenna port
- * power and enable both amp bypass and mixer bypass.
- */
- switchctrl_set(rf_path, SWITCHCTRL_SAFE);
-
- /* Configure RF switch control signals as outputs */
- gpio_output(rf_path->gpio_ant_bias_en_n);
- gpio_output(rf_path->gpio_tx_en);
- gpio_output(rf_path->gpio_mix_en_n);
- gpio_output(rf_path->gpio_lpf_en);
- gpio_output(rf_path->gpio_rf_amp_en);
-#else
- (void) rf_path; /* silence unused param warning */
#endif
}
void rf_path_init(rf_path_t* const rf_path)
{
- ssp1_set_mode_max5864();
max5864_setup(&max5864);
max5864_shutdown(&max5864);
- ssp1_set_mode_max283x();
-#ifdef PRALINE
- max283x_setup(&max283x, MAX2831_VARIANT);
-#else
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
- max283x_setup(&max283x, MAX2839_VARIANT);
- } else {
- max283x_setup(&max283x, MAX2837_VARIANT);
- }
-#endif
+ max283x_setup(&max283x);
max283x_start(&max283x);
- // On HackRF One, the mixer is now set up earlier in boot.
-#ifndef HACKRF_ONE
- mixer_setup(&mixer);
+#ifdef IS_RAD1O
+ if (IS_RAD1O) {
+ mixer_setup(&mixer, MAX2871_VARIANT);
+ }
+#endif
+#ifdef IS_NOT_RAD1O
+ if (IS_NOT_RAD1O) {
+ #ifdef IS_NOT_HACKRF_ONE
+ if (IS_NOT_HACKRF_ONE) {
+ mixer_setup(&mixer, RFFC5071_VARIANT);
+ }
+ #endif
+ }
#endif
rf_path->switchctrl = SWITCHCTRL_SAFE;
}
@@ -476,11 +604,8 @@ 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:
@@ -494,27 +619,21 @@ 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 PRALINE
+#ifdef IS_PRALINE
case RF_PATH_DIRECTION_TX_CALIBRATION:
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
@@ -524,17 +643,12 @@ 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)
@@ -559,8 +673,6 @@ void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter)
}
switchctrl_set(rf_path, rf_path->switchctrl);
-
- hackrf_ui()->set_filter(filter);
}
void rf_path_set_lna(rf_path_t* const rf_path, const uint_fast8_t enable)
@@ -584,8 +696,6 @@ 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 */
@@ -598,6 +708,11 @@ void rf_path_set_antenna(rf_path_t* const rf_path, const uint_fast8_t enable)
}
switchctrl_set(rf_path, rf_path->switchctrl);
-
- hackrf_ui()->set_antenna_bias(enable);
}
+
+#ifdef IS_PRALINE
+void narrowband_filter_set(const uint8_t value)
+{
+ gpio_write(platform_gpio()->aa_en, value & 1);
+}
+#endif
diff --git a/firmware/common/rf_path.h b/firmware/common/rf_path.h
index 2d098d86..a1796f13 100644
--- a/firmware/common/rf_path.h
+++ b/firmware/common/rf_path.h
@@ -21,12 +21,11 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __RFPATH_H__
-#define __RFPATH_H__
+#pragma once
#include
-#if defined(HACKRF_ONE) || defined(RAD1O) || defined(PRALINE)
+#ifdef IS_NOT_JAWBREAKER
#include "gpio.h"
#endif
@@ -34,7 +33,7 @@ typedef enum {
RF_PATH_DIRECTION_OFF,
RF_PATH_DIRECTION_RX,
RF_PATH_DIRECTION_TX,
-#ifdef PRALINE
+#ifdef IS_PRALINE
RF_PATH_DIRECTION_TX_CALIBRATION,
RF_PATH_DIRECTION_RX_CALIBRATION,
#endif
@@ -46,47 +45,53 @@ typedef enum {
RF_PATH_FILTER_HIGH_PASS = 2,
} rf_path_filter_t;
-typedef struct rf_path_t {
+typedef struct {
uint8_t switchctrl;
-#ifdef HACKRF_ONE
- gpio_t gpio_hp;
- gpio_t gpio_lp;
- gpio_t gpio_tx_mix_bp;
- gpio_t gpio_no_mix_bypass;
- gpio_t gpio_rx_mix_bp;
- gpio_t gpio_tx_amp;
- gpio_t gpio_tx;
- gpio_t gpio_mix_bypass;
- gpio_t gpio_rx;
- gpio_t gpio_no_tx_amp_pwr;
- gpio_t gpio_amp_bypass;
- gpio_t gpio_rx_amp;
- gpio_t gpio_no_rx_amp_pwr;
- // In HackRF One r9 this control signal has been moved to the microcontroller.
- gpio_t gpio_h1r9_no_ant_pwr;
+
+ struct {
+#ifdef IS_HACKRF_ONE
+ gpio_t gpio_hp;
+ gpio_t gpio_lp;
+ gpio_t gpio_tx_mix_bp;
+ gpio_t gpio_no_mix_bypass;
+ gpio_t gpio_rx_mix_bp;
+ gpio_t gpio_tx_amp;
+ gpio_t gpio_tx;
+ gpio_t gpio_mix_bypass;
+ gpio_t gpio_rx;
+ gpio_t gpio_no_tx_amp_pwr;
+ gpio_t gpio_amp_bypass;
+ gpio_t gpio_rx_amp;
+ gpio_t gpio_no_rx_amp_pwr;
+ // In HackRF One r9 this control signal has been moved to the microcontroller.
+ gpio_t gpio_h1r9_no_ant_pwr;
#endif
-#ifdef RAD1O
- gpio_t gpio_tx_rx_n;
- gpio_t gpio_tx_rx;
- gpio_t gpio_by_mix;
- gpio_t gpio_by_mix_n;
- gpio_t gpio_by_amp;
- gpio_t gpio_by_amp_n;
- gpio_t gpio_mixer_en;
- gpio_t gpio_low_high_filt;
- gpio_t gpio_low_high_filt_n;
- gpio_t gpio_tx_amp;
- gpio_t gpio_rx_lna;
+
+#ifdef IS_RAD1O
+ gpio_t gpio_tx_rx_n;
+ gpio_t gpio_tx_rx;
+ gpio_t gpio_by_mix;
+ gpio_t gpio_by_mix_n;
+ gpio_t gpio_by_amp;
+ gpio_t gpio_by_amp_n;
+ gpio_t gpio_mixer_en;
+ gpio_t gpio_low_high_filt;
+ gpio_t gpio_low_high_filt_n;
+ gpio_t gpio_tx_amp;
+ gpio_t gpio_rx_lna;
#endif
-#ifdef PRALINE
- gpio_t gpio_tx_en;
- gpio_t gpio_mix_en_n;
- gpio_t gpio_lpf_en;
- gpio_t gpio_rf_amp_en;
- gpio_t gpio_ant_bias_en_n;
+
+#ifdef IS_PRALINE
+ gpio_t gpio_tx_en;
+ gpio_t gpio_mix_en_n;
+ gpio_t gpio_lpf_en;
+ gpio_t gpio_rf_amp_en;
+ gpio_t gpio_ant_bias_en_n;
#endif
+ };
} 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);
@@ -97,4 +102,9 @@ void rf_path_set_filter(rf_path_t* const rf_path, const rf_path_filter_t filter)
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);
-#endif /*__RFPATH_H__*/
+#ifdef IS_PRALINE
+void narrowband_filter_set(const uint8_t value);
+#endif
+
+/* RF Path instance. */
+extern rf_path_t rf_path;
diff --git a/firmware/common/rffc5071.c b/firmware/common/rffc5071.c
index 177537f5..d847b5c4 100644
--- a/firmware/common/rffc5071.c
+++ b/firmware/common/rffc5071.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2014 Jared Boone
*
* This file is part of HackRF.
@@ -31,21 +31,24 @@
* that the RFFC5071 includes a second mixer.
*/
+#include
#include
#include
-#if defined(PRALINE)
- #include
-#endif
-
#include "delay.h"
+#include "fixed_point.h"
+#include "platform_detect.h"
#include "rffc5071.h"
#include "rffc5071_regs.def" // private register def macros
#include "selftest.h"
-#if defined(PRALINE)
+#ifdef IS_PRALINE
+ #include
#include "platform_scu.h"
#endif
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
static bool enabled = false;
/* Default register values from vendor documentation or software. */
@@ -107,11 +110,13 @@ void rffc5071_setup(rffc5071_driver_t* const drv)
gpio_set(drv->gpio_reset);
gpio_output(drv->gpio_reset);
-#ifdef PRALINE
- /* Configure mixer PLL lock detect pin */
- const platform_scu_t* scu = platform_scu();
- scu_pinmux(scu->MIXER_LD, scu->MIXER_LD_PINCFG);
- gpio_input(drv->gpio_ld);
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ /* Configure mixer PLL lock detect pin */
+ const platform_scu_t* scu = platform_scu();
+ scu_pinmux(scu->MIXER_LD, scu->MIXER_LD_PINCFG);
+ gpio_input(drv->gpio_ld);
+ }
#endif
rffc5071_init(drv);
@@ -130,9 +135,10 @@ void rffc5071_setup(rffc5071_driver_t* const drv)
set_RFFC5071_FULLD(drv, 0);
set_RFFC5071_MODE(drv, 1);
-#if defined(PRALINE) || defined(HACKRF_ONE)
- /* Enable GPO Lock output signal */
- set_RFFC5071_LOCK(drv, 1);
+#ifdef IS_H1_OR_PRALINE
+ if (IS_H1_OR_PRALINE) {
+ set_RFFC5071_LOCK(drv, 1);
+ }
#endif
/* Enable reference oscillator standby */
@@ -153,17 +159,17 @@ void rffc5071_lock_test(rffc5071_driver_t* const drv)
for (int i = 0; i < NUM_LOCK_ATTEMPTS; i++) {
// Tune to 100MHz.
- rffc5071_set_frequency(drv, 100000000);
+ rffc5071_set_frequency(drv, FP_MHZ(100), true);
rffc5071_enable(drv);
// Wait 1ms.
- delay_us_at_mhz(1000, 204);
+ delay_ms(1);
// Check for lock.
lock = rffc5071_check_lock(drv);
rffc5071_disable(drv);
- delay_us_at_mhz(100, 204);
+ delay_us(100);
selftest.mixer_locks[i] = lock;
}
@@ -176,13 +182,19 @@ void rffc5071_lock_test(rffc5071_driver_t* const drv)
bool rffc5071_check_lock(rffc5071_driver_t* const drv)
{
-#ifdef PRALINE
- return gpio_read(drv->gpio_ld);
-#else
- set_RFFC5071_READSEL(drv, 0b0001);
- rffc5071_regs_commit(drv);
- return !!(rffc5071_reg_read(drv, RFFC5071_READBACK_REG) & 0x8000);
+#ifdef IS_PRALINE
+ if (IS_PRALINE) {
+ return gpio_read(drv->gpio_ld);
+ }
#endif
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ set_RFFC5071_READSEL(drv, 0b0001);
+ rffc5071_regs_commit(drv);
+ return !!(rffc5071_reg_read(drv, RFFC5071_READBACK_REG) & 0x8000);
+ }
+#endif
+ return false;
}
static uint16_t rffc5071_spi_read(rffc5071_driver_t* const drv, uint8_t r)
@@ -190,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, data, 2);
+ spi_bus_transfer(drv->bus, drv->bus->config, data, 2);
return data[1];
}
@@ -199,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, data, 2);
+ spi_bus_transfer(drv->bus, drv->bus->config, data, 2);
}
uint16_t rffc5071_reg_read(rffc5071_driver_t* const drv, uint8_t r)
@@ -257,23 +269,27 @@ void rffc5071_enable(rffc5071_driver_t* const drv)
}
}
-#define FREQ_ONE_MHZ (1000ULL * 1000ULL)
-#define REF_FREQ (40 * FREQ_ONE_MHZ)
-#define LO_MAX (5400 * FREQ_ONE_MHZ)
+#define REF_FREQ_HZ (40ULL * (1000ULL * 1000ULL))
+#define MIN_LO FP_KHZ(84375)
+#define MAX_LO FP_MHZ(5400)
-/* configure frequency synthesizer (lo in Hz) */
-uint64_t rffc5071_config_synth(rffc5071_driver_t* const drv, uint64_t lo)
+/* configure frequency synthesizer (lo in 1/(2**24) Hz) */
+fp_40_24_t rffc5071_config_synth(rffc5071_driver_t* const drv, fp_40_24_t lo, bool program)
{
- uint64_t fvco;
+ fp_40_24_t fvco;
uint8_t fbkdivlog;
uint16_t n;
- uint64_t tune_freq_hz;
+ fp_40_24_t tune_freq;
uint16_t p1nmsb;
uint8_t p1nlsb;
+ uint8_t charge_pump_leakage;
+
+ lo = MIN(lo, MAX_LO);
+ lo = MAX(lo, MIN_LO);
/* Calculate n_lo (no division) */
uint8_t n_lo = 0;
- uint64_t x = LO_MAX >> 1;
+ fp_40_24_t x = MAX_LO >> 1;
while ((x >= lo) && (n_lo < 5)) {
n_lo++;
x >>= 1;
@@ -285,15 +301,15 @@ uint64_t rffc5071_config_synth(rffc5071_driver_t* const drv, uint64_t lo)
* Higher charge pump leakage setting and fbkdivlog are required above
* 3.2 GHz.
*/
- if (fvco > (3200 * FREQ_ONE_MHZ)) {
+ if (fvco > FP_MHZ(3200)) {
fbkdivlog = 2;
- set_RFFC5071_PLLCPL(drv, 3);
+ charge_pump_leakage = 3;
} else {
fbkdivlog = 1;
- set_RFFC5071_PLLCPL(drv, 2);
+ charge_pump_leakage = 2;
}
- uint64_t tmp_n = (fvco << (24ULL - fbkdivlog)) / REF_FREQ;
+ uint64_t tmp_n = (fvco / REF_FREQ_HZ) >> fbkdivlog;
/* Round to nearest step = ref_MHz / 2**s. For s=6, step=625000 Hz */
/* This also ensures the lowest 22-s fractional bits are set to 0. */
@@ -301,33 +317,40 @@ uint64_t rffc5071_config_synth(rffc5071_driver_t* const drv, uint64_t lo)
const uint8_t d = (24 - fbkdivlog + n_lo) - s;
tmp_n = ((tmp_n + (1 << (d - 1))) >> d) << d;
- n = tmp_n >> 24ULL;
- p1nmsb = (tmp_n >> 8ULL) & 0xffff;
- p1nlsb = tmp_n & 0xff;
+ tune_freq = ((tmp_n * REF_FREQ_HZ) << fbkdivlog) >> n_lo;
- tune_freq_hz = (tmp_n * REF_FREQ) >> (24 - fbkdivlog + n_lo);
+ if (program) {
+ set_RFFC5071_PLLCPL(drv, charge_pump_leakage);
- /* Path 2 */
- set_RFFC5071_P2LODIV(drv, n_lo);
- set_RFFC5071_P2N(drv, n);
- set_RFFC5071_P2PRESC(drv, fbkdivlog);
- set_RFFC5071_P2NMSB(drv, p1nmsb);
- if (s > 14) {
- /* Only set when the step size is small enough. */
- set_RFFC5071_P2NLSB(drv, p1nlsb);
+ n = tmp_n >> 24ULL;
+ p1nmsb = (tmp_n >> 8ULL) & 0xffff;
+ p1nlsb = tmp_n & 0xff;
+
+ /* Path 2 */
+ set_RFFC5071_P2LODIV(drv, n_lo);
+ set_RFFC5071_P2N(drv, n);
+ set_RFFC5071_P2PRESC(drv, fbkdivlog);
+ set_RFFC5071_P2NMSB(drv, p1nmsb);
+ if (s > 14) {
+ /* Only set when the step size is small enough. */
+ set_RFFC5071_P2NLSB(drv, p1nlsb);
+ }
+
+ rffc5071_regs_commit(drv);
}
- rffc5071_regs_commit(drv);
-
- return tune_freq_hz;
+ return tune_freq;
}
-uint64_t rffc5071_set_frequency(rffc5071_driver_t* const drv, uint64_t hz)
+fp_40_24_t rffc5071_set_frequency(
+ rffc5071_driver_t* const drv,
+ fp_40_24_t lo,
+ bool program)
{
- uint32_t tune_freq;
+ fp_40_24_t tune_freq;
- tune_freq = rffc5071_config_synth(drv, hz);
- if (enabled) {
+ tune_freq = rffc5071_config_synth(drv, lo, program);
+ if (enabled && program) {
set_RFFC5071_RELOK(drv, 1);
rffc5071_regs_commit(drv);
}
@@ -344,9 +367,19 @@ void rffc5071_set_gpo(rffc5071_driver_t* const drv, uint8_t gpo)
rffc5071_regs_commit(drv);
}
-#ifdef PRALINE
+#ifdef IS_PRALINE
bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state)
{
+ // This is only supported on Praline hardware.
+ //
+ // For all other boards we'll just return true to avoid a situation where a
+ // a caller is waiting for a lock signal that will never be detected.
+ #ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ return true;
+ }
+ #endif
+
// The RFFC5072 can be configured to output PLL lock status on
// GPO4. The lock detect signal is produced by a window detector
// on the VCO tuning voltage. It goes high to show PLL lock when
diff --git a/firmware/common/rffc5071.h b/firmware/common/rffc5071.h
index 506f1a09..40971061 100644
--- a/firmware/common/rffc5071.h
+++ b/firmware/common/rffc5071.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2014 Jared Boone
*
* This file is part of HackRF.
@@ -20,12 +20,12 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __RFFC5071_H
-#define __RFFC5071_H
+#pragma once
#include
#include
+#include "fixed_point.h"
#include "gpio.h"
#include "spi_bus.h"
@@ -35,7 +35,7 @@
typedef struct {
spi_bus_t* const bus;
gpio_t gpio_reset;
-#ifdef PRALINE
+#ifdef IS_PRALINE
gpio_t gpio_ld;
#endif
uint16_t regs[RFFC5071_NUM_REGS];
@@ -60,16 +60,17 @@ extern void rffc5071_reg_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t
* provided routines for those operations. */
extern void rffc5071_regs_commit(rffc5071_driver_t* const drv);
-/* Set frequency (Hz). */
-extern uint64_t rffc5071_set_frequency(rffc5071_driver_t* const drv, uint64_t hz);
+/* Set frequency in 1/(2**24) Hz. */
+extern fp_40_24_t rffc5071_set_frequency(
+ rffc5071_driver_t* const drv,
+ fp_40_24_t lo,
+ bool program);
extern void rffc5071_enable(rffc5071_driver_t* const drv);
extern void rffc5071_disable(rffc5071_driver_t* const drv);
extern void rffc5071_set_gpo(rffc5071_driver_t* const drv, uint8_t);
-#ifdef PRALINE
+#ifdef IS_PRALINE
extern bool rffc5071_poll_ld(rffc5071_driver_t* const drv, uint8_t* prelock_state);
#endif
extern bool rffc5071_check_lock(rffc5071_driver_t* const drv);
-
-#endif // __RFFC5071_H
diff --git a/firmware/common/rffc5071_regs.def b/firmware/common/rffc5071_regs.def
index 022010c0..dd352a03 100644
--- a/firmware/common/rffc5071_regs.def
+++ b/firmware/common/rffc5071_regs.def
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __RFFC5071_REGS_DEF
-#define __RFFC5071_REGS_DEF
+#pragma once
#define RFFC5071_REG_SET_CLEAN(_d, _r) (_d->regs_dirty &= ~(1UL<<_r))
#define RFFC5071_REG_SET_DIRTY(_d, _r) (_d->regs_dirty |= (1UL<<_r))
@@ -257,5 +256,3 @@ __MREG__(RFFC5071_LFSR,30,9,1)
__MREG__(RFFC5071_TSEL,30,10,2)
__MREG__(RFFC5071_TMUX,30,12,3)
__MREG__(RFFC5071_TEN,30,15,1)
-
-#endif // __RFFC5071_REGS_DEF
diff --git a/firmware/common/rffc5071_spi.h b/firmware/common/rffc5071_spi.h
index 7df2d8bf..552d0769 100644
--- a/firmware/common/rffc5071_spi.h
+++ b/firmware/common/rffc5071_spi.h
@@ -20,15 +20,14 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __RFFC5071_SPI_H
-#define __RFFC5071_SPI_H
+#pragma once
#include
#include "gpio.h"
#include "spi_bus.h"
-typedef struct rffc5071_spi_config_t {
+typedef struct {
gpio_t gpio_select;
gpio_t gpio_clock;
gpio_t gpio_data;
@@ -41,5 +40,3 @@ void rffc5071_spi_transfer_gather(
spi_bus_t* const bus,
const spi_transfer_t* const transfer,
const size_t count);
-
-#endif // __RFFC5071_SPI_H
diff --git a/firmware/common/rom_iap.c b/firmware/common/rom_iap.c
index a8f185ad..ce1f113b 100644
--- a/firmware/common/rom_iap.c
+++ b/firmware/common/rom_iap.c
@@ -22,9 +22,7 @@
#include
-#include "hackrf_core.h"
#include "rom_iap.h"
-#include "spi_bus.h"
#include "w25q80bv.h"
#define ROM_IAP_ADDR (0x10400100)
@@ -78,7 +76,6 @@ 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) {
diff --git a/firmware/common/rom_iap.h b/firmware/common/rom_iap.h
index 50cfcf93..8b027342 100644
--- a/firmware/common/rom_iap.h
+++ b/firmware/common/rom_iap.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __ROM_IAP__
-#define __ROM_IAP__
+#pragma once
#include
#include
@@ -93,7 +92,7 @@ typedef enum
INVALID_FLASH_UNIT = 0x00000014, /* Invalid flash unit. */
USER_CODE_CHECKSUM = 0x00000015,
ERROR_SETTING_ACTIVE_PARTITION = 0x00000016,
-
+
/* Special Error */
ERROR_IAP_NOT_IMPLEMENTED = 0x00000100 /* IAP is not implemented in this part */
} isp_iap_ret_code_t;
@@ -118,5 +117,3 @@ typedef struct {
bool iap_is_implemented(void);
isp_iap_ret_code_t iap_cmd_call(iap_cmd_res_t* iap_cmd_res);
-
-#endif //__ROM_IAP__
diff --git a/firmware/common/sct.h b/firmware/common/sct.h
index 61c43199..5bedfa75 100644
--- a/firmware/common/sct.h
+++ b/firmware/common/sct.h
@@ -19,6 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
+#pragma once
+
#include
#include
diff --git a/firmware/common/selftest.h b/firmware/common/selftest.h
index 86ee2213..cea60da2 100644
--- a/firmware/common/selftest.h
+++ b/firmware/common/selftest.h
@@ -19,8 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __SELFTEST_H
-#define __SELFTEST_H
+#pragma once
#include
#include
@@ -38,23 +37,22 @@ typedef uint8_t test_result_t;
typedef struct {
uint16_t mixer_id;
-#ifndef RAD1O
+#ifdef IS_NOT_RAD1O
bool mixer_locks[NUM_LOCK_ATTEMPTS];
#endif
-#ifdef PRALINE
+#ifdef IS_PRALINE
uint16_t max2831_mux_rssi_1;
uint16_t max2831_mux_temp;
uint16_t max2831_mux_rssi_2;
bool max2831_mux_test_ok;
-#else
+#endif
uint16_t max283x_readback_bad_value;
uint16_t max283x_readback_expected_value;
uint8_t max283x_readback_register_count;
uint8_t max283x_readback_total_registers;
-#endif
uint8_t si5351_rev_id;
bool si5351_readback_ok;
-#ifdef PRALINE
+#ifdef IS_PRALINE
test_result_t fpga_image_load;
test_result_t fpga_spi;
test_result_t sgpio_rx;
@@ -76,5 +74,3 @@ typedef struct {
} selftest_t;
extern selftest_t selftest;
-
-#endif // __SELFTEST_H
diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c
index f364d7b0..1528933a 100644
--- a/firmware/common/sgpio.c
+++ b/firmware/common/sgpio.c
@@ -29,18 +29,86 @@
#include
#include "platform_detect.h"
+#include "platform_gpio.h"
#include "platform_scu.h"
#include "sgpio.h"
-#if !defined(PRALINE)
- #include "hackrf_core.h"
+#ifdef IS_NOT_PRALINE
+ #include "clock_io.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_configure_pin_functions(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);
@@ -57,24 +125,29 @@ void sgpio_configure_pin_functions(sgpio_config_t* const config)
scu_pinmux(scu->PINMUX_SGPIO14, scu->PINMUX_SGPIO14_PINCFG); /* GPIO5[13] */
scu_pinmux(scu->PINMUX_SGPIO15, scu->PINMUX_SGPIO15_PINCFG); /* GPIO5[14] */
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
-#if defined(HACKRF_ONE)
+#ifdef IS_H1_R9
+ if (IS_H1_R9) {
scu_pinmux(
scu->H1R9_TRIGGER_EN,
SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[5] */
+ }
#endif
- } else {
+#ifdef IS_NOT_H1_R9
+ if (IS_NOT_H1_R9) {
scu_pinmux(
scu->TRIGGER_EN,
SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[12] */
}
+#endif
sgpio_cpld_set_mixer_invert(config, 0);
gpio_output(config->gpio_q_invert);
-#ifndef PRALINE
- trigger_enable(false);
- gpio_output(config->gpio_trigger_enable);
+#ifdef IS_NOT_PRALINE
+ if (IS_NOT_PRALINE) {
+ trigger_enable(false);
+ gpio_output(config->gpio_trigger_enable);
+ }
#endif
}
@@ -157,11 +230,11 @@ void sgpio_configure(sgpio_config_t* const config, const sgpio_direction_t direc
SGPIO_OUT_MUX_CFG_P_OE_CFG(0) // gpio_oe (state set by GPIO_OEREG)
| SGPIO_OUT_MUX_CFG_P_OUT_CFG(0) // dout_doutm1 (1-bit mode)
;
- SGPIO_OUT_MUX_CFG(10) = // GPIO10: Output: disable
+ SGPIO_OUT_MUX_CFG(10) = // GPIO10: Output: disable
SGPIO_OUT_MUX_CFG_P_OE_CFG(0) // gpio_oe (state set by GPIO_OEREG)
| SGPIO_OUT_MUX_CFG_P_OUT_CFG(4) // gpio_out (level set by GPIO_OUTREG)
;
- SGPIO_OUT_MUX_CFG(11) = // GPIO11: Output: direction
+ SGPIO_OUT_MUX_CFG(11) = // GPIO11: Output: direction
SGPIO_OUT_MUX_CFG_P_OE_CFG(0) // gpio_oe (state set by GPIO_OEREG)
| SGPIO_OUT_MUX_CFG_P_OUT_CFG(4) // gpio_out (level set by GPIO_OUTREG)
;
diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h
index 08d7d222..0aebdb5f 100644
--- a/firmware/common/sgpio.h
+++ b/firmware/common/sgpio.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __SGPIO_H__
-#define __SGPIO_H__
+#pragma once
#include
#include
@@ -33,14 +32,15 @@ typedef enum {
SGPIO_DIRECTION_TX,
} sgpio_direction_t;
-typedef struct sgpio_config_t {
+typedef struct {
gpio_t gpio_q_invert;
-#ifndef PRALINE
+#ifdef IS_NOT_PRALINE
gpio_t gpio_trigger_enable;
#endif
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);
@@ -51,4 +51,5 @@ 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);
-#endif //__SGPIO_H__
+/* Driver configuration instance. */
+extern sgpio_config_t sgpio_config;
diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c
index ceb9f4a9..a455a23e 100644
--- a/firmware/common/si5351c.c
+++ b/firmware/common/si5351c.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2012 Jared Boone
*
* This file is part of HackRF.
@@ -22,32 +22,38 @@
#include
#include
+#include
-#if defined(HACKRF_ONE)
- #include
-#endif
-
-#include "clkin.h"
+#include "clock_io.h"
#include "delay.h"
+#include "i2c_lpc.h"
#include "platform_detect.h"
#include "selftest.h"
#include "si5351c.h"
-#if defined(HACKRF_ONE)
+#ifdef IS_HACKRF_ONE
+ #include
#include "gpio.h"
#include "platform_gpio.h"
#include "platform_scu.h"
#endif
-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;
+#include "si5351c_regs.def"
+
+/* Driver instance. */
+si5351c_driver_t si5351c = {
+ .bus = &i2c0,
+ .i2c_address = 0x60,
+};
/* 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};
- si5351c_write(drv, data_tx, 2);
+ 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);
+ }
}
/* read single register */
@@ -56,72 +62,100 @@ 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);
- return data_rx[0];
+ uint8_t val = data_rx[0];
+ if (reg < SI5351C_CACHED_REGS) {
+ drv->regs[reg] = val;
+ si5351c_reg_set_clean(drv, reg);
+ }
+ return val;
}
-/*
- * 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)
+/* Commit changes to register values. */
+void si5351c_regs_commit(si5351c_driver_t* drv)
{
- i2c_bus_transfer(drv->bus, drv->i2c_address, data, data_count, NULL, 0);
+ 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;
}
/* Disable all CLKx outputs. */
void si5351c_disable_all_outputs(si5351c_driver_t* const drv)
{
- outputs_disabled = 0xff;
- uint8_t data[] = {3, outputs_disabled};
- si5351c_write(drv, data, sizeof(data));
+ set_all_CLK_OEB(drv, SI5351C_OUTPUT_DISABLE);
+ si5351c_regs_commit(drv);
}
/* Disable all CLKx outputs using selected PLL. */
-void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_t pll)
+void si5351c_disable_pll_outputs(si5351c_driver_t* const drv, si5351c_pll_mask_t mask)
{
- /*
- * 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;
+ /* 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);
+ }
}
-
- 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));
+ si5351c_regs_commit(drv);
}
/* Turn off OEB pin control for all CLKx */
void si5351c_disable_oeb_pin_control(si5351c_driver_t* const drv)
{
- uint8_t data[] = {9, 0xFF};
- si5351c_write(drv, data, sizeof(data));
+ set_all_OEB_MASK(drv, true);
+ si5351c_regs_commit(drv);
}
/* Power down all CLKx */
void si5351c_power_down_all_clocks(si5351c_driver_t* const 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));
+ set_all_CLK_PDN(drv, true);
+ set_FBA_INT(drv, true);
+ set_FBB_INT(drv, true);
+ si5351c_regs_commit(drv);
}
/*
@@ -131,8 +165,8 @@ void si5351c_power_down_all_clocks(si5351c_driver_t* const drv)
*/
void si5351c_set_crystal_configuration(si5351c_driver_t* const drv)
{
- uint8_t data[] = {183, 0x80};
- si5351c_write(drv, data, sizeof(data));
+ set_XTAL_CL(drv, SI5351C_XTAL_8PF);
+ si5351c_regs_commit(drv);
}
/*
@@ -141,8 +175,10 @@ void si5351c_set_crystal_configuration(si5351c_driver_t* const drv)
*/
void si5351c_enable_xo_and_ms_fanout(si5351c_driver_t* const drv)
{
- uint8_t data[] = {187, 0xD0};
- si5351c_write(drv, data, sizeof(data));
+ set_CLKIN_FANOUT_EN(drv, true);
+ set_XO_FANOUT_EN(drv, true);
+ set_MS_FANOUT_EN(drv, true);
+ si5351c_regs_commit(drv);
}
/*
@@ -150,51 +186,40 @@ 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_pll_sources(
- si5351c_driver_t* const drv,
- const enum pll_sources source)
+void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input)
{
- uint8_t data[] = {15, 0x00};
-
- if (source == PLL_SOURCE_CLKIN) {
- data[1] = 0x0c;
- }
- si5351c_write(drv, data, sizeof(data));
+ set_CLKIN_DIV(drv, SI5351C_DIV_1);
+ set_PLLA_SRC(drv, input);
+ set_PLLB_SRC(drv, input);
+ si5351c_regs_commit(drv);
}
/* MultiSynth NA (PLLA) and NB (PLLB) */
void si5351c_configure_pll_multisynth(
si5351c_driver_t* const drv,
- const enum pll_sources source)
+ const si5351c_input_t input)
{
- /* 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;
+ 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);
}
- si5351c_write(drv, data, sizeof(data));
-
- /* Apply same configuration to PLL B. */
- data[0] = 34;
- si5351c_write(drv, data, sizeof(data));
+ si5351c_regs_commit(drv);
}
-void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll)
+void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask)
{
- 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_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);
si5351c_enable_clock_outputs(drv);
}
@@ -218,155 +243,37 @@ void si5351c_configure_multisynth(
* ...
* 7 means divide by 128
*/
- 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));
+ 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);
}
void si5351c_configure_clock_control(si5351c_driver_t* const drv)
{
- 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;
+ 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);
}
- /* 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 */
- };
- if (detected_platform() == BOARD_ID_HACKRF1_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;
- }
-#ifdef 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));
+ si5351c_regs_commit(drv);
}
-#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)
{
- /* 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 */
-
-#ifndef PRALINE
- uint8_t 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);
-#else
- uint8_t 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);
+ for (int i = 0; i < 8; i++) {
+ set_CLK_OEB(
+ drv,
+ i,
+ drv->clk[i].output_enable ? SI5351C_OUTPUT_ENABLE :
+ SI5351C_OUTPUT_DISABLE);
}
-#endif
- uint8_t clkout = 3;
-
- /* HackRF One r9 has only three clock generator outputs. */
- if (detected_platform() == BOARD_ID_HACKRF1_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);
- }
-
- 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;
-
-#if defined(HACKRF_ONE)
- if (detected_platform() == BOARD_ID_HACKRF1_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
+ si5351c_regs_commit(drv);
}
void si5351c_set_int_mode(
@@ -374,47 +281,39 @@ void si5351c_set_int_mode(
const uint_fast8_t ms_number,
const uint_fast8_t on)
{
- 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);
- }
+ set_MS_INT(drv, ms_number, on);
+ si5351c_regs_commit(drv);
}
-void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source)
+void si5351c_change_input(si5351c_driver_t* const drv, si5351c_input_t input)
{
- if (source == active_clock_source) {
+ if (drv->input_initialized && input == drv->active_input) {
return;
}
si5351c_disable_all_outputs(drv);
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
-#if defined(HACKRF_ONE)
+#ifdef IS_H1_R9
+ if (IS_H1_R9) {
/*
* HackRF One r9 always uses PLL A on the XTAL input
* but externally switches that input to CLKIN.
*/
- si5351c_configure_pll_sources(drv, PLL_SOURCE_XTAL);
- if (source == PLL_SOURCE_CLKIN) {
+ si5351c_configure_inputs(drv, SI5351C_INPUT_XTAL);
+ if (input == SI5351C_INPUT_CLKIN) {
gpio_set(platform_gpio()->h1r9_clkin_en);
} else {
gpio_clear(platform_gpio()->h1r9_clkin_en);
}
-#endif
- } else {
- si5351c_configure_pll_sources(drv, source);
}
- si5351c_configure_pll_multisynth(drv, source);
- active_clock_source = source;
- si5351c_reset_pll(drv, SI5351C_PLL_BOTH);
+#endif
+#ifdef IS_NOT_H1_R9
+ if (IS_NOT_H1_R9) {
+ si5351c_configure_inputs(drv, input);
+ }
+#endif
+ si5351c_configure_pll_multisynth(drv, input);
+ drv->active_input = input;
+ drv->input_initialized = true;
+ si5351c_reset_plls(drv, SI5351C_PLL_MASK_BOTH);
}
bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv)
@@ -423,31 +322,93 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv)
uint32_t f = clkin_frequency();
return (f > 9000000) && (f < 11000000);
} else {
- return (si5351c_read_single(drv, 0) & SI5351C_LOS) == 0;
+ si5351c_read_single(drv, LOS_CLKIN);
+ bool los_clkin = get_LOS_CLKIN(drv);
+ return !los_clkin;
}
}
-void si5351c_clkout_enable(si5351c_driver_t* const drv, uint8_t enable)
+static void si5351c_clkout_ms_enable(si5351c_driver_t* const drv, bool enable)
{
- clkout_enabled = (enable > 0);
+ drv->clk[drv->clkout_id].output_enable = enable;
+ drv->clk[drv->clkout_id].power_down = !enable;
- 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, clkout, 80 * 128 - 512, 0, 1, 0);
+ si5351c_configure_multisynth(drv, drv->clkout_id, 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 */
- selftest.si5351_rev_id = si5351c_read_single(drv, 0) & SI5351C_REVID;
+ si5351c_read_single(drv, REVID);
+ selftest.si5351_rev_id = get_REVID(drv);
/* Read back interrupt status mask register, flip the mask bits and verify. */
uint8_t int_mask = si5351c_read_single(drv, 2);
@@ -466,8 +427,24 @@ void si5351c_init(si5351c_driver_t* const drv)
selftest.report.pass = false;
}
-#if defined(HACKRF_ONE)
- if (detected_platform() == BOARD_ID_HACKRF1_R9) {
+ /* 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();
const platform_scu_t* scu = platform_scu();
@@ -487,7 +464,170 @@ void si5351c_init(si5351c_driver_t* const drv)
gpio_output(gpio->h1r9_mcu_clk_en);
}
#endif
- (void) drv;
+
+ 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
}
/*
@@ -502,8 +642,6 @@ void si5351c_set_phase(
const uint8_t ms_number,
const uint8_t offset)
{
- const uint8_t address = 165 + ms_number;
- if (ms_number < 8) {
- si5351c_write_single(drv, address, offset & 0x7f);
- }
+ set_CLK_PHOFF(drv, ms_number, offset);
+ si5351c_regs_commit(drv);
}
diff --git a/firmware/common/si5351c.h b/firmware/common/si5351c.h
index 52438886..fd2d1aed 100644
--- a/firmware/common/si5351c.h
+++ b/firmware/common/si5351c.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets
+ * Copyright 2012-2026 Great Scott Gadgets
* Copyright 2012 Jared Boone
*
* This file is part of HackRF.
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __SI5351C_H
-#define __SI5351C_H
+#pragma once
#ifdef __cplusplus
extern "C" {
@@ -29,50 +28,86 @@ extern "C" {
#include
#include
-#include
#include "i2c_bus.h"
#define SI_INTDIV(x) (x * 128 - 512)
-#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
+#define SI5351C_CACHED_REGS 188
typedef enum {
- SI5351C_PLL_A = 1,
- SI5351C_PLL_B = 2,
- SI5351C_PLL_BOTH = 3,
+ 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_t;
-#define SI5351C_CLK_PLL_SRC(x) ((x & SI5351C_PLL_B) << 4)
+typedef enum {
+ SI5351C_PLL_MASK_A = 1,
+ SI5351C_PLL_MASK_B = 2,
+ SI5351C_PLL_MASK_BOTH = 3,
+} si5351c_pll_mask_t;
-enum pll_sources {
- PLL_SOURCE_UNINITIALIZED = -1,
- PLL_SOURCE_XTAL = 0,
- PLL_SOURCE_CLKIN = 1,
-};
+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;
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);
@@ -80,13 +115,11 @@ 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_pll_sources(
- si5351c_driver_t* const drv,
- const enum pll_sources source);
+void si5351c_configure_inputs(si5351c_driver_t* const drv, const si5351c_input_t input);
void si5351c_configure_pll_multisynth(
si5351c_driver_t* const drv,
- const enum pll_sources source);
-void si5351c_reset_pll(si5351c_driver_t* const drv, si5351c_pll_t pll);
+ const si5351c_input_t input);
+void si5351c_reset_plls(si5351c_driver_t* const drv, si5351c_pll_mask_t mask);
void si5351c_configure_multisynth(
si5351c_driver_t* const drv,
const uint_fast8_t ms_number,
@@ -100,24 +133,22 @@ void si5351c_set_int_mode(
si5351c_driver_t* const drv,
const uint_fast8_t ms_number,
const uint_fast8_t on);
-void si5351c_set_clock_source(si5351c_driver_t* const drv, const enum pll_sources source);
+void si5351c_change_input(si5351c_driver_t* const drv, const si5351c_input_t input);
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_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_clkout_enable(si5351c_driver_t* const drv, bool enable);
+void si5351c_mcu_clkin_enable(si5351c_driver_t* const drv, bool 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
-
-#endif /* __SI5351C_H */
diff --git a/firmware/common/si5351c_regs.def b/firmware/common/si5351c_regs.def
new file mode 100644
index 00000000..5c3a6774
--- /dev/null
+++ b/firmware/common/si5351c_regs.def
@@ -0,0 +1,494 @@
+/*
+ * Copyright 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.
+ */
+
+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<regs[r] &= (uint8_t)(~(((1L<regs[r] |= (uint8_t)(((v&((1L<> 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)
diff --git a/firmware/common/spi_bus.c b/firmware/common/spi_bus.c
index a26b4bab..a667db8a 100644
--- a/firmware/common/spi_bus.c
+++ b/firmware/common/spi_bus.c
@@ -24,23 +24,36 @@
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, void* const data, const size_t count)
+void spi_bus_transfer(
+ spi_bus_t* const bus,
+ const void* const config,
+ 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);
}
diff --git a/firmware/common/spi_bus.h b/firmware/common/spi_bus.h
index cd885644..bc9c7dee 100644
--- a/firmware/common/spi_bus.h
+++ b/firmware/common/spi_bus.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __SPI_BUS_H__
-#define __SPI_BUS_H__
+#pragma once
#include
@@ -30,27 +29,30 @@ typedef struct {
const size_t count;
} spi_transfer_t;
-struct spi_bus_t; // IWYU pragma: keep - fixed in #1704
-typedef struct spi_bus_t spi_bus_t;
-
-struct spi_bus_t {
+typedef struct _spi_bus_t {
void* const obj;
const void* config;
- void (*start)(spi_bus_t* const bus, const void* const config);
- void (*stop)(spi_bus_t* const bus);
- void (*transfer)(spi_bus_t* const bus, void* const data, const size_t count);
+ void (*start)(struct _spi_bus_t* const bus, const void* const config);
+ void (*stop)(struct _spi_bus_t* const bus);
+ void (*transfer)(
+ struct _spi_bus_t* const bus,
+ void* const data,
+ const size_t count);
void (*transfer_gather)(
- spi_bus_t* const bus,
+ struct _spi_bus_t* const bus,
const spi_transfer_t* const transfers,
const size_t count);
-};
+} spi_bus_t;
void spi_bus_start(spi_bus_t* const bus, const void* const config);
void spi_bus_stop(spi_bus_t* const bus);
-void spi_bus_transfer(spi_bus_t* const bus, void* const data, const size_t count);
+void spi_bus_transfer(
+ spi_bus_t* const bus,
+ const void* const config,
+ void* const data,
+ const size_t 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);
-
-#endif /*__SPI_BUS_H__*/
diff --git a/firmware/common/spi_ssp.c b/firmware/common/spi_ssp.c
index 7c288d51..868556be 100644
--- a/firmware/common/spi_ssp.c
+++ b/firmware/common/spi_ssp.c
@@ -28,6 +28,23 @@
#include
#include
+/* Driver instances. */
+spi_bus_t spi_bus_ssp0 = {
+ .obj = (void*) SSP0_BASE,
+ .start = spi_ssp_start,
+ .stop = spi_ssp_stop,
+ .transfer = spi_ssp_transfer,
+ .transfer_gather = spi_ssp_transfer_gather,
+};
+
+spi_bus_t spi_bus_ssp1 = {
+ .obj = (void*) SSP1_BASE,
+ .start = spi_ssp_start,
+ .stop = spi_ssp_stop,
+ .transfer = spi_ssp_transfer,
+ .transfer_gather = spi_ssp_transfer_gather,
+};
+
void spi_ssp_start(spi_bus_t* const bus, const void* const _config)
{
const ssp_config_t* const config = _config;
diff --git a/firmware/common/spi_ssp.h b/firmware/common/spi_ssp.h
index 2325f49f..5e754dc4 100644
--- a/firmware/common/spi_ssp.h
+++ b/firmware/common/spi_ssp.h
@@ -20,8 +20,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __SPI_SSP_H__
-#define __SPI_SSP_H__
+#pragma once
#include
#include
@@ -31,7 +30,7 @@
#include "gpio.h"
#include "spi_bus.h"
-typedef struct ssp_config_t {
+typedef struct {
ssp_datasize_t data_bits;
ssp_cpol_cpha_t spi_mode;
uint8_t serial_clock_rate;
@@ -47,4 +46,6 @@ void spi_ssp_transfer_gather(
const spi_transfer_t* const transfers,
const size_t count);
-#endif /*__SPI_SSP_H__*/
+/* Driver instances. */
+extern spi_bus_t spi_bus_ssp0;
+extern spi_bus_t spi_bus_ssp1;
diff --git a/firmware/common/streaming.h b/firmware/common/streaming.h
index 47f466c1..47af5120 100644
--- a/firmware/common/streaming.h
+++ b/firmware/common/streaming.h
@@ -21,12 +21,9 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __STREAMING_H__
-#define __STREAMING_H__
+#pragma once
#include
void baseband_streaming_enable(sgpio_config_t* const sgpio_config);
void baseband_streaming_disable(sgpio_config_t* const sgpio_config);
-
-#endif /*__STREAMING_H__*/
diff --git a/firmware/common/transceiver_mode.h b/firmware/common/transceiver_mode.h
new file mode 100644
index 00000000..dccb71b2
--- /dev/null
+++ b/firmware/common/transceiver_mode.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012-2026 Great Scott Gadgets
+ * Copyright 2012 Jared Boone
+ * Copyright 2013 Benjamin Vernoux
+ *
+ * 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
+
+typedef enum {
+ TRANSCEIVER_MODE_OFF = 0,
+ TRANSCEIVER_MODE_RX = 1,
+ TRANSCEIVER_MODE_TX = 2,
+ TRANSCEIVER_MODE_SS = 3,
+ TRANSCEIVER_MODE_CPLD_UPDATE = 4,
+ TRANSCEIVER_MODE_RX_SWEEP = 5,
+} transceiver_mode_t;
diff --git a/firmware/common/tune_config.h b/firmware/common/tune_config.h
index dd21d028..dd81f62d 100644
--- a/firmware/common/tune_config.h
+++ b/firmware/common/tune_config.h
@@ -19,10 +19,11 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef __TUNE_CONFIG_H__
-#define __TUNE_CONFIG_H__
+#pragma once
-#ifdef PRALINE
+#include "platform_detect.h"
+
+#ifdef IS_PRALINE
#include "fpga.h"
typedef struct {
@@ -391,5 +392,3 @@ static const tune_config_t praline_tune_config_rx[] = {
};
// clang-format on
#endif
-
-#endif /*__TUNE_CONFIG_H__*/
diff --git a/firmware/common/tuning.c b/firmware/common/tuning.c
index 47724e36..9e3c41ba 100644
--- a/firmware/common/tuning.c
+++ b/firmware/common/tuning.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 Great Scott Gadgets