Loading...
Searching...
No Matches
X4x0 GPIO API

# The X4x0 Front Panel GPIO

Like other USRP devices (e.g., E310, X310), the X4x0 devices expose auxiliary GPIO connections through the motherboard. These GPIO pins can be controlled from either the user application in the FPGA or from the radio blocks.

There are 24 GPIO pins in total, split between two HDMI connectors (labelled GPIO0 and GPIO1) which each expose 12 pins.

Additionally, the X4x0 GPIO lines include a 3.3V power supply which is disabled by default, which can provide up to 450mA with overcurrent protection. See Configuring External Power Supply

## X4x0 Front Panel GPIO

The GPIO port is not meant to drive big loads.

### Connector

HDMI pinout

### Pin Mapping

- Pin 1: Data[0]
- Pin 2: 0V
- Pin 3: Data[1]
- Pin 4: Data[2]
- Pin 5: 0V
- Pin 6: Data[3]
- Pin 7: Data[4]
- Pin 8: 0V
- Pin 9: Data[5]
- Pin 10: Data[6]
- Pin 11: 0V
- Pin 12: Data[7]
- Pin 13: Data[8]
- Pin 14: N/C
- Pin 15: Data[9]
- Pin 16: Data[10]
- Pin 17: 0V
- Pin 18: +3.3V (see Configuring External Power Supply)
- Pin 19: Data[11]

## Setting GPIO Output

The GPIO lines can be configured according to the uhd::usrp::multi_usrp::set_gpio_attr() API, like can be seen at Explaining ATR.

The major difference is that in order to use that API, the GPIO source must be correctly configured. The source can be configured using uhd::usrp::multi_usrp::set_gpio_src(), which takes two arguments: A "bank" and a "src". The bank argument specifies the GPIO port to configure, and the src argument is a vector of twelve elements, each specifying the source for the given GPIO pin.

# Set every pin on GPIO0 to be controlled by DB1_RF0
usrp.set_gpio_src("GPIO0", ["DB1_RF0"]*12)

The bank can be either "GPIO0" or "GPIO1", and the sources can be any combination of:

- DBx_RFy: Controlled by the slot-x radio block via the set_gpio_attr API, with ATR states derived from channel y on that slot if CTRL is set to 1. If CTRL is set to 0, y is ignored and can be either 0 or 1.
- DBx_SPI: Controlled via the digital interface block in the slot-x radio block.
- PS: Controlled directly via the Linux GPIO API on the embedded processor.
- USER_APP: Controlled via user logic in the FPGA. Note that this only works with custom modifications to the FPGA codebase, and not with standard UHD FPGA images.

Once the source is set, using the GPIO proceeds identically to the usage on other devices. Note that the values and masks for the uhd::usrp::multi_usrp::set_gpio_attr() API combines all 24 pins, with bits [23:12] representing the GPIO1 port and bits [11:0] representing the GPIO0 port. For example, to configure the 4th bit on GPIO1 (HDMI pin number 7) as a high output, one would run:

pin_mask = 1 << (12 + 4) # 12 for GPIO1, 4 for the bit on that port
usrp.set_gpio_attr("GPIOA", "CTRL", 0, pin_mask) # Non-ATR mode
usrp.set_gpio_attr("GPIOA", "DDR", pin_mask, pin_mask) # Output
usrp.set_gpio_attr("GPIOA", "OUT", pin_mask, pin_mask) # Set value high
usrp.set_gpio_attr("GPIOA", "OUT", 0, pin_mask) # Set value low

## Configuring External Power Supply

The X410's GPIO ports each have 3.3V power supply pins, which is disabled by default. The GPIO lines will function correctly without the external power supply enabled, and the voltage of the power supply is independent of the selected GPIO line voltage. To enable the power supply, call the uhd::features::gpio_power_iface::set_external_power() method on the gpio_power discoverable feature attached to the mb_controller:

auto usrp = uhd::usrp::multi_usrp::make("type=x4xx");
auto& gpio = usrp->get_mb_controller().get_feature<uhd::features::gpio_power_iface>();
gpio.set_external_power("GPIO1", true); // Enable external power on GPIO1
uhd::features::gpio_power_iface
Definition gpio_power_iface.hpp:21
uhd::features::gpio_power_iface::set_external_power
virtual void set_external_power(const std::string &port, bool enable)=0
uhd::usrp::multi_usrp::make
static sptr make(const device_addr_t &dev_addr)

The status of the external power supply can be queried using uhd::features::gpio_power_iface::get_external_power_status(), which will return one of the following values:

- OFF: Power supply is disabled (the default).
- ON: Power supply is operating normally.
- FAULT: Power supply has encountered a fault and disabled itself. This condition can be cleared by calling uhd::features::gpio_power_iface::set_external_power().

## Configuring GPIO Voltage

The voltage level of the I/O lines can be selected as any of 1.8V, 2.5V, or 3.3V voltage levels on a per-bank basis. To do this use the uhd::features::gpio_power_iface::set_port_voltage() API:

auto usrp = uhd::usrp::multi_usrp::make("type=x4xx");
auto& gpio = usrp->get_mb_controller().get_feature<uhd::features::gpio_power_iface>();
gpio.set_port_voltage("GPIO0", "2V5"); // Set GPIO0 voltage to 2.5V
uhd::features::gpio_power_iface::set_port_voltage
virtual void set_port_voltage(const std::string &port, const std::string &voltage)=0

Valid values can be enumerated with the uhd::features::gpio_power_iface::supported_voltages() call, and are "1V8", "2V5", and "3V3".

# The x4x0 SPI Mode

The GPIO ports of the x4x0 can be used with the Serial Peripheral Interface (SPI) to control external components. To use SPI mode, set the pins you need on the desired GPIO port to be controlled by the SPI engine and configure the data direction.

auto usrp = uhd::usrp::multi_usrp::make(args);
auto& spi_getter_iface =
usrp->get_radio_control().get_feature<uhd::features::spi_getter_iface>();
usrp->set_gpio_src("GPIO0",
{"DB0_SPI",
"DB0_SPI",
"DB0_SPI",
"DB0_SPI",
"DB0_SPI",
"DB0_SPI",
"DB0_SPI",
"DB0_SPI",
"DB0_SPI",
"DB0_SPI",
"DB0_SPI",
"DB0_SPI"});
uhd::features::spi_getter_iface
Definition spi_getter_iface.hpp:38

The example shows the usage of GPIO port 0 (GPIO0) for SPI and needs to be run for GPIO1 again to use that port with SPI, too.

## Configuration of SPI lines

The x4x0 SPI mode supports up to 4 peripherals. All of these peripherals may have a different SPI pin configuration. The pins available for the usage with SPI are listed in Pin Mapping. For GPIO0 the available pins are enumerated from 0 through 11, for GPIO1 the available pins are from 12 through 23. The vector of peripheral configurations is passed to the spi_iface_getter to get the reference:

uhd::features::spi_periph_config_t periph_cfg;
periph_cfg.periph_clk = 0;
periph_cfg.periph_sdi = 1;
periph_cfg.periph_sdo = 2;
periph_cfg.periph_cs = 3;
std::vector<uhd::features::spi_periph_config_t> periph_cfgs;
periph_cfgs.push_back(periph_cfg);
auto spi_ref = spi_getter_iface.get_spi_ref(periph_cfgs);
// Set data direction register (set all to outgoing except for SDI)
usrp->set_gpio_attr("GPIOA", "DDR", 0xD, 0xF);
uhd::features::spi_periph_config_t
Definition spi_getter_iface.hpp:21
uhd::features::spi_periph_config_t::periph_cs
uint8_t periph_cs
Indicates which GPIO line to use for the CS signal.
Definition spi_getter_iface.hpp:23
uhd::features::spi_periph_config_t::periph_sdi
uint8_t periph_sdi
Indicates which GPIO line to use for the SDI signal.
Definition spi_getter_iface.hpp:26
uhd::features::spi_periph_config_t::periph_sdo
uint8_t periph_sdo
Indicates which GPIO line to use for the SDO signal.
Definition spi_getter_iface.hpp:29
uhd::features::spi_periph_config_t::periph_clk
uint8_t periph_clk
Indicates which GPIO line to use for the SCLK signal.
Definition spi_getter_iface.hpp:32

## Write and read on SPI

With the SPI reference read and write operations can be performed. For doing this, some characteristics of the SPI need to be configured:

uhd::spi_config_t config;
config.divider = 4;
config.miso_edge = config.EDGE_RISE;
...
spi_ref->write_spi(0, config, 0xFEFE, 32);
uint32_t read_data = spi_ref->read_spi(0, config, 0xFEFE, 32);
uhd::spi_config_t
Definition serial.hpp:84
uhd::spi_config_t::miso_edge
edge_t miso_edge
on what edge is the miso data valid?
Definition serial.hpp:95
uhd::spi_config_t::EDGE_RISE
@ EDGE_RISE
Definition serial.hpp:89
uhd::spi_config_t::divider
size_t divider
Optionally set the SPI clock divider for this transaction.
Definition serial.hpp:101

The terms 'MISO' and 'MOSI' in the spi_config_t struct map to 'SDI' and 'SDO' respectively. They are legacy terms which will not be used in new code anymore following the resolution to redefine SPI signal names by the Open Source Hardware Association: https://oshwa.org/resources/a-resolution-to-redefine-spi-signal-names/

The SPI clock frequency, \(f_{SCLK}\), is derived from the radio clock frequency ( \(f_{RADIO\_CLK}\)) and the SPI clock divider value ( \(SPI\_CLK\_DIV\)). For X410, the SPI clock frequency is given by the equation shown below, where \(f_{RADIO\_CLK}\) is 122.88 MHz or 125 MHz, depending on the master clock rate. Note that this is the master clock rate divided by 1 (for 100 MHz bandwidth), by 2 (for 200 MHz bandwidth), or by 4 (for 400 MHz bandwidth).

\[X410: f_{SCLK} = \frac{f_{RADIO\_CLK}}{SPI\_CLK\_DIV + 1}\]

For X440, the SPI clock frequency is given by the equation shown below, where \(f_{RADIO\_CLK}\) is the master clock rate divided by 8.

\[X440: f_{SCLK} = \frac{f_{RADIO\_CLK}}{2\cdot\left(SPI\_CLK\_DIV + 1\right)}\]