AnsweredAssumed Answered

Libiio exmaple for AD9371

Question asked by muthamma on Jul 26, 2017
Latest reply on Jun 1, 2018 by sleony

Hi,

 

I'm trying to modify ad9361_iostream.c for AD9371. Below is the modified code, when i run the elf on zc706, i get following error

 

Error -13 writing to channel "rf_port_select"
value may not be supported.

 

Please help me here to know the changes required for AD9371.

 

***Code***

/*
 * libiio - AD9371 IIO streaming example
 *
 * Copyright (C) 2014 IABG mbH
 * Author: Michael Feilen <feilen_at_iabg.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 **/

 

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <stdio.h>

 

#ifdef __APPLE__
#include <iio/iio.h>
#else
#include <iio.h>
#endif

 

/* helper macros */
#define MHZ(x) ((long long)(x*1000000.0 + .5))
#define GHZ(x) ((long long)(x*1000000000.0 + .5))

 

/* RX is input, TX is output */
enum iodev { RX, TX };

 

/* common RX and TX streaming params */
struct stream_cfg {
    long long bw_hz; // Analog banwidth in Hz
    long long fs_hz; // Baseband sample rate in Hz
    long long lo_hz; // Local oscillator frequency in Hz
    const char* rfport; // Port name
};

 

/* static scratch mem for strings */
static char tmpstr[64];

 

/* IIO structs required for streaming */
static struct iio_context *ctx   = NULL;
static struct iio_channel *rx0_i = NULL;
static struct iio_channel *rx0_q = NULL;
static struct iio_channel *tx0_i = NULL;
static struct iio_channel *tx0_q = NULL;
static struct iio_buffer  *rxbuf = NULL;
static struct iio_buffer  *txbuf = NULL;

 

static bool stop;

 

/* cleanup and exit */
static void shutdown()
{
    printf("* Destroying buffers\n");
    if (rxbuf) { iio_buffer_destroy(rxbuf); }
    if (txbuf) { iio_buffer_destroy(txbuf); }

 

    printf("* Disabling streaming channels\n");
    if (rx0_i) { iio_channel_disable(rx0_i); }
    if (rx0_q) { iio_channel_disable(rx0_q); }
    if (tx0_i) { iio_channel_disable(tx0_i); }
    if (tx0_q) { iio_channel_disable(tx0_q); }

 

    printf("* Destroying context\n");
    if (ctx) { iio_context_destroy(ctx); }
    exit(0);
}

 

static void handle_sig(int sig)
{
    printf("Waiting for process to finish...\n");
    stop = true;
}

 

/* check return value of attr_write function */
static void errchk(int v, const char* what) {
     if (v < 0) { fprintf(stderr, "Error %d writing to channel \"%s\"\nvalue may not be supported.\n", v, what); shutdown(); }
}

 

/* write attribute: long long int */
static void wr_ch_lli(struct iio_channel *chn, const char* what, long long val)
{
    errchk(iio_channel_attr_write_longlong(chn, what, val), what);
}

 

/* write attribute: string */
static void wr_ch_str(struct iio_channel *chn, const char* what, const char* str)
{
    errchk(iio_channel_attr_write(chn, what, str), what);
}

 

/* helper function generating channel names */
static char* get_ch_name(const char* type, int id)
{
    snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id);
    return tmpstr;
}

 

/* returns ad9371 phy device */
static struct iio_device* get_ad9371_phy(struct iio_context *ctx)
{
    struct iio_device *dev =  iio_context_find_device(ctx, "ad9371-phy");
    assert(dev && "No ad9371-phy found");
    return dev;
}

 

/* finds AD9371 streaming IIO devices */
static bool get_ad9371_stream_dev(struct iio_context *ctx, enum iodev d, struct iio_device **dev)
{
    switch (d) {
    case TX: *dev = iio_context_find_device(ctx, "axi-ad9371-tx-hpc"); return *dev != NULL;
    case RX: *dev = iio_context_find_device(ctx, "axi-ad9371-rx-hpc");  return *dev != NULL;
    default: assert(0); return false;
    }
}

 

/* finds AD9371 streaming IIO channels */
static bool get_ad9371_stream_ch(struct iio_context *ctx, enum iodev d, struct iio_device *dev, int chid, struct iio_channel **chn)
{
    *chn = iio_device_find_channel(dev, get_ch_name("voltage", chid), d == TX);
    if (!*chn)
        *chn = iio_device_find_channel(dev, get_ch_name("altvoltage", chid), d == TX);
    return *chn != NULL;
}

 

/* finds AD9371 phy IIO configuration channel with id chid */
static bool get_phy_chan(struct iio_context *ctx, enum iodev d, int chid, struct iio_channel **chn)
{
    switch (d) {
    case RX: *chn = iio_device_find_channel(get_ad9371_phy(ctx), get_ch_name("voltage", chid), false); return *chn != NULL;
    case TX: *chn = iio_device_find_channel(get_ad9371_phy(ctx), get_ch_name("voltage", chid), true);  return *chn != NULL;
    default: assert(0); return false;
    }
}

 

/* finds AD9371 local oscillator IIO configuration channels */
static bool get_lo_chan(struct iio_context *ctx, enum iodev d, struct iio_channel **chn)
{
    switch (d) {
     // LO chan is always output, i.e. true
    case RX: *chn = iio_device_find_channel(get_ad9371_phy(ctx), get_ch_name("altvoltage", 0), true); return *chn != NULL;
    case TX: *chn = iio_device_find_channel(get_ad9371_phy(ctx), get_ch_name("altvoltage", 1), true); return *chn != NULL;
    default: assert(0); return false;
    }
}

 

/* applies streaming configuration through IIO */
bool cfg_ad9372_streaming_ch(struct iio_context *ctx, struct stream_cfg *cfg, enum iodev type, int chid)
{
    struct iio_channel *chn = NULL;

 

    // Configure phy and lo channels
    printf("* Acquiring AD9371 phy channel %d\n", chid);
    if (!get_phy_chan(ctx, type, chid, &chn)) {    return false; }
    wr_ch_str(chn, "rf_port_select",     cfg->rfport);
    wr_ch_lli(chn, "rf_bandwidth",       cfg->bw_hz);
    wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz);

 

    // Configure LO channel
    printf("* Acquiring AD9371 %s lo channel\n", type == TX ? "TX" : "RX");
    if (!get_lo_chan(ctx, type, &chn)) { return false; }
    wr_ch_lli(chn, "frequency", cfg->lo_hz);
    return true;
}

 

/* simple configuration and streaming */
int main (int argc, char **argv)
{
    // Streaming devices
    struct iio_device *tx;
    struct iio_device *rx;

 

    // RX and TX sample counters
    size_t nrx = 0;
    size_t ntx = 0;

 

    // Stream configurations
    struct stream_cfg rxcfg;
    struct stream_cfg txcfg;

 

    // Listen to ctrl+c and assert
    signal(SIGINT, handle_sig);

 

    // RX stream config
    rxcfg.bw_hz = MHZ(100);   // 2 MHz rf bandwidth
    rxcfg.fs_hz = MHZ(122.88);   // 2.5 MS/s rx sample rate
    rxcfg.lo_hz = MHZ(760); // 2.5 GHz rf frequency
    rxcfg.rfport = "A_BALANCED"; // port A (select for rf freq.)

 

    // TX stream config
    txcfg.bw_hz = MHZ(100); // 1.5 MHz rf bandwidth
    txcfg.fs_hz = MHZ(122.88);   // 2.5 MS/s tx sample rate
    txcfg.lo_hz = MHZ(760); // 2.5 GHz rf frequency
    txcfg.rfport = "A"; // port A (select for rf freq.)

 

    printf("* Acquiring IIO context\n");
    assert((ctx = iio_create_default_context()) && "No context");
    assert(iio_context_get_devices_count(ctx) > 0 && "No devices");

 

    printf("* Acquiring AD9371 streaming devices\n");
    assert(get_ad9371_stream_dev(ctx, TX, &tx) && "No tx dev found");
    assert(get_ad9371_stream_dev(ctx, RX, &rx) && "No rx dev found");

 

    printf("* Configuring AD9371 for streaming\n");
    assert(cfg_ad9371_streaming_ch(ctx, &rxcfg, RX, 0) && "RX port 0 not found");
    assert(cfg_ad9371_streaming_ch(ctx, &txcfg, TX, 0) && "TX port 0 not found");

 

    printf("* Initializing AD9371 IIO streaming channels\n");
    assert(get_ad9371_stream_ch(ctx, RX, rx, 0, &rx0_i) && "RX chan i not found");
    assert(get_ad9371_stream_ch(ctx, RX, rx, 1, &rx0_q) && "RX chan q not found");
    assert(get_ad9371_stream_ch(ctx, TX, tx, 0, &tx0_i) && "TX chan i not found");
    assert(get_ad9371_stream_ch(ctx, TX, tx, 1, &tx0_q) && "TX chan q not found");

 

    printf("* Enabling IIO streaming channels\n");
    iio_channel_enable(rx0_i);
    iio_channel_enable(rx0_q);
    iio_channel_enable(tx0_i);
    iio_channel_enable(tx0_q);

 

    printf("* Creating non-cyclic IIO buffers with 1 MiS\n");
    rxbuf = iio_device_create_buffer(rx, 1024*1024, false);
    if (!rxbuf) {
        perror("Could not create RX buffer");
        shutdown();
    }
    txbuf = iio_device_create_buffer(tx, 1024*1024, false);
    if (!txbuf) {
        perror("Could not create TX buffer");
        shutdown();
    }

 

    printf("* Starting IO streaming (press CTRL+C to cancel)\n");
    while (!stop)
    {
        ssize_t nbytes_rx, nbytes_tx;
        void *p_dat, *p_end;
        ptrdiff_t p_inc;

 

        // Schedule TX buffer
        nbytes_tx = iio_buffer_push(txbuf);
        if (nbytes_tx < 0) { printf("Error pushing buf %d\n", (int) nbytes_tx); shutdown(); }

 

        // Refill RX buffer
        nbytes_rx = iio_buffer_refill(rxbuf);
        if (nbytes_rx < 0) { printf("Error refilling buf %d\n",(int) nbytes_rx); shutdown(); }

 

        // READ: Get pointers to RX buf and read IQ from RX buf port 0
        p_inc = iio_buffer_step(rxbuf);
        p_end = iio_buffer_end(rxbuf);
        for (p_dat = iio_buffer_first(rxbuf, rx0_i); p_dat < p_end; p_dat += p_inc) {
            // Example: swap I and Q
            const int16_t i = ((int16_t*)p_dat)[0]; // Real (I)
            const int16_t q = ((int16_t*)p_dat)[1]; // Imag (Q)
            ((int16_t*)p_dat)[0] = q;
            ((int16_t*)p_dat)[1] = i;
        }

 

        // WRITE: Get pointers to TX buf and write IQ to TX buf port 0
        p_inc = iio_buffer_step(txbuf);
        p_end = iio_buffer_end(txbuf);
        for (p_dat = iio_buffer_first(txbuf, tx0_i); p_dat < p_end; p_dat += p_inc) {
            // Example: fill with zeros
            // 12-bit sample needs to be MSB alligned so shift by 4
            // https://wiki.analog.com/resources/eval/user-guides/ad-fmcomms2-ebz/software/basic_iq_datafiles#binary_format
            ((int16_t*)p_dat)[0] = 0 << 4; // Real (I)
            ((int16_t*)p_dat)[1] = 0 << 4; // Imag (Q)
        }

 

        // Sample counter increment and status output
        nrx += nbytes_rx / iio_device_get_sample_size(rx);
        ntx += nbytes_tx / iio_device_get_sample_size(tx);
        printf("\tRX %8.2f MSmp, TX %8.2f MSmp\n", nrx/1e6, ntx/1e6);
    }

 

    shutdown();

 

    return 0;
}

Outcomes