AD9361学习笔记(二)数据流、寄存器和FM调制

数据流

为了尽快上手AD9361,我决定实现一个FM电台,这篇文章主要介绍了要在AD9361上实现FM调制必须的知识。要基于AD9361进行SDR开发,主要有三种方式:

1、不使用任何IP核,直接写RTL代码配置寄存器和收发数据流。这种方法的性能最高、面积最小、功耗最低,但是开发周期最长。

2、编译官方提供的AD9361 IP核,与ZYNQ的PS端或者Microblaze软核处理器通过AXI总线连接,编译ADI的no-OS库,以C语言开发。好处是不用写HDL代码,但是要调用非常多的IP核,体积和功耗比前者大。

3、在2的基础上,编译ADI的linux,在系统上运行ADI的IIO接口,将开发板通过USB或者以太网接入PC,然后以IIO接口为媒介进行开发。可以访问IIO接口的不只是C/C++,还有MATLAB、GNURadio、SDRSharp、IIO Oscilloscope等。

这里我选择使用MATLAB进行开发,因为MATLAB非常适合算法的开发和验证。但是我下面会提到,MATLAB有一个巨坑。。。。。。

AD9361可以发送很多种数据:

enum dds_data_select {
	DATA_SEL_DDS,
	DATA_SEL_SED,
	DATA_SEL_DMA,
	DATA_SEL_ZERO,	/* OUTPUT 0 */
	DATA_SEL_PN7,
	DATA_SEL_PN15,
	DATA_SEL_PN23,
	DATA_SEL_PN31,
	DATA_SEL_LB,	/* loopback data (ADC) */
	DATA_SEL_PNXX,	/* (Device specific) */
};

DATA_SEL_DDS是使用片上频率合成器合成纯净的单音频率,具体性能怎么样还不清楚。DATA_SEL_DMA模式下,我们可以向AD9361的缓冲区写入最多10^6左右个样本。然后通过配置寄存器,可以让AD9361自动重复发送缓冲区的内容。DATA_SEL_ZERO模式下,AD9361会输出全0。其他的模式不常用。

对于MATLAB来说,一个通道的数据是一个复数的列向量(Nx1)。实部和虚部分别表示I/Q分量。如果用C/C++,一般用两组float来表示。

image 30 - AD9361学习笔记(二)数据流、寄存器和FM调制
数据例子,单通道

MATLAB和AD9361通信,发送数据流的函数都是阻塞的。加上MATLAB只能单线程运行,所以有一个很蛋疼的问题:根据我的测试,发送0.1S的FM调制样本,在MATLAB 中平均耗时0.11S。。。。。。我把官方的MATLAB代码找了一圈,根本找不到他发送的函数在哪里,所以我们这次做的FM电台会有噪音、播放速度也会慢10%左右。

寄存器配置

FPGA通过单线/四线SPI对AD9361进行寄存器配置。AD9361的寄存器非常多,如果用FPGA配置好全部的寄存器,大约需要几千行代码,中间还有一些寄存器还需要配置完之后延时若干毫秒。但是对于我们来说,常用的、关键的寄存器其实不多。在MATLAB上只有几个关心的内容,他们都是adi.AD9361.Tx类的成员。

1、tx=adi.AD9361.Tx(‘uri’,’uri’);

这行代码创建一个adi.AD9361.Tx类。第一个参数代表我们要指定这个设备的URL。 第二个参数指定IP地址或者USB的地址(端口、集线器等)。

比如:’ip:192.168.2.2’、’usb:1.3.5’。

2、tx.DataSource:指定从DMA(缓冲区)发送数据或者用DDS合成频率。输入字符串’DMA’或者’DDS’。

3、tx.EnableCyclicBuffers:指定是否循环发送缓冲区,true或者false。

4、tx.AttenuationChannel10/tx.AttenuationChannel11:设置TX的衰减,两个频道分开设置,单位db。

5、tx.SamplingRate:设置TX发送样本的速率,单位:样本/s。

6、tx.RFBandwidth:射频带宽,单位Hz。

7、tx.CenterFrequency:设置TX的中心频率,单位Hz。

FM调制

我们的FM调制程序分为四个过程:采样、插值、调制和发送。

采样:我选择WAV文件作为音频数据源,因为WAV文件结构简单,可以用MATLAB的函数直接读取进内存。但是现在的音频文件一般都是双声道的,所以我先将两个声道的数据取平均值。

插值:AD9361发送数据的速率我设置为了1MSPS,但是WAV文件采样率一般只有44.1K。所以我们需要先对WAV文件进行线性插值来填补中间样本的空缺,让音频的采样率等于射频DAC的采样率。

调制:FM调制的核心公式如下

fm_deviation = 2.0 * M_PI * 75.0e3 / sample_rate;

fm_phase += fm_deviation * audio_amp;

output[i * BYTES_PER_SAMPLE] = (float)sin(fm_phase) * 32767.0;
output[i * BYTES_PER_SAMPLE + 1] = (float)cos(fm_phase) * 32767.0;

按照公式生成样本发送给AD9361即可。

下面是实现代码:

bandwidth=0.5e6;%TX带宽
samplerate=1e6;%TX采样率
centerfrequency=75e6;%TX中心频率
tx_time=0.1;%一次发送多少秒的音频数据,单位秒

%读取文件,处理为单声道。
[raw,audio_samplerate]=audioread('intro.wav','double');
raw_single=(raw(:,1)+raw(:,2))/2;%处理为单声道
raw_len=length(raw);%wav总样本数
num_audio_sample=audio_samplerate*tx_time;%一次在wav文件中读取的样本数
t=0:num_audio_sample;%样本x轴

fm_deviation=2*pi*75e3/samplerate;%fm频率偏移
num_sample=samplerate*tx_time;%一次发送给设备的样本数
fm_out=complex(1:num_sample,1:num_sample);%预先分配内存,提高性能
fm_out=fm_out';%设备只接受列向量,转置为列向量

tx = adi.AD9361.Tx('uri','ip:192.168.2.2');%连接设备
tx.DataSource = 'DMA';%写入设备DMA
tx.EnableCyclicBuffers = false;%关闭循环
tx.AttenuationChannel0=0;%设置为最大发射功率
tx.SamplingRate=samplerate;%设置采样率
tx.RFBandwidth=bandwidth;%设置带宽
tx.CenterFrequency=centerfrequency;%设置中心频率

y=double(1:num_audio_sample+1);%预分配音频样本的内存
num_samples=ceil(raw_len/num_audio_sample);%fm总帧数
fm_out_all=double(ones(num_sample,num_samples));%预分配fm所有帧的内存

fm_phase=0;%时间
t0=clock;%统计调制时间
k=1;
j=1;
used_time=0;

while j<raw_len
    %读取样本
    if raw_len-j>num_audio_sample
        y=raw_single(j:j+num_audio_sample);
        j=j+num_audio_sample;
    else
        y(1:num_audio_sample+1)=0;
        y(1:raw_len-j+1)=raw_single(j:raw_len);
        j=raw_len;
    end
    %插值
    tp=0:audio_samplerate/samplerate:num_audio_sample;
    intp=interp1(t,y,tp);
    %fm调制
    for i=1:num_sample
        fm_phase=fm_phase+fm_deviation*intp(i)*0.45;
        next_out=32767*sin(fm_phase)+32767i*cos(fm_phase);
        fm_out(i)=next_out;
    end
    fm_out_all(:,k)=fm_out;
    k=k+1;
end
etime(clock,t0)%调制用时

while true
    t0=clock;
    for i=1:num_samples
        t1=clock;
        tx(fm_out_all(:,i));
        etime(clock,t1)%单帧发送时间(阻塞I/O)
    end
    etime(clock,t0)%输出播放时间
end
image 29 - AD9361学习笔记(二)数据流、寄存器和FM调制
MATLAB的运行效果

发表评论

电子邮件地址不会被公开。 必填项已用*标注