2023年寒假在家练-基于STM32+ICE40实现DDS任意波形发生器/本地控制
此项目主要实现DDS 任意波形发生器,通过按键和旋转编码器可以实现 波形选择、频率选择、频率步进调整、增益调整等功能
标签
STM32
FPGA
DDS
2023寒假
xinshuwei
更新2023-03-28
395

项目描述(项目介绍,设计思路)

此次实现功能是 2023年寒假在家练基于STM32G01+ICE40UP5K实现DDS信号发生器,信号发生器支持 输出正弦波、方波、三角波;支持输出频率范围1Hz到5MHz,支持频率步进调整,支持步进有 1Hz ,10Hz,100Hz,1kHz,10kHz,100kHz,1MHz;支持输出幅度调整,0.1V到1V(即0.2VPP 到2VPP,步进0.2Vpp).

整体设计思路如下:

 FlFIgC_vBY8DV2V944ZIA_uyJaIj

1.通过stm32完成基础外设的驱动,包括OLED的显示、按键处理、EC11旋转编码器的旋转驱动等

2.制定stm32 与FPGA SPI通讯协议,消息格式

3.通过FPGA 产生 时钟和 数据 从而驱动DAC 产生相应的波形

4.给每个按键赋予不同的功能,整体如下

序号       名称    功能
1 EC11 旋转 不同模式时,实现不同模式下参数的增、减
2 KEY1 模式切换,支持波形选择模式,频率调整模式,频率步进调整模式,输出波形增益调整模式
3 KEY2 重置当前界面设置
4 EC11 Key 界面参数生效,发送给FPGA,输出相应设计波形

 

 

 

 

 

FPGA资源占用报告

Fin3vb7EY3ZOBqrX3W2RJ6olIjt-

FqrUf7eQZIbPALDgLiXCnC7lL0we

  上图通过Radiant 内部report 功能截取,通过报告可以看出使用的lut4 只有228 ,FPGA 中有效利用ROM PLL等资源,减少LUT4使用,up5k整体LUT4为5280,使用了不到1/20.

硬件介绍

FlOOnd3sqdTnwJSyhqdZRxMLp2Lt

EC11 和两个按钮设置为上拉输入,原理图中标注了相应的MCU管脚,方便stm32cube 进行工程初始化

Fkai9J9MxJgsXOvb7Ge0xy-5IpG4

OLED 使用硬件SPI 进行驱动外加DC RST 管脚GPIO 设置

FkqHvRg7Zu8KwQjnIDosEJXuT132

DAC 使用的是3Peak 125Msps 模块,10bit 数据输入加clk 即可完成驱动

FinNW0zEGyKfxbEjj2cP90JVg01w

核心板内 MCU 与FPGA 通过 SPI 进行连接,这里整理对应的管脚关系,方便FPGA 进行管脚指定约束。

实现的功能及图片展示

FoBYTRAJC7JyM3JLZjTTGYHOu4cF

oled 显示4行  当点击模式切换按钮时,会出现相应箭头提示进行相关的调整

EC11 btn 实现当前界面参数生效,发送给FPGA 进行波形显示

主要代码片段及说明

记录下群里面整理的bin 转 rbt python 脚本,脚本是widow下的,linux 需要更换换行(windows-- /r/n  linux -/n),使用开源工具会生成bin格式,通过脚本生成rbt格式

import datetime
import click


head_line = "Lattice Semiconductor Corporation ASCII Bitstream\r\n"
gen_time  = "Date:"+f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"+"\r\n"*6


def int2bin_list(int_number_list):
    # convert the binary code to binary text one by one
    return "".join(["{0:08b}".format(i) for i in int_number_list])

@click.command()
@click.option('--binfile', default='binfile.bin', help='binfile.bin')
@click.option('--rbtfile', default='rbtfile.txt', help='rbtfile.txt.')
def bin2rbt(binfile, rbtfile):
    # read file 
    with open(binfile, "rb") as bf:
        bin_data = bf.read()

    # process text
    # convert the binary code to binary text every 10 byte. 
    binlines = ""
    for i in range(len(bin_data)-6):
        if i%10 == 0:
            binlines = binlines + "\r\n"
        binlines += int2bin_list([bin_data[i]])
    binlines+= "\r\n"
    
    # the laste 6 byte need specific format
    endline1 = int2bin_list(bin_data[-6:-4]) + "\r\n"
    endline2 = int2bin_list(bin_data[-4:])+"\r\n"

    # save the text to file 
    with open(rbtfile, "wb") as rf:
        context_lines = head_line + \
                gen_time + binlines + \
                endline1 + endline2 
        context_lines = "".join(context_lines).encode()
        rf.write(context_lines)


if __name__ == '__main__':
    bin2rbt()

stm32 EC11  按键中断响应

整体使用两个中断回调函数,GPIO 下降沿、上升沿,

下降沿完成所有btn 的检测,通过延迟判读当前电平状态进行消抖,防止误触发多次。

EC11 通过检测A的下降沿 然后读取B的电平实现左旋转 右旋转的判定,完成相应的逻辑判定后,根据模式设定或功能定义实现oled 的显示变化或者发送消息给FPGA

void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
	switch(GPIO_Pin)
	{
		case key1_Pin: 
		{
			if(KEY1_READ==0)
			{
				HAL_Delay(10);
				if(KEY1_READ==0)
				{
					
					OLED_Clear1();
					draw_sel_mode(cur_row_mode);
					
					SetCursor(40,16);
					step_flag = WAV_FREQ_KHZ;
					wav_freq_uint = pow(10,step_flag);
					freq = wav_freq_uint; 
					printf("%dkHz",freq/wav_freq_uint);
					SetCursor(40,32);
					printf("%s",wav_uint_name[step_flag]);
					
					OLED_Refresh();
				}
				
			}
			break;
		}
		case key2_Pin: 
		{
			if(KEY2_READ==0)
			{
				HAL_Delay(10);
				if(KEY2_READ==0)
				{
					cur_row_mode++;
					cur_row_mode = cur_row_mode%ROW_NUM;
					draw_sel_mode(cur_row_mode);
					
				}
			}
			break;
		}
		case encoderA_Pin: //A negedge
		{
			EC11_B_State = EC11_B_READ;
			switch (cur_row_mode)
			{
				case TYPE_ROW:EC11_update_mode(1);break;
				case FREQ_ROW:EC11_update_freq(1);break;
				case STEP_ROW:EC11_update_uint(1);break;			
				case GAIN_ROW:EC11_update_gain(1);break;
				default:break;
			}
			
			break;
		}
		
		case encoder_key_Pin: //
		{
			fpga_spi_send();
			break;
		}
		default:	break;
	}
}




void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
	switch(GPIO_Pin)
	{
		case encoderA_Pin:
		{
			EC11_B_State = EC11_B_READ;
			switch (cur_row_mode)
			{
				case TYPE_ROW:EC11_update_mode(0);break;
				case FREQ_ROW:EC11_update_freq(0);;break;
				case STEP_ROW:EC11_update_uint(0);break;			
				case GAIN_ROW:EC11_update_gain(0);break;
				default:break;
			}
			break;
		}
		default:	break;
	}
}

FPGA 和mcu的通信通过32bit的位域进行定义,位域的定义减少通信位宽的设定,提高通信效率。

typedef struct 
{
	u32 freq_code:10;
	u32 freq_uint:2;
	u32 wave_gain:4;
	u32 wave_type:2;
	u32 unused:14;
}DDSData;

这里实际使用了18个bit,后续可进行相位调整、偏置调整、方波占空比调整做功能预留。

 

FPGA ROM 产生脚本

正弦波生成mem 文件

import matplotlib.pyplot as plt
import numpy as np
ADDR_Bit = 10
Data_Bit = 10



P  = 0
Fs = 2**ADDR_Bit ## sample cnt
t= np.linspace(0,(Fs-1)/Fs,Fs)
#DC = 2**(Data_Bit-1)
##DC = 51.2
DC = 42.7

Gain = 42.7  
#Gain = 2**(Data_Bit-1)
sin_singal = Gain*np.sin(2*np.pi*t+np.pi*P/180)+DC
plt.figure(1)
plt.plot(sin_singal)
out_signal = []
outfile = open("sin_wav_1024.mem",'wt')
for i in range(Fs):
    outdata = int(np.ceil(sin_singal[i]))
    out_signal.append(outdata)
    outfile.write("%X\n"%outdata)
outfile.close()
plt.figure(2)
plt.plot(out_signal)

三角波生成mem 文件

import matplotlib.pyplot as plt
import numpy as np
ADDR_Bit = 10
Data_Bit = 10


P  = 0
Fs = 2**ADDR_Bit ## sample cnt
t= np.linspace(0,(Fs-1)/Fs,Fs)
DC = 2**(Data_Bit-1)
Gain = 42.7  #Gain = 2**(Data_Bit-1)


half_size= 2**(ADDR_Bit-1)
tri = np.linspace(0,(half_size-1)/half_size,half_size)
tri_signal =[]
for i in range(half_size):
    tri_signal.append(tri[i]*Gain+DC)
for i in range(half_size):
    tri_signal.append(np.flip(tri)[i]*Gain+DC)
plt.figure(1)
plt.plot(tri_signal)

out_signal = []
outfile = open("tri_wav_1024.mem","wt")
for i in range(Fs):
    outdata = int(np.ceil(tri_signal[i]))
    out_signal.append(outdata)
    outfile.write("%X\n"%outdata)
outfile.close()
plt.figure(2)
plt.plot(out_signal)

 

 

到的主要难题及解决方法

  1. FPGA DDS 输出波形不对 ,后来定位到输出采样和同步采用同一个边沿会导致DAC采样数据输出错误,后来将FPGA输出在上升沿,给DAC clk 进行边沿取反,方便DAC在原始clk的下降沿采样,使采样和同步进行边沿区分
  2. FPGA 正弦波不能调整幅度,后来定位到直流偏移未和波形进行统一的缩放,会导致输出波形向上偏,导致输出波形不连续,后来在波形生成时,将波形和增益进行统一缩放,当然如果适当的运算,可以不使用ROM,直接通过LUT进行计算得到,这里通过ROM更换LUT资源。

未来的计划或建议

     1. 进行下功能扩展,添加锯齿波,心形波,支持相位调整,支持PC端调整,方便在后续项目中使用

       2.多举办这样的活动,希望活动越办越好。

 

代码地址 

 

 

 

附件下载
dds_pro_impl_1.rbt
dds_pro.hex
团队介绍
苏州工程师一枚
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号