|
| 1 | +ADRV9001 no-OS Driver |
| 2 | +===================== |
| 3 | + |
| 4 | +Supported Devices |
| 5 | +----------------- |
| 6 | + |
| 7 | +- `ADRV9002 <https://www.analog.com/en/products/adrv9002.html>`_ |
| 8 | + |
| 9 | +Overview |
| 10 | +-------- |
| 11 | + |
| 12 | +The ADRV9002 is a dual-mode RF transceiver supporting both narrow-band |
| 13 | +and wideband operations from 30 MHz to 6000 MHz, suitable for VHF, UHF, |
| 14 | +ISM, and cellular bands with bandwidths up to 40 MHz. It features dual |
| 15 | +transmitters and receivers, customizable synthesizers, and digital |
| 16 | +filters for improved performance. The transceiver supports TDD and FDD |
| 17 | +applications, offering high linearity and dynamic range, and includes |
| 18 | +capabilities such as low power monitoring, dynamic profile switching, |
| 19 | +rapid frequency hopping, and synchronization across multiple chips. In |
| 20 | +addition, several auxiliary functions, such as auxiliary |
| 21 | +analog-to-digital converters (ADCs), auxiliary digital-to-analog |
| 22 | +converters (DACs), and general-purpose inputs/outputs (GPIOs), are |
| 23 | +integrated to provide additional monitoring and control capability. |
| 24 | + |
| 25 | +Applications |
| 26 | +------------ |
| 27 | + |
| 28 | +- Mission Critical Communications |
| 29 | +- Very High Frequency (VHF), Ultra High Frequency (UHF), |
| 30 | + and Cellular to 6 GHz |
| 31 | +- Time Division Duplex (TDD) and Frequency Division Duplex (FDD) |
| 32 | + Applications |
| 33 | + |
| 34 | +Operation Modes |
| 35 | +--------------- |
| 36 | + |
| 37 | ++-------------------------+-------------------------+-----------------+-------------------------+ |
| 38 | +| Mode Name | Description | Configuration | Typical Use | |
| 39 | +| | | Bits | Case | |
| 40 | ++-------------------------+-------------------------+-----------------+-------------------------+ |
| 41 | +| ADI_ADRV9001_LDO_POWER | Sets all LDO power | 0x00 | Used during low-power | |
| 42 | +| _SAVING_MODE_1 | saving modes for the | | operations to save | |
| 43 | +| | device. | | power. | |
| 44 | ++-------------------------+-------------------------+-----------------+-------------------------+ |
| 45 | +| ADI_ADRV9001_SPI_MODE | Allows operations | SPI | Used when direct | |
| 46 | +| | through the SPI | | control via SPI is | |
| 47 | +| | interface. | | required. | |
| 48 | ++-------------------------+-------------------------+-----------------+-------------------------+ |
| 49 | +| ADI_ADRV9001_CHANNEL | State in which the | Channel Mask | Initial state for | |
| 50 | +| _PRIMED | channel is ready but | | channels in TDD mode. | |
| 51 | +| | not transmitting/ | | | |
| 52 | +| | receiving. | | | |
| 53 | ++-------------------------+-------------------------+-----------------+-------------------------+ |
| 54 | +| ADI_ADRV9001_CHANNEL | State allowing full RF | N/A | Used in FDD mode for | |
| 55 | +| _RF_ENABLED | functionality. | | active transmission or | |
| 56 | +| | | | reception. | |
| 57 | ++-------------------------+-------------------------+-----------------+-------------------------+ |
| 58 | +| ADI_ADRV9001_INT_LO1 | Uses internal LO1 for | LO1 | Standard operation with | |
| 59 | +| | operations. | Configuration | internal local | |
| 60 | +| | | | oscillator. | |
| 61 | ++-------------------------+-------------------------+-----------------+-------------------------+ |
| 62 | + |
| 63 | +Device Configuration |
| 64 | +-------------------- |
| 65 | + |
| 66 | +Initialization and Configuration |
| 67 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 68 | + |
| 69 | +The ADRV9002 initialization and configuration functions handle initial |
| 70 | +setup, ensuring optimal operation of the RF transceiver. Key functions |
| 71 | +like ``adrv9002_setup`` integrate device channels, configure GPIOs, |
| 72 | +manage power, and perform initial calibrations. These operations align |
| 73 | +the device parameters with application-specific requirements, |
| 74 | +facilitating effective device boot-up and functionality. Key operations |
| 75 | +include implementing error-handling routines throughout the |
| 76 | +initialization process to report and manage issues encountered, allowing |
| 77 | +for a robust configuration adaptable to numerous RF applications. |
| 78 | + |
| 79 | +Transceiver Path Management |
| 80 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 81 | + |
| 82 | +Transceiver path management functions in the ADRV9002 handle the |
| 83 | +configuration of RX and TX paths, adjusting gain, transmit power, and |
| 84 | +data interfaces to meet specific operation needs. For instance, |
| 85 | +``adrv9002_tx_path_config`` and ``adrv9001_rx_path_config`` set channel |
| 86 | +states and apply gain control settings through API calls. These |
| 87 | +functions ensure that the signal paths remain robust under various |
| 88 | +operational modes, such as TDD and FDD. They dynamically manage the |
| 89 | +transceiver’s performance metrics to maintain signal integrity aligned |
| 90 | +with predetermined operational conditions. |
| 91 | + |
| 92 | +Frequency Hopping and Calibration |
| 93 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 94 | + |
| 95 | +These functions support dynamic frequency switching and system |
| 96 | +calibration tasks, vital for efficient multi-frequency operations. The |
| 97 | +frequency hopping is managed through modes and configurations set during |
| 98 | +initialization with precise timing requirements, allowing the ADRV9002 |
| 99 | +to dynamically adapt its operating frequency to limit interference, as |
| 100 | +seen in frequency hopping functions that control PLL retunes and hop |
| 101 | +tables efficiently. Calibration routines like |
| 102 | +``adi_adrv9001_cals_InitCals_Run`` maintain performance by aligning |
| 103 | +internal parameters with environmental conditions and hardware states, |
| 104 | +offset by the frequency hopping capabilities. |
| 105 | + |
| 106 | +Test Pattern and Performance Tuning |
| 107 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 108 | + |
| 109 | +Performance tuning functions for the ADRV9002 focus on validating and |
| 110 | +enhancing signal integrity through digital interface tuning and test |
| 111 | +pattern configurations. Functions like |
| 112 | +``adrv9002_axi_tx_test_pattern_set`` and |
| 113 | +``adrv9002_axi_rx_test_pattern_pn_sel`` set specific test patterns and |
| 114 | +perform interface tuning to optimize transmission and reception paths. |
| 115 | +These tasks involve iterative adjustments of clock and data delays, |
| 116 | +logging test results, and storing configurations, creating a refined |
| 117 | +tuning approach that ensures device operation at desired performance |
| 118 | +thresholds. Functions like ``adrv9002_check_tx_test_pattern`` further |
| 119 | +assess signal correctness, providing a comprehensive tuning suite. |
| 120 | + |
| 121 | +Driver Initialization Example |
| 122 | +----------------------------- |
| 123 | + |
| 124 | +.. code-block:: C |
| 125 | +
|
| 126 | + #include <stdio.h> |
| 127 | + #include "no_os_spi.h" |
| 128 | + #include "no_os_gpio.h" |
| 129 | + #include "no_os_error.h" |
| 130 | + #include "adrv9002.h" |
| 131 | +
|
| 132 | + int adrv9002_driver_init(void) |
| 133 | + { |
| 134 | + int ret; |
| 135 | + struct no_os_spi_init_param spi_init = { |
| 136 | + .device_id = XPAR_PS7_SPI_0_DEVICE_ID, |
| 137 | + .max_speed_hz = 20000000, |
| 138 | + .mode = NO_OS_SPI_MODE_0, |
| 139 | + .chip_select = 0, |
| 140 | + .platform_ops = &xil_spi_ops, |
| 141 | + }; |
| 142 | +
|
| 143 | + struct no_os_gpio_init_param gpio_reset_init = { |
| 144 | + .number = 46 + GPIO_OFFSET, |
| 145 | + .platform_ops = &xil_gpio_ops, |
| 146 | + }; |
| 147 | +
|
| 148 | + struct no_os_gpio_desc *gpio_reset; |
| 149 | + struct no_os_spi_desc *spi_desc; |
| 150 | +
|
| 151 | + /* Initialize GPIO for reset */ |
| 152 | + ret = no_os_gpio_get(&gpio_reset, &gpio_reset_init); |
| 153 | + if (ret) { |
| 154 | + printf("GPIO initialization failed\n"); |
| 155 | + return ret; |
| 156 | + } |
| 157 | +
|
| 158 | + ret = no_os_gpio_direction_output(gpio_reset, NO_OS_GPIO_LOW); |
| 159 | + if (ret) { |
| 160 | + printf("GPIO direction set failed\n"); |
| 161 | + no_os_gpio_remove(gpio_reset); |
| 162 | + return ret; |
| 163 | + } |
| 164 | +
|
| 165 | + /* Initialize SPI */ |
| 166 | + ret = no_os_spi_init(&spi_desc, &spi_init); |
| 167 | + if (ret) { |
| 168 | + printf("SPI initialization failed\n"); |
| 169 | + no_os_gpio_remove(gpio_reset); |
| 170 | + return ret; |
| 171 | + } |
| 172 | +
|
| 173 | + /* Reset the device */ |
| 174 | + ret = no_os_gpio_set_value(gpio_reset, NO_OS_GPIO_HIGH); |
| 175 | + if (ret) { |
| 176 | + printf("GPIO set value failed\n"); |
| 177 | + no_os_spi_remove(spi_desc); |
| 178 | + no_os_gpio_remove(gpio_reset); |
| 179 | + return ret; |
| 180 | + } |
| 181 | +
|
| 182 | + /* Call additional initialization/configuration functions */ |
| 183 | + ret = adrv9002_setup(spi_desc); |
| 184 | + if (ret) { |
| 185 | + printf("adrv9002_setup failed\n"); |
| 186 | + no_os_spi_remove(spi_desc); |
| 187 | + no_os_gpio_remove(gpio_reset); |
| 188 | + return ret; |
| 189 | + } |
| 190 | +
|
| 191 | + ret = adrv9002_tx_path_config(spi_desc); |
| 192 | + if (ret) { |
| 193 | + printf("adrv9002_tx_path_config failed\n"); |
| 194 | + no_os_spi_remove(spi_desc); |
| 195 | + no_os_gpio_remove(gpio_reset); |
| 196 | + return ret; |
| 197 | + } |
| 198 | +
|
| 199 | + printf("Driver initialization complete\n"); |
| 200 | +
|
| 201 | + /* Cleanup */ |
| 202 | + no_os_spi_remove(spi_desc); |
| 203 | + no_os_gpio_remove(gpio_reset); |
| 204 | +
|
| 205 | + return 0; |
| 206 | + } |
| 207 | +
|
| 208 | +IIO Device Initialization Example |
| 209 | +--------------------------------- |
| 210 | + |
| 211 | +.. code-block:: C |
| 212 | +
|
| 213 | + static uint8_t adc_buffers[IIO_DEV_COUNT][1024]; |
| 214 | + static uint8_t dac_buffers[IIO_DEV_COUNT][1024]; |
| 215 | +
|
| 216 | + struct { |
| 217 | + struct axi_dmac *tx1_dmac; |
| 218 | + struct axi_dmac *rx1_dmac; |
| 219 | + } phy; |
| 220 | +
|
| 221 | + static int32_t iio_run(struct iio_axi_adc_init_param *adc_pars, |
| 222 | + struct iio_axi_dac_init_param *dac_pars) |
| 223 | + { |
| 224 | + struct iio_axi_adc_desc *adcs[IIO_DEV_COUNT]; |
| 225 | + struct iio_axi_dac_desc *dacs[IIO_DEV_COUNT]; |
| 226 | + struct iio_data_buffer iio_dac_buffers[IIO_DEV_COUNT]; |
| 227 | + struct iio_data_buffer iio_adc_buffers[IIO_DEV_COUNT]; |
| 228 | + struct iio_device *iio_descs[IIO_DEV_COUNT * 2]; |
| 229 | + struct iio_app_device app_devices[IIO_DEV_COUNT * 2] = {0}; |
| 230 | + struct xil_uart_init_param platform_uart_init_par = { |
| 231 | + .type = UART_PS, |
| 232 | + .irq_id = UART_IRQ_ID |
| 233 | + }; |
| 234 | + struct no_os_uart_init_param iio_uart_ip = { |
| 235 | + .device_id = UART_DEVICE_ID, |
| 236 | + .irq_id = UART_IRQ_ID, |
| 237 | + .baud_rate = UART_BAUDRATE, |
| 238 | + .size = NO_OS_UART_CS_8, |
| 239 | + .parity = NO_OS_UART_PAR_NO, |
| 240 | + .stop = NO_OS_UART_STOP_1_BIT, |
| 241 | + .extra = &platform_uart_init_par, |
| 242 | + .platform_ops = &xil_uart_ops |
| 243 | + }; |
| 244 | + struct iio_app_desc *app; |
| 245 | + struct iio_app_init_param app_init_param = { 0 }; |
| 246 | + int32_t ret; |
| 247 | +
|
| 248 | + for (int i = 0; i < IIO_DEV_COUNT; i++) { |
| 249 | + /* Initialize ADC */ |
| 250 | + iio_adc_buffers[i].buff = adc_buffers[i]; |
| 251 | + iio_adc_buffers[i].size = sizeof(adc_buffers[i]); |
| 252 | +
|
| 253 | + ret = iio_axi_adc_init(&adcs[i], &adc_pars[i]); |
| 254 | + if (ret < 0) |
| 255 | + goto error; |
| 256 | +
|
| 257 | + int a = 2 * i; |
| 258 | + iio_axi_adc_get_dev_descriptor(adcs[i], &iio_descs[a]); |
| 259 | + app_devices[a].name = (char *)adc_pars[i].rx_adc->name; |
| 260 | + app_devices[a].dev = adcs[i]; |
| 261 | + app_devices[a].dev_descriptor = iio_descs[a]; |
| 262 | + app_devices[a].read_buff = &iio_adc_buffers[i]; |
| 263 | +
|
| 264 | + /* Initialize DAC */ |
| 265 | + iio_dac_buffers[i].buff = dac_buffers[i]; |
| 266 | + iio_dac_buffers[i].size = sizeof(dac_buffers[i]); |
| 267 | +
|
| 268 | + ret = iio_axi_dac_init(&dacs[i], &dac_pars[i]); |
| 269 | + if (ret < 0) |
| 270 | + goto error; |
| 271 | +
|
| 272 | + a = 2 * i + 1; |
| 273 | + iio_axi_dac_get_dev_descriptor(dacs[i], &iio_descs[a]); |
| 274 | + app_devices[a].name = (char *)dac_pars[i].tx_dac->name; |
| 275 | + app_devices[a].dev = dacs[i]; |
| 276 | + app_devices[a].dev_descriptor = iio_descs[a]; |
| 277 | + app_devices[a].write_buff = &iio_dac_buffers[i]; |
| 278 | + } |
| 279 | +
|
| 280 | + struct axi_dmac_init rx1_dmac_init = { |
| 281 | + "rx_dmac", |
| 282 | + RX1_DMA_BASEADDR, |
| 283 | + IRQ_DISABLED |
| 284 | + }; |
| 285 | +
|
| 286 | + struct axi_dmac_init tx1_dmac_init = { |
| 287 | + "tx_dmac", |
| 288 | + TX1_DMA_BASEADDR, |
| 289 | + IRQ_DISABLED |
| 290 | + }; |
| 291 | +
|
| 292 | + /* Initialize DMA Controllers */ |
| 293 | + ret = axi_dmac_init(&phy.tx1_dmac, &tx1_dmac_init); |
| 294 | + if (ret) { |
| 295 | + printf("axi_dmac_init(tx) failed with status %d\n", ret); |
| 296 | + goto error; |
| 297 | + } |
| 298 | +
|
| 299 | + ret = axi_dmac_init(&phy.rx1_dmac, &rx1_dmac_init); |
| 300 | + if (ret) { |
| 301 | + printf("axi_dmac_init(rx) failed with status %d\n", ret); |
| 302 | + goto error; |
| 303 | + } |
| 304 | +
|
| 305 | + /* Initialize IIO application */ |
| 306 | + app_init_param.devices = app_devices; |
| 307 | + app_init_param.nb_devices = NO_OS_ARRAY_SIZE(app_devices); |
| 308 | + app_init_param.uart_init_params = iio_uart_ip; |
| 309 | +
|
| 310 | + ret = iio_app_init(&app, app_init_param); |
| 311 | + if (ret) |
| 312 | + goto error; |
| 313 | +
|
| 314 | + return iio_app_run(app); |
| 315 | +
|
| 316 | + error: |
| 317 | + printf("iio_run() failed with status %d\n", ret); |
| 318 | + return ret; |
| 319 | + } |
0 commit comments