• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libiio - AD9371 IIO streaming example
3  *
4  * Copyright (C) 2014 IABG mbH
5  * Author: Michael Feilen <feilen_at_iabg.de>
6  * Copyright (C) 2017 Analog Devices Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  **/
19 
20 #include <stdbool.h>
21 #include <stdint.h>
22 #include <string.h>
23 #include <signal.h>
24 #include <stdio.h>
25 
26 #ifdef __APPLE__
27 #include <iio/iio.h>
28 #else
29 #include <iio.h>
30 #endif
31 
32 /* helper macros */
33 #define MHZ(x) ((long long)(x*1000000.0 + .5))
34 #define GHZ(x) ((long long)(x*1000000000.0 + .5))
35 
36 #define ASSERT(expr) { \
37 	if (!(expr)) { \
38 		(void) fprintf(stderr, "assertion failed (%s:%d)\n", __FILE__, __LINE__); \
39 		(void) abort(); \
40 	} \
41 }
42 
43 /* RX is input, TX is output */
44 enum iodev { RX, TX };
45 
46 /* common RX and TX streaming params */
47 struct stream_cfg {
48 	long long lo_hz; // Local oscillator frequency in Hz
49 };
50 
51 /* static scratch mem for strings */
52 static char tmpstr[64];
53 
54 /* IIO structs required for streaming */
55 static struct iio_context *ctx   = NULL;
56 static struct iio_channel *rx0_i = NULL;
57 static struct iio_channel *rx0_q = NULL;
58 static struct iio_channel *tx0_i = NULL;
59 static struct iio_channel *tx0_q = NULL;
60 static struct iio_buffer  *rxbuf = NULL;
61 static struct iio_buffer  *txbuf = NULL;
62 
63 static bool stop;
64 
65 /* cleanup and exit */
shutdown()66 static void shutdown()
67 {
68 	printf("* Destroying buffers\n");
69 	if (rxbuf) { iio_buffer_destroy(rxbuf); }
70 	if (txbuf) { iio_buffer_destroy(txbuf); }
71 
72 	printf("* Disabling streaming channels\n");
73 	if (rx0_i) { iio_channel_disable(rx0_i); }
74 	if (rx0_q) { iio_channel_disable(rx0_q); }
75 	if (tx0_i) { iio_channel_disable(tx0_i); }
76 	if (tx0_q) { iio_channel_disable(tx0_q); }
77 
78 	printf("* Destroying context\n");
79 	if (ctx) { iio_context_destroy(ctx); }
80 	exit(0);
81 }
82 
handle_sig(int sig)83 static void handle_sig(int sig)
84 {
85 	printf("Waiting for process to finish...\n");
86 	stop = true;
87 }
88 
89 /* check return value of attr_write function */
errchk(int v,const char * what)90 static void errchk(int v, const char* what) {
91 	 if (v < 0) { fprintf(stderr, "Error %d writing to channel \"%s\"\nvalue may not be supported.\n", v, what); shutdown(); }
92 }
93 
94 /* write attribute: long long int */
wr_ch_lli(struct iio_channel * chn,const char * what,long long val)95 static void wr_ch_lli(struct iio_channel *chn, const char* what, long long val)
96 {
97 	errchk(iio_channel_attr_write_longlong(chn, what, val), what);
98 }
99 
100 /* write attribute: long long int */
rd_ch_lli(struct iio_channel * chn,const char * what)101 static long long rd_ch_lli(struct iio_channel *chn, const char* what)
102 {
103 	long long val;
104 
105 	errchk(iio_channel_attr_read_longlong(chn, what, &val), what);
106 
107 	printf("\t %s: %lld\n", what, val);
108 	return val;
109 }
110 
111 #if 0
112 /* write attribute: string */
113 static void wr_ch_str(struct iio_channel *chn, const char* what, const char* str)
114 {
115 	errchk(iio_channel_attr_write(chn, what, str), what);
116 }
117 #endif
118 
119 /* helper function generating channel names */
get_ch_name_mod(const char * type,int id,char modify)120 static char* get_ch_name_mod(const char* type, int id, char modify)
121 {
122 	snprintf(tmpstr, sizeof(tmpstr), "%s%d_%c", type, id, modify);
123 	return tmpstr;
124 }
125 
126 /* helper function generating channel names */
get_ch_name(const char * type,int id)127 static char* get_ch_name(const char* type, int id)
128 {
129 	snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id);
130 	return tmpstr;
131 }
132 
133 /* returns ad9371 phy device */
get_ad9371_phy(struct iio_context * ctx)134 static struct iio_device* get_ad9371_phy(struct iio_context *ctx)
135 {
136 	struct iio_device *dev =  iio_context_find_device(ctx, "ad9371-phy");
137 	ASSERT(dev && "No ad9371-phy found");
138 	return dev;
139 }
140 
141 /* finds AD9371 streaming IIO devices */
get_ad9371_stream_dev(struct iio_context * ctx,enum iodev d,struct iio_device ** dev)142 static bool get_ad9371_stream_dev(struct iio_context *ctx, enum iodev d, struct iio_device **dev)
143 {
144 	switch (d) {
145 	case TX: *dev = iio_context_find_device(ctx, "axi-ad9371-tx-hpc"); return *dev != NULL;
146 	case RX: *dev = iio_context_find_device(ctx, "axi-ad9371-rx-hpc");  return *dev != NULL;
147 	default: ASSERT(0); return false;
148 	}
149 }
150 
151 /* finds AD9371 streaming IIO channels */
get_ad9371_stream_ch(struct iio_context * ctx,enum iodev d,struct iio_device * dev,int chid,char modify,struct iio_channel ** chn)152 static bool get_ad9371_stream_ch(struct iio_context *ctx, enum iodev d, struct iio_device *dev, int chid, char modify, struct iio_channel **chn)
153 {
154 	*chn = iio_device_find_channel(dev, modify ? get_ch_name_mod("voltage", chid, modify) : get_ch_name("voltage", chid), d == TX);
155 	if (!*chn)
156 		*chn = iio_device_find_channel(dev, modify ? get_ch_name_mod("voltage", chid, modify) : get_ch_name("voltage", chid), d == TX);
157 	return *chn != NULL;
158 }
159 
160 /* finds AD9371 phy IIO configuration channel with id chid */
get_phy_chan(struct iio_context * ctx,enum iodev d,int chid,struct iio_channel ** chn)161 static bool get_phy_chan(struct iio_context *ctx, enum iodev d, int chid, struct iio_channel **chn)
162 {
163 	switch (d) {
164 	case RX: *chn = iio_device_find_channel(get_ad9371_phy(ctx), get_ch_name("voltage", chid), false); return *chn != NULL;
165 	case TX: *chn = iio_device_find_channel(get_ad9371_phy(ctx), get_ch_name("voltage", chid), true);  return *chn != NULL;
166 	default: ASSERT(0); return false;
167 	}
168 }
169 
170 /* finds AD9371 local oscillator IIO configuration channels */
get_lo_chan(struct iio_context * ctx,enum iodev d,struct iio_channel ** chn)171 static bool get_lo_chan(struct iio_context *ctx, enum iodev d, struct iio_channel **chn)
172 {
173 	switch (d) {
174 	 // LO chan is always output, i.e. true
175 	case RX: *chn = iio_device_find_channel(get_ad9371_phy(ctx), get_ch_name("altvoltage", 0), true); return *chn != NULL;
176 	case TX: *chn = iio_device_find_channel(get_ad9371_phy(ctx), get_ch_name("altvoltage", 1), true); return *chn != NULL;
177 	default: ASSERT(0); return false;
178 	}
179 }
180 
181 /* applies streaming configuration through IIO */
cfg_ad9371_streaming_ch(struct iio_context * ctx,struct stream_cfg * cfg,enum iodev type,int chid)182 bool cfg_ad9371_streaming_ch(struct iio_context *ctx, struct stream_cfg *cfg, enum iodev type, int chid)
183 {
184 	struct iio_channel *chn = NULL;
185 
186 	// Configure phy and lo channels
187 	printf("* Acquiring AD9371 phy %s channel %d\n", type == TX ? "TX" : "RX", chid);
188 	if (!get_phy_chan(ctx, type, chid, &chn)) {	return false; }
189 
190 	rd_ch_lli(chn, "rf_bandwidth");
191 	rd_ch_lli(chn, "sampling_frequency");
192 
193 	// Configure LO channel
194 	printf("* Acquiring AD9371 %s lo channel\n", type == TX ? "TX" : "RX");
195 	if (!get_lo_chan(ctx, type, &chn)) { return false; }
196 	wr_ch_lli(chn, type == TX ? "TX_LO_frequency" : "RX_LO_frequency" , cfg->lo_hz);
197 	return true;
198 }
199 
200 /* simple configuration and streaming */
main(int argc,char ** argv)201 int main (int argc, char **argv)
202 {
203 	// Streaming devices
204 	struct iio_device *tx;
205 	struct iio_device *rx;
206 
207 	// RX and TX sample counters
208 	size_t nrx = 0;
209 	size_t ntx = 0;
210 
211 	// Stream configurations
212 	struct stream_cfg rxcfg;
213 	struct stream_cfg txcfg;
214 
215 	// Listen to ctrl+c and ASSERT
216 	signal(SIGINT, handle_sig);
217 
218 	// RX stream config
219 	rxcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
220 
221 	// TX stream config
222 	txcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
223 
224 	printf("* Acquiring IIO context\n");
225 	ASSERT((ctx = iio_create_default_context()) && "No context");
226 	ASSERT(iio_context_get_devices_count(ctx) > 0 && "No devices");
227 
228 	printf("* Acquiring AD9371 streaming devices\n");
229 	ASSERT(get_ad9371_stream_dev(ctx, TX, &tx) && "No tx dev found");
230 	ASSERT(get_ad9371_stream_dev(ctx, RX, &rx) && "No rx dev found");
231 
232 	printf("* Configuring AD9371 for streaming\n");
233 	ASSERT(cfg_ad9371_streaming_ch(ctx, &rxcfg, RX, 0) && "RX port 0 not found");
234 	ASSERT(cfg_ad9371_streaming_ch(ctx, &txcfg, TX, 0) && "TX port 0 not found");
235 
236 	printf("* Initializing AD9371 IIO streaming channels\n");
237 	ASSERT(get_ad9371_stream_ch(ctx, RX, rx, 0, 'i', &rx0_i) && "RX chan i not found");
238 	ASSERT(get_ad9371_stream_ch(ctx, RX, rx, 0, 'q', &rx0_q) && "RX chan q not found");
239 	ASSERT(get_ad9371_stream_ch(ctx, TX, tx, 0, 0, &tx0_i) && "TX chan i not found");
240 	ASSERT(get_ad9371_stream_ch(ctx, TX, tx, 1, 0, &tx0_q) && "TX chan q not found");
241 
242 	printf("* Enabling IIO streaming channels\n");
243 	iio_channel_enable(rx0_i);
244 	iio_channel_enable(rx0_q);
245 	iio_channel_enable(tx0_i);
246 	iio_channel_enable(tx0_q);
247 
248 	printf("* Creating non-cyclic IIO buffers with 1 MiS\n");
249 	rxbuf = iio_device_create_buffer(rx, 1024*1024, false);
250 	if (!rxbuf) {
251 		perror("Could not create RX buffer");
252 		shutdown();
253 	}
254 	txbuf = iio_device_create_buffer(tx, 1024*1024, false);
255 	if (!txbuf) {
256 		perror("Could not create TX buffer");
257 		shutdown();
258 	}
259 
260 	printf("* Starting IO streaming (press CTRL+C to cancel)\n");
261 	while (!stop)
262 	{
263 		ssize_t nbytes_rx, nbytes_tx;
264 		char *p_dat, *p_end;
265 		ptrdiff_t p_inc;
266 
267 		// Schedule TX buffer
268 		nbytes_tx = iio_buffer_push(txbuf);
269 		if (nbytes_tx < 0) { printf("Error pushing buf %d\n", (int) nbytes_tx); shutdown(); }
270 
271 		// Refill RX buffer
272 		nbytes_rx = iio_buffer_refill(rxbuf);
273 		if (nbytes_rx < 0) { printf("Error refilling buf %d\n",(int) nbytes_rx); shutdown(); }
274 
275 		// READ: Get pointers to RX buf and read IQ from RX buf port 0
276 		p_inc = iio_buffer_step(rxbuf);
277 		p_end = iio_buffer_end(rxbuf);
278 		for (p_dat = iio_buffer_first(rxbuf, rx0_i); p_dat < p_end; p_dat += p_inc) {
279 			// Example: swap I and Q
280 			const int16_t i = ((int16_t*)p_dat)[0]; // Real (I)
281 			const int16_t q = ((int16_t*)p_dat)[1]; // Imag (Q)
282 			((int16_t*)p_dat)[0] = q;
283 			((int16_t*)p_dat)[1] = i;
284 		}
285 
286 		// WRITE: Get pointers to TX buf and write IQ to TX buf port 0
287 		p_inc = iio_buffer_step(txbuf);
288 		p_end = iio_buffer_end(txbuf);
289 		for (p_dat = iio_buffer_first(txbuf, tx0_i); p_dat < p_end; p_dat += p_inc) {
290 			// Example: fill with zeros
291 			// 14-bit sample needs to be MSB alligned so shift by 2
292 			// https://wiki.analog.com/resources/eval/user-guides/ad-fmcomms2-ebz/software/basic_iq_datafiles#binary_format
293 			((int16_t*)p_dat)[0] = 0 << 2; // Real (I)
294 			((int16_t*)p_dat)[1] = 0 << 2; // Imag (Q)
295 		}
296 
297 		// Sample counter increment and status output
298 		nrx += nbytes_rx / iio_device_get_sample_size(rx);
299 		ntx += nbytes_tx / iio_device_get_sample_size(tx);
300 		printf("\tRX %8.2f MSmp, TX %8.2f MSmp\n", nrx/1e6, ntx/1e6);
301 	}
302 
303 	shutdown();
304 
305 	return 0;
306 }
307