固件
原文来源:【https://hackrf.readthedocs.io/en/latest/index.html】机器翻译加人工修改,不正之处,请指正!
更新固件
HackRF 设备附带 SPI 闪存上的固件。只需 USB 数据线和主机即可更新固件。
这些说明允许您升级固件以利用新功能或错误修复。
如果您在本机操作系统上执行此过程有任何困难,您可以 使用 Pentoo 或 GNU Radio Live DVD来执行更新。
更新 SPI 闪存固件
要更新正在运行的 HackRF One 上的固件,请使用 hackrf_spiflash 程序:
hackrf_spiflash -w hackrf_one_usb.bin
您可以在最新发布包的 firmware-bin 目录中找到固件二进制文件 (hackrf_one_usb.bin),或者您可以从源代码编译您自己的。对于 Jawbreaker,使用 hackrf_jawbreaker_usb.bin。如果您从源代码编译,该文件将被称为 hackrf_usb.bin。
hackrf_spiflash 程序是 hackrf-tools 的一部分。
将固件映像写入 SPI 闪存时,请务必选择文件名以“.bin”结尾的固件。
将固件写入 SPI 闪存后,您可能需要通过按下 RESET 按钮或拔下并重新插入来重置 HackRF 设备。
如果您收到提到 HACKRF_ERROR_NOT_FOUND 的错误,请查看常见问题解答。这通常是可以快速解决的权限问题。
更新 CPLD
较旧版本的 HackRF 固件(2021.03.1 之前的版本)需要额外的步骤才能将比特流编程到 CPLD 中。
要更新 CPLD 映像,首先将 SPI 闪存固件、libhackrf 和 hackrf-tools 更新为您正在安装的版本。然后:
hackrf_cpldjtag -x firmware/cpld/sgpio_if/default.xsvf
几秒钟后,三个 LED 应该开始闪烁。这表明 CPLD 已成功编程。通过按下 RESET 按钮或拔下并重新插入来重置 HackRF 设备。
仅在必要时:DFU 启动
DFU 引导模式通常仅在固件无法正常工作或从未安装时才需要。
HackRF 上的 LPC4330 微控制器能够从多个不同的代码源启动。默认情况下,HackRF 从 SPI 闪存 (SPIFI) 启动。它还可以在 DFU (USB) 启动模式下启动 HackRF。在 DFU 启动模式下,HackRF 将通过 USB 进行枚举,等待使用 DFU(设备固件更新)标准通过 USB 传送代码,然后从 RAM 中执行该代码。SPIFI 在 DFU 模式下通常不被使用和改变。
要在 DFU 模式下启动 HackRF One,请在开机时按住 DFU 按钮或按下并释放 RESET 按钮。3V3 LED 亮起后松开 DFU 按钮。1V8 LED 应保持关闭状态。此时 HackRF One 已准备好通过 USB 接收固件。
要在 DFU 模式下启动 Jawbreaker,请在首次供电时短接其中一个“BOOT”接头连接器上的两个引脚。必须短接的引脚是 Jawbreaker 上接头 P32 的引脚 1 和 2。大多数 Jawbreaker 上的接头 P32 标记为“P2_8”,但在原型装置上可能标记为“2”。引脚 1 标有“VCC”。引脚 2 是中心引脚。DFU 启动后,您应该看到 VCCLED 亮起,并注意 1V8LED 不亮。此时 Jawbreaker 已准备好通过 USB 接收固件。
您应该只使用文件名以“.dfu”结尾的固件映像而不是以“.bin”结尾的固件。
仅在必要时:恢复 SPI 闪存固件
如果安装在 SPI flash 中的固件已经损坏,或者如果您是第一次编写自制的 HackRF,您将无法立即使用上述过程中列出的 hackrf_spiflash 程序。请改为执行以下步骤:
- 按照 DFU 引导说明以 DFU 引导模式启动 HackRF。
- 键入以将固件从发布包加载到 RAM 中。如果您有 Jawbreaker,请改用 hackrf_jawbreaker_usb.dfu。或者,使用将固件加载到 RAM 中并启动它。
dfu-util --device 1fc9:000c --alt 0 --download hackrf_one_usb.dfu
make -e BOARD=HACKRF_ONE RUN_FROM=RAM program
- 按照上面的 SPI 闪存固件更新程序将“.bin”固件映像写入 SPI 闪存。
获取DFU-Util
在全新安装操作系统时,您可能需要获取一份 DFU-Util。对于大多数 Linux 发行版,它应该作为一个包提供,例如在 Debian/Ubuntu 上
sudo apt-get install dfu-util
如果您使用的平台没有 dfu-util 包,可以在 dfu-util source forge 构建页面上找到构建说明。
cd ~ sudo apt-get build-dep dfu-util sudo apt-get install libusb-1.0-0-dev git clone git://git.code.sf.net/p/dfu-util/dfu-util cd dfu-util ./autogen.sh ./configure make sudo make install
现在您将在系统上安装当前版本的 DFU Util。
固件开发环境设置
固件构建说明包含在 firmware/README 下的存储库中:
https://github.com/mossmann/hackrf/blob/master/firmware/README
LPC43xx调试
存在用于 LPC43xx 的各种调试器选项。
Black Magic Probe
https://github.com/blacksphere/blackmagic
将 gdb 与 Black Magic Probe 结合使用的示例:
arm-none-eabi-gdb -n blinky.elf target extended-remote /dev/ttyACM0 monitor swdp_scan attach 1 set {int}0x40043100 = 0x10000000 load cont
如果您使用 jtag_scan 而不是 swdp_scan,则可以连接到 M0 而不是 M4,但是我上次尝试使用 M0 时,Black Magic Probe 有一些错误。
LPC-Link
(包含在 LPCXpresso 板中)
TitanMKD 取得了一些成功。请参阅 hackrf/doc/LPCXPresso_Flash_Debug_Tutorial.pdf 或 .odt 中的教程(PDF 和 OpenOffice 文档)文档链接 [ https://github.com/mossmann/hackrf/tree/master/doc )
ST-LINK/V2
硬件配置
从 STM32F4-Discovery 板开始。从 CN3 上拆下跳线。将目标的 SWD 接口连接到 CN2“SWD”连接器。
软件配置
我正在使用 libusb-1.0.9。
安装 OpenOCD-0.6.0 开发版
# Cloned at hash a21affa42906f55311ec047782a427fcbcb98994 git clone git://openocd.git.sourceforge.net/gitroot/openocd/openocd cd openocd ./bootstrap ./configure --enable-stlink --enable-buspirate --enable-jlink --enable-maintainer-mode make sudo make install
OpenOCD 配置文件
openocd配置文件
#debug_level 3 source [find interface/stlink-v2.cfg] source ./lpc4350.cfg
lpc4350.cfg
set _CHIPNAME lpc4350 set _M0_CPUTAPID 0x4ba00477 set _M4_SWDTAPID 0x2ba01477 set _M0_TAPID 0x0BA01477 set _TRANSPORT stlink_swd transport select $_TRANSPORT stlink newtap $_CHIPNAME m4 -expected-id $_M4_SWDTAPID stlink newtap $_CHIPNAME m0 -expected-id $_M0_TAPID target create $_CHIPNAME.m4 stm32_stlink -chain-position $_CHIPNAME.m4 #target create $_CHIPNAME.m0 stm32_stlink -chain-position $_CHIPNAME.m0
target.xml,来自 OpenOCD 邮件列表线程,用于修复 GDB 和更新的 OpenOCD 构建之间的通信问题。
<?xml version="1.0"?> <!DOCTYPE target SYSTEM "gdb-target.dtd"> <target> <feature name="org.gnu.gdb.arm.core"> <reg name="r0" bitsize="32" type="uint32"/> <reg name="r1" bitsize="32" type="uint32"/> <reg name="r2" bitsize="32" type="uint32"/> <reg name="r3" bitsize="32" type="uint32"/> <reg name="r4" bitsize="32" type="uint32"/> <reg name="r5" bitsize="32" type="uint32"/> <reg name="r6" bitsize="32" type="uint32"/> <reg name="r7" bitsize="32" type="uint32"/> <reg name="r8" bitsize="32" type="uint32"/> <reg name="r9" bitsize="32" type="uint32"/> <reg name="r10" bitsize="32" type="uint32"/> <reg name="r11" bitsize="32" type="uint32"/> <reg name="r12" bitsize="32" type="uint32"/> <reg name="sp" bitsize="32" type="data_ptr"/> <reg name="lr" bitsize="32"/> <reg name="pc" bitsize="32" type="code_ptr"/> <reg name="cpsr" bitsize="32" regnum="25"/> </feature> <feature name="org.gnu.gdb.arm.fpa"> <reg name="f0" bitsize="96" type="arm_fpa_ext" regnum="16"/> <reg name="f1" bitsize="96" type="arm_fpa_ext"/> <reg name="f2" bitsize="96" type="arm_fpa_ext"/> <reg name="f3" bitsize="96" type="arm_fpa_ext"/> <reg name="f4" bitsize="96" type="arm_fpa_ext"/> <reg name="f5" bitsize="96" type="arm_fpa_ext"/> <reg name="f6" bitsize="96" type="arm_fpa_ext"/> <reg name="f7" bitsize="96" type="arm_fpa_ext"/> <reg name="fps" bitsize="32"/> </feature> </target>
运行 ARM GDB
很快,我将这些东西转储到 .gdbinit 文件中。
arm-none-eabi-gdb -n target extended-remote localhost:3333 set tdesc filename target.xml monitor reset init monitor mww 0x40043100 0x10000000 monitor mdw 0x40043100 # Verify 0x0 shadow register is set properly. file lpc4350-test.axf # This is an ELF file. load # Place image into RAM. monitor reset init break main # Set a breakpoint. continue # Run to breakpoint. continue # To continue from the breakpoint. step # Step-execute the next source line. stepi # Step-execute the next processor instruction. info reg # Show processor registers.
针对不熟悉 GDB 的更多 GDB 技巧:
# Write the variable "buffer" (an array) to file "buffer.u8". dump binary value buffer.u8 buffer # Display the first 32 values in buffer whenever you halt # execution. display/32xh buffer # Print the contents of a range of registers (in this case the # CGU peripheral, starting at 0x40050014, for 46 words): x/46 0x40050014
还有更多,用于调试 ARM Cortex-M4 硬故障:
# Assuming you have a hard-fault handler wired in: display/8xw args # Examine fault-related registers: # Configurable Fault Status Register (CFSR) contains: # CFSR[15:8]: BusFault Status Register (BFSR) # "Shows the status of bus errors resulting from instruction # prefetches and data accesses." # BFSR[7]: BFARVALID: BFSR contents valid. # BFSR[5]: LSPERR: fault during FP lazy state preservation. # BFSR[4]: STKERR: derived bus fault on exception entry. # BFSR[3]: UNSTKERR: derived bus fault on exception return. # BFSR[2]: IMPRECISERR: imprecise data access error. # BFSR[1]: PRECISERR: precise data access error, faulting # address in BFAR. # BFSR[0]: IBUSERR: bus fault on instruction prefetch. Occurs # only if instruction is issued. display/xw 0xE000ED28 # BusFault Address Register (BFAR) # "Shows the address associated with a precise data access fault." # "This is the location addressed by an attempted data access that # was faulted. The BFSR shows the reason for the fault and whether # BFAR_ADDRESS is valid..." # "For unaligned access faults, the value returned is the address # requested by the instruction. This might not be the address that # faulted." display/xw 0xE000ED38
LPC43xx SGPIO 配置
LPC43xx SGPIO 外设用于在 USB 和 ADC/DAC 芯片 (MAX5864) 之间传输采样数据。SGPIO 是具有一组 32 位移位寄存器的外设。这些移位寄存器可以配置为充当不同宽度的并行接口。对于 HackRF,我们将 SGPIO 配置为一次传输八位。SGPIO 接口也可以接受外部时钟,我们用它来同步传输与采样时钟。
在当前的 HackRF 设计中,有一个 CPLD 管理 MAX5864 和 SGPIO 接口之间的接口。有四个 SGPIO 信号控制 SGPIO 数据传输:
- 时钟:确定何时传输 SGPIO 数据总线上的值。
- Direction:确定是将 MAX5864 DA (ADC) 数据驱动到 SGPIO 线上,还是 SGPIO 线驱动数据总线为 MAX5864 DD (DAC) 信号提供数据。
- 数据有效:表示 SGPIO 数据总线上的样本是有效数据。
- Transfer Enable:允许 SGPIO 与 I/Q 数据流同步。MAX5864 在每个采样周期产生/消耗两个值(正交/复值)——一个 I 值和一个 Q 值。这两个值在 SGPIO 线上复用。该信号暂停数据有效,直到应该传输 I 值。
经常问的问题
为什么不使用GPDMA而是通过SGPIO传输采样数据呢?
如果可以,那就太好了,因为这会释放大量的处理器时间。不幸的是,LPC43xx 中的 GPDMA 方案似乎不支持使用 SGPIO 外设进行外设到内存和内存到外设的传输。
您可能会观察到 SGPIO 外设可以使用切片移位寄存器中的任意位模式从 SGPIO14 和 SGPIO15 生成请求。切片中的模式决定了请求间隔。这是一个好的开始。但是,您如何指定在每次请求时读取/写入哪些 SGPIO 影子寄存器,以及这些寄存器以何种顺序随内存传输?事实证明你做不到。事实上,如果您的源或目标是“外设”,SGPIO 请求似乎根本不会引起任何传输。相反,SGPIO 请求旨在执行与 SGPIO 同步的内存到内存传输。但是,就从 SGPIO 影子寄存器获取数据或从中获取数据而言,您只能靠自己了。我相信这就是为什么用户手册中的 SGPIO 相机示例描述了一个 SGPIO 中断执行 SGPIO 影子寄存器传输,
也许如果我们只传输一个 SGPIO 影子寄存器,使用内存到内存?那么我们就不用担心SGPIO寄存器的顺序,或者哪些需要转移了。事实证明,当您切换到内存到内存传输时,您将失去外设请求生成。因此 GPDMA 将尽可能快地传输——比 SGPIO 产生/消耗的字快得多。
我真的很想在这一切上犯错,但我的所有测试都表明没有比使用 SGPIO 中断传输样本更好的使用 GPDMA 的可行解决方案。如果您想要一些示例 GPDMA 代码来进行试验,请联系 Jared(在 Discord 或 IRC 的#hackrf 上分享)。