0.目录
- 项目需求
- 实现功能
- 项目分析
- 程序流程
- 资源占用
- 主要代码
- 主要难题
- 未来的计划建议
- 附件
1.项目需求
(1)通过板上的高速DAC(10bits/125Msps)配合FPGA内部DDS的逻辑,生成波形可调(正弦波、三角波、方波)、频率可调(DC-)、幅度可调的波形
(2)生成模拟信号的频率范围为DC-20MHz,调节精度为1Hz
(3)生成模拟信号的幅度为最大1Vpp,调节范围为0.1V-1V
(4)在OLED上显示当前波形的形状、波形的频率以及幅度
(5)利用板上旋转编码器和按键能够对波形进行切换、进行参数调节
2.实现功能
使用2个按键与1个旋转编码器实现波形参数设置。
K1控制波形转换,按照正弦波-三角波-方波的顺序切换,且通过RGB指示灯显示,分别对应红色、绿色和蓝色。
K2控制幅度,幅度范围从0.1V-1Vpp,默认为0.1Vpp,每次按下后增大0.1Vpp。
旋转编码器控制频率,频率范围最大为20MHz,按下旋转编码器后可以调整频率步进值,每次按下步进值x10。
(1)0.5Vpp 1kHz正弦波
(2)0.5Vpp 1kHz三角波
(3)0.5Vpp 1kHz方波
(4)0.8Vpp 100kHz 正弦波
(5)1.0Vpp 1MHz 正弦波
频率到达10MHz时由于示波器性能有限已无法观测。
3.实现思路
主要分为输入模块、输出模块和显示模块。
3.1 输入模块
输入模块内主要为按键防抖与旋转编码器解码ip核,对输入信息进行整合,得当设置的波形、幅度、频率以及频率步进。
3.1.1 按键
根据上面的原理描述,我们可以对按键的输入管脚进行边沿检测,当检测到输入信号变化后计数清零并开始计数,计数到20ms时进行采样,然后再对每次采样的值进行下降沿检测,检测的结果作为按键消抖的脉冲输出,然后每次检测到下降沿时让状态输出进行翻转并输出。
3.1.2 旋转编码器
旋转编码器给出两相方波,它们的相位差90°,通常称为A通道和B通道。其中一个通道给出与转速相关的信息,与此同时,通过两个通道信号进行顺序对比,得到旋转方向的信息。
3.2 输出模块
输出模块分为DAC驱动和ROM(存储波形数据)。
3.2.1 DAC
DAC使用基于3Peaks的3PD5651制作的高速DAC模块,驱动简单,仅需10位数据位与时钟信号。
波形数据使用Guagle_wave软件生成mif格式文件,如下图所示。需要注意保存时不能使用另存为,不然会保存空文件,直接使用文件-保存即可。
3.2.2 ROM
通过ROM的ip核可以完成ROM初始化,为节约空间将波形设置为256位,如有需要可以自行修改。
利用相位累加器实现对波形频率的调整,更详细的DDS教程可以查看dds_verilog [电子森林] (eetree.cn)。
3.2.3 PLL
但由于板载晶振频率较低仅为12M,使用PLL进行倍频,可以得到200MHz时钟作为DAC的时钟信号。
3.3 显示模块
显示模块使用0.96寸单色OLED,非常常见,使用FPGA驱动较为繁琐,使用状态机来进行初始化寄存器的写入以及屏幕刷新。
OLED为6线,从左到右依次为GND、VCC、SCL、SDA、RES、DC,使用3.3V供电。使用时可以参考中景园电子的教程,按照其单片机驱动时的代码,对OLED模块编写寄存器,初始化时的寄存器命令不同版本数量不等,使用查找表实现存储器,可以优化成调用官方RAM或ROM IP存储。为节省空间去除了中文字库等,仅保留5*8点阵字库数据,亦可删除掉大写字母或小写字母部分以进一步节省空间。
OLED分为4行,分别显示波形、幅值、频率以及频率步进。
更详细教程参见如何用FPGA驱动OLED屏? (qq.com)。
4.程序流程
RTL图如下所示。
输入模块为WaveControl中,输出模块为DAC,显示模块为OLED12864。
上方FreqStep的部分对频率步进的led灯进行设置。
5.资源占用
资源占用报告如下图所示:
6.主要代码
6.1 DAC
module DAC (
input DAC_clk,
input rst_n,
input [2:0] WaveType,
input [31:0] WaveFreq,
input [3:0] WaveAmp,
output[9:0] DAC_Data
);
localparam internal = 12375000;
//localparam amp_ver = 9;
reg [31:0] wavedata;
reg [31:0] phase_acc;
reg [63:0] acc;
reg [31:0] amp;
reg [15:0] amp_ver;
wire [9:0] Square_Data;
wire [9:0] Triangular_Data;
wire [9:0] Sine_Data;
assign DAC_Data = (wavedata*amp_ver)>>8;
sin256_10bit sin256_10bit_inst (
.address (phase_acc[31:24]),
.clock (DAC_clk),
.q (Sine_Data)
);
square256_10bit square256_10bit_inst (
.address (phase_acc[31:24]),
.clock (DAC_clk),
.q (Square_Data)
);
triangular256_10bit triangular_10bit_inst (
.address (phase_acc[31:24]),
.clock (DAC_clk),
.q (Triangular_Data)
);
always @(negedge rst_n or posedge DAC_clk) begin
if (!rst_n) begin
wavedata<=0;
amp<=0;
phase_acc<=0;
acc<=23'hFFFFFF;
end
else begin
phase_acc<=phase_acc+acc;
acc<= (WaveFreq<<28)/internal;
//DAC_Data<=amp/amp_ver;
case (WaveType)
3'b100:begin
wavedata<=((Square_Data));
end
3'b010:begin
wavedata<=((Triangular_Data));
end
3'b001:begin
wavedata<=((Sine_Data));
end
default:begin
wavedata<=0;
end
endcase
case (WaveAmp)
1:begin amp_ver<=9;end
2:begin amp_ver<=18;end
3:begin amp_ver<=28;end
4:begin amp_ver<=37;end
5:begin amp_ver<=47;end
6:begin amp_ver<=57;end
7:begin amp_ver<=66;end
8:begin amp_ver<=76;end
9:begin amp_ver<=85;end
10:begin amp_ver<=94;end
default: begin amp_ver<=8;end
endcase
end
end
endmodule
完整代码见附件。
整个程序较为复杂,设计时需一个一个ip核进行调试,最后进行连接。
7.主要难题
- OLED的驱动较单片机相比复杂很多,为实现初始化需要使用复杂的状态机,对编程能力要求较高,可以参考文中OLED驱动的代码,在此基础上进行修改。
- 利用相位累加器对频率进行控制,设置较长位数的相位累加器,可以减小原信号频率,使用对任意频率信号的生成。
8.未来的计划建议
- OLED配置指令数据与字库可以存入ROM当中,减少逻辑单元使用。
- 使用Intel/Altera在其Quartus中的NCO(数字控制振荡器)生成正弦波数据。
9.附件
附件过大无法上传,见百度网盘。
链接:https://pan.baidu.com/s/1AGnfPpEvE67csIHHnyFe8A
提取码:eair