diff --git a/firmware/common/adc.c b/firmware/common/adc.c new file mode 100644 index 00000000..5d1ddaca --- /dev/null +++ b/firmware/common/adc.c @@ -0,0 +1,45 @@ +/* + * Copyright 2025 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 "adc.h" +#include +#include + +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; +} diff --git a/firmware/common/adc.h b/firmware/common/adc.h new file mode 100644 index 00000000..94481678 --- /dev/null +++ b/firmware/common/adc.h @@ -0,0 +1,30 @@ +/* + * Copyright 2025 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 __ADC_H__ +#define __ADC_H__ + +#include + +uint16_t adc_read(uint8_t pin); +void adc_off(void); + +#endif // __ADC_H__ diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 58f3b17d..7cf62a97 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -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; diff --git a/firmware/common/platform_detect.c b/firmware/common/platform_detect.c index 73656479..ec00a3b8 100644 --- a/firmware/common/platform_detect.c +++ b/firmware/common/platform_detect.c @@ -23,9 +23,9 @@ #include "firmware_info.h" #include "gpio_lpc.h" #include "hackrf_core.h" +#include "adc.h" #include -#include 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 diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 06cb7bec..a620310b 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -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") diff --git a/firmware/hackrf_usb/CMakeLists.txt b/firmware/hackrf_usb/CMakeLists.txt index f4b06d8e..9f6d3cc5 100644 --- a/firmware/hackrf_usb/CMakeLists.txt +++ b/firmware/hackrf_usb/CMakeLists.txt @@ -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" diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index d9717864..a8adf401 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -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 = diff --git a/firmware/hackrf_usb/usb_api_adc.c b/firmware/hackrf_usb/usb_api_adc.c new file mode 100644 index 00000000..ee1bc98e --- /dev/null +++ b/firmware/hackrf_usb/usb_api_adc.c @@ -0,0 +1,49 @@ +/* + * Copyright 2025 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 +#include +#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; +} diff --git a/firmware/hackrf_usb/usb_api_adc.h b/firmware/hackrf_usb/usb_api_adc.h new file mode 100644 index 00000000..8f4d2568 --- /dev/null +++ b/firmware/hackrf_usb/usb_api_adc.h @@ -0,0 +1,32 @@ +/* + * Copyright 2025 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 __USB_API_ADC_H__ +#define __USB_API_ADC_H__ + +#include +#include + +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__ diff --git a/host/hackrf-tools/src/hackrf_debug.c b/host/hackrf-tools/src/hackrf_debug.c index b95b2478..b1977f8d 100644 --- a/host/hackrf-tools/src/hackrf_debug.c +++ b/host/hackrf-tools/src/hackrf_debug.c @@ -577,6 +577,7 @@ static void usage() printf("\t-u, --ui <1/0>: enable/disable UI\n"); printf("\t-l, --leds : configure LED state (0 for all off, 1 for default)\n"); printf("\t-t, --selftest: read self-test report\n"); + printf("\t-a, --adc : 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", diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index eab9b0e0..77f055a5 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -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) diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index 0ffa048f..10c51ee4 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -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 *