Add hackrf_debug option to read ADC

Co-authored-by: Martin Ling <martin-git@earth.li>
This commit is contained in:
Michael Ossmann
2025-11-25 20:08:20 -05:00
parent 692da36d76
commit 29be31bf71
12 changed files with 233 additions and 22 deletions

45
firmware/common/adc.c Normal file
View File

@@ -0,0 +1,45 @@
/*
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "adc.h"
#include <libopencm3/lpc43xx/adc.h>
#include <libopencm3/lpc43xx/scu.h>
uint16_t adc_read(uint8_t pin)
{
bool alt_pin = (pin & 0x80);
pin &= ~0x80;
uint8_t pin_mask = (1 << pin);
if (alt_pin) {
SCU_ENAIO0 |= pin_mask;
} else {
SCU_ENAIO0 &= ~pin_mask;
}
ADC0_CR = ADC_CR_SEL(pin_mask) | ADC_CR_CLKDIV(45) | ADC_CR_PDN | ADC_CR_START(1);
while (!(ADC0_GDR & ADC_DR_DONE) || (((ADC0_GDR >> 24) & 0x7) != pin))
;
return (ADC0_GDR >> 6) & 0x03FF;
}
void adc_off(void)
{
ADC0_CR = 0;
}

30
firmware/common/adc.h Normal file
View File

@@ -0,0 +1,30 @@
/*
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __ADC_H__
#define __ADC_H__
#include <stdint.h>
uint16_t adc_read(uint8_t pin);
void adc_off(void);
#endif // __ADC_H__

View File

@@ -954,7 +954,7 @@ void cpu_clock_init(void)
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_ADC0_CFG = 0;
CCU1_CLK_APB3_ADC1_CFG = 0;
CCU1_CLK_APB3_CAN0_CFG = 0;
CCU1_CLK_APB3_DAC_CFG = 0;

View File

@@ -23,9 +23,9 @@
#include "firmware_info.h"
#include "gpio_lpc.h"
#include "hackrf_core.h"
#include "adc.h"
#include <libopencm3/lpc43xx/scu.h>
#include <libopencm3/lpc43xx/adc.h>
static board_id_t platform = BOARD_ID_UNDETECTED;
static board_rev_t revision = BOARD_REV_UNDETECTED;
@@ -64,23 +64,6 @@ static struct gpio_t gpio_led1 = GPIO(2, 1);
static struct gpio_t gpio_led2 = GPIO(2, 2);
static struct gpio_t gpio_led3 = GPIO(2, 8);
/*
* Return 10-bit ADC result.
*/
uint16_t adc_read(uint8_t pin)
{
pin &= 0x7;
uint8_t pin_mask = (1 << pin);
ADC0_CR = ADC_CR_SEL(pin_mask) | ADC_CR_CLKDIV(45) | ADC_CR_PDN | ADC_CR_START(1);
while (!(ADC0_GDR & ADC_DR_DONE) || (((ADC0_GDR >> 24) & 0x7) != pin)) {}
return (ADC0_GDR >> 6) & 0x03FF;
}
void adc_off(void)
{
ADC0_CR = 0;
}
/*
* Starting with r6, HackRF One has pin straps on ADC pins that indicate
* hardware revision. Those pins were unconnected prior to r6, so we test for

View File

@@ -191,6 +191,7 @@ macro(DeclareTargets)
${PATH_HACKRF_FIRMWARE_COMMON}/radio.c
${PATH_HACKRF_FIRMWARE_COMMON}/selftest.c
${PATH_HACKRF_FIRMWARE_COMMON}/m0_state.c
${PATH_HACKRF_FIRMWARE_COMMON}/adc.c
)
if(BOARD STREQUAL "RAD1O")

View File

@@ -62,6 +62,7 @@ set(SRC_M4
usb_api_sweep.c
usb_api_selftest.c
usb_api_ui.c
usb_api_adc.c
"${PATH_HACKRF_FIRMWARE_COMMON}/usb_queue.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/fault_handler.c"
"${PATH_HACKRF_FIRMWARE_COMMON}/cpld_jtag.c"

View File

@@ -48,6 +48,7 @@
#include "usb_api_operacake.h"
#include "usb_api_praline.h"
#include "usb_api_selftest.h"
#include "usb_api_adc.h"
#include "operacake.h"
#include "usb_api_sweep.h"
#include "usb_api_transceiver.h"
@@ -148,6 +149,7 @@ static usb_request_handler_fn vendor_request_handler[] = {
NULL,
#endif
usb_vendor_request_read_selftest,
usb_vendor_request_adc_read,
};
static const uint32_t vendor_request_handler_count =

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include <stddef.h>
#include <usb_queue.h>
#include "adc.h"
#include "usb_api_adc.h"
usb_request_status_t usb_vendor_request_adc_read(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
if (stage == USB_TRANSFER_STAGE_SETUP) {
if ((endpoint->setup.index & ~0x80) > 7) {
return USB_REQUEST_STATUS_STALL;
}
uint16_t value = adc_read(endpoint->setup.index);
adc_off();
endpoint->buffer[0] = value & 0xff;
endpoint->buffer[1] = value >> 8;
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
2,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
return USB_REQUEST_STATUS_OK;
}
return USB_REQUEST_STATUS_OK;
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2025 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __USB_API_ADC_H__
#define __USB_API_ADC_H__
#include <usb_type.h>
#include <usb_request.h>
usb_request_status_t usb_vendor_request_adc_read(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage);
#endif // __USB_API_ADC_H__

View File

@@ -577,6 +577,7 @@ static void usage()
printf("\t-u, --ui <1/0>: enable/disable UI\n");
printf("\t-l, --leds <state>: configure LED state (0 for all off, 1 for default)\n");
printf("\t-t, --selftest: read self-test report\n");
printf("\t-a, --adc <channel>: read value from an ADC channel. Add 0x80 for alternate pin\n");
printf("\nExamples:\n");
printf("\thackrf_debug --si5351c -n 0 -r # reads from si5351c register 0\n");
printf("\thackrf_debug --si5351c -c # displays si5351c multisynth configuration\n");
@@ -608,6 +609,7 @@ static struct option long_options[] = {
{"ui", required_argument, 0, 'u'},
{"leds", required_argument, 0, 'l'},
{"selftest", no_argument, 0, 't'},
{"adc", required_argument, 0, 'a'},
{0, 0, 0, 0},
};
@@ -636,6 +638,7 @@ int main(int argc, char** argv)
uint32_t clkin_state;
uint32_t narrowband_state;
uint32_t bitstream_index;
uint32_t adc_channel;
bool set_tx_limit = false;
bool set_rx_limit = false;
bool set_p1 = false;
@@ -644,6 +647,7 @@ int main(int argc, char** argv)
bool set_narrowband = false;
bool set_fpga_bitstream = false;
bool read_selftest = false;
bool read_adc = false;
int result = hackrf_init();
if (result) {
@@ -656,7 +660,7 @@ int main(int argc, char** argv)
while ((opt = getopt_long(
argc,
argv,
"n:rw:d:cmsfg1:2:C:N:P:ST:R:h?u:l:t",
"n:rw:d:cmsfg1:2:C:N:P:ST:R:h?u:l:ta:",
long_options,
&option_index)) != EOF) {
switch (opt) {
@@ -764,6 +768,11 @@ int main(int argc, char** argv)
read_selftest = true;
break;
case 'a':
read_adc = true;
result = parse_int(optarg, &adc_channel);
break;
case 'h':
case '?':
usage();
@@ -803,7 +812,7 @@ int main(int argc, char** argv)
if (!(write || read || dump_config || dump_state || set_tx_limit ||
set_rx_limit || set_ui || set_leds || set_p1 || set_p2 || set_clkin ||
set_narrowband || set_fpga_bitstream || read_selftest)) {
set_narrowband || set_fpga_bitstream || read_selftest || read_adc)) {
fprintf(stderr, "Specify read, write, or config option.\n");
usage();
return EXIT_FAILURE;
@@ -811,7 +820,7 @@ int main(int argc, char** argv)
if (part == PART_NONE && !set_ui && !dump_state && !set_tx_limit &&
!set_rx_limit && !set_leds && !set_p1 && !set_p2 && !set_clkin &&
!set_narrowband && !set_fpga_bitstream && !read_selftest) {
!set_narrowband && !set_fpga_bitstream && !read_selftest && !read_adc) {
fprintf(stderr, "Specify a part to read, write, or print config from.\n");
usage();
return EXIT_FAILURE;
@@ -964,6 +973,21 @@ int main(int argc, char** argv)
printf("%s", selftest.msg);
}
if (read_adc) {
uint16_t value;
result = hackrf_read_adc(device, adc_channel, &value);
if (result != HACKRF_SUCCESS) {
printf("hackrf_read_adc() failed: %s (%d)\n",
hackrf_error_name(result),
result);
return EXIT_FAILURE;
}
printf("ADC0_%d (%s pin): %d\n",
adc_channel & 0x7,
adc_channel & 0x80 ? "alternate" : "dedicated",
value);
}
result = hackrf_close(device);
if (result) {
printf("hackrf_close() failed: %s (%d)\n",

View File

@@ -109,6 +109,7 @@ typedef enum {
HACKRF_VENDOR_REQUEST_SET_FPGA_BITSTREAM = 54,
HACKRF_VENDOR_REQUEST_CLKIN_CTRL = 55,
HACKRF_VENDOR_REQUEST_READ_SELFTEST = 56,
HACKRF_VENDOR_REQUEST_READ_ADC = 57,
} hackrf_vendor_request;
#define USB_CONFIG_STANDARD 0x1
@@ -1244,6 +1245,35 @@ int ADDCALL hackrf_read_selftest(hackrf_device* device, hackrf_selftest* selftes
}
}
int ADDCALL hackrf_read_adc(hackrf_device* device, uint8_t adc_channel, uint16_t* value)
{
USB_API_REQUIRED(device, 0x0109);
int result;
if ((adc_channel & ~0x80) > 7) {
return HACKRF_ERROR_INVALID_PARAM;
}
result = libusb_control_transfer(
device->usb_device,
LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
HACKRF_VENDOR_REQUEST_READ_ADC,
0,
adc_channel,
(unsigned char*) value,
2,
0);
if (result < 2) {
last_libusb_error = result;
return HACKRF_ERROR_LIBUSB;
} else {
*value = FROM_LE16(*value);
return HACKRF_SUCCESS;
}
}
int ADDCALL hackrf_get_m0_state(hackrf_device* device, hackrf_m0_state* state)
{
USB_API_REQUIRED(device, 0x0106)

View File

@@ -1354,6 +1354,20 @@ extern ADDAPI int ADDCALL hackrf_read_selftest(
hackrf_device* device,
hackrf_selftest* value);
/**
* Read a value from an ADC channel
*
* @param[in] device device to query
* @param[in] adc_channel ADC channel, e.g. 0 for ADC0_0. Add 0x80 to use an alternate pin.
* @param[out] value Value read from ADC.
* @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant
* @ingroup debug
*/
extern ADDAPI int ADDCALL hackrf_read_adc(
hackrf_device* device,
uint8_t adc_channel,
uint16_t* value);
/**
* Set transmit underrun limit
*