2023年电赛H题(信号分离装置)-FPGA+stm32解法
目录
第二部分(stm32接收数据进行FFT识别波形以及频率并发送)
前言
本文章除开要求一使用的增益为一的加法器以外,其余皆由FPGA+stm32实现。
题目




解题思路
题目要求从信号发生器输出两路波形A和B并通过一个增益为1的加法器生成信号C,我们需要做的就是从C中将A'和B'解出来,有一种方法是将C进行傅里叶变换然后识别出两路波形分别是什么然后将频域分离出来随后进行傅里叶逆变换再输出就行,还有一种方法就是本文章所使用的方法,由于题目不考虑波形的相位和峰峰值与原波形的关系,所以我们可以将输入的C信号进行傅里叶变换识别出两路波形的频率,并且由于AB波的峰峰值为固定的1V,而正弦波为单音信号频谱集中,三角波有三次谐波的存在导致基波信号的峰峰值会被削弱使得可以通过比较频谱中基波的幅值大小来识别输入的波形是三角波还是正弦波,然后就能得到两路波的波形以及频率,随后就可以使用dds重构出A'和B'波形,但是这个方法有一个问题就是由于重构的波形与信号发生器所出的波形不在同一个时钟上,由于不同时钟的微小差异,会使得相位差在不停的累积就会产生信号漂移,所以我们需要设计一个锁相环将生成的信号与原信号的相位锁住就不会再产生漂移了,移相只需要改变ROM表的地址就可以实现。
基本框架
C信号——>高速ADC——>FPGA——>FIFO——>串口——>stm32——>FFT——>识别波形和频率——>串口——>DDS——>锁相环——>高速DAC——>A',B'
代码思路
第一部分(FPGA的FIFO以及串口发送接收)
1.FIFO
此处使用的FIFO为黑金AX7035B所给的历程,其中写入速率为1MHz(与高速ADC的采样频率相同),读取速率为12.5KHz(串口的发送速率)
assign ad9238_clk_ch0 = clk_1M;
wire wr_en,rd_en,full,empty,wr_rst_busy,rd_rst_busy;
wire [10:0] rd_data_count,wr_data_count;
reg [7:0] w_data;
localparam W_IDLE = 1;
localparam W_FIFO = 2;
localparam R_IDLE = 1;
localparam R_FIFO = 2;
reg[2:0] write_state;
reg[2:0] next_write_state;
reg[2:0] read_state;
reg[2:0] next_read_state;
wire [7:0] rx_data,tx_data;
reg tx_vilid;
always@(posedge clk_1M or negedge rst)
begin
if(rst == 1'b0)
write_state <= W_IDLE;
else
write_state <= next_write_state;
end
always@(*)
begin
case(write_state)
W_IDLE:
if(empty == 1'b1) //FIFO is empty, start writing FIFO
next_write_state <= W_FIFO;
else
next_write_state <= W_IDLE;
W_FIFO:
if(full == 1'b1) //FIFO is full
next_write_state <= W_IDLE;
else
next_write_state <= W_FIFO;
default:
next_write_state <= W_IDLE;
endcase
end
assign wr_en = (next_write_state == W_FIFO) ? 1'b1 : 1'b0;
always@(posedge clk_1M or negedge rst)
begin
if(rst == 1'b0)
w_data <= 16'd0;
else
if (wr_en == 1'b1)
w_data <= ADC;
else
w_data <= ADC;
end
always@(posedge clk_1M or negedge rst)
begin
if(rst == 1'b0)
read_state <= R_IDLE;
else
read_state <= next_read_state;
end
always@(*)
begin
case(read_state)
R_IDLE:
if(full == 1'b1) //FIFO is full, starting read FIFO
next_read_state <= R_FIFO;
else
next_read_state <= R_IDLE;
R_FIFO:
if(empty == 1'b1) //FIFO is empty
next_read_state <= R_IDLE;
else
next_read_state <= R_FIFO;
default:
next_read_state <= R_IDLE;
endcase
end
reg [10:0] rd_data_count_last;
always@(posedge clk or negedge rst)
begin
if(!rst)
rd_data_count_last<=0;
else
begin
rd_data_count_last<=rd_data_count;
if(rd_data_count_last!=rd_data_count && rd_en)
tx_vilid<=1;
else
tx_vilid<=0;
end
end
assign rd_en = (next_read_state == R_FIFO) ? 1'b1 : 1'b0;
fifo fifo (
.rst(~rst), // input wire rst
.wr_clk(clk_1M), // input wire wr_clk
.rd_clk(clk_100K), // input wire rd_clk
.din(w_data), // input wire [7 : 0] din
.wr_en(wr_en), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(tx_data), // output wire [7 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.rd_data_count(rd_data_count), // output wire [10 : 0] rd_data_count
.wr_data_count(wr_data_count) // output wire [10 : 0] wr_data_count
);
2.(FPGA串口发送)
此处串口发送使用的也是黑金的AX7035B的历程(在FIFO中每读一个数就通过串口发送出去)
uart_tx#(
.CLK_FRE(50),
.BAUD_RATE(115200) //serial baud rate
)uart_tx(
.clk(clk), //clock input
.rst_n(rst), //asynchronous reset input, low active
.tx_data(tx_data), //data to send
.tx_data_valid(tx_vilid), //data to be sent is valid
.tx_pin(tx_pin) //serial data output
);
module uart_tx
#(
parameter CLK_FRE = 50, //clock frequency(Khz)
parameter BAUD_RATE = 115200 //serial baud rate
)
(
input clk, //clock input
input rst_n, //asynchronous reset input, low active
input[7:0] tx_data, //data to send
input tx_data_valid, //data to be sent is valid
output reg tx_data_ready, //send ready
output tx_pin //serial data output
);
//calculates the clock cycle for baud rate
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;
//state machine code
localparam S_IDLE = 1;
localparam S_START = 2;//start bit
localparam S_SEND_BYTE = 3;//data bits
localparam S_STOP = 4;//stop bit
reg[2:0] state;
reg[2:0] next_state;
reg[15:0] cycle_cnt; //baud counter
reg[2:0] bit_cnt;//bit counter
reg[7:0] tx_data_latch; //latch data to send
reg tx_reg; //serial data output
assign tx_pin = tx_reg;
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
state <= S_IDLE;
else
state <= next_state;
end
always@(*)
begin
case(state)
S_IDLE:
if(tx_data_valid == 1'b1)
next_state <= S_START;
else
next_state <= S_IDLE;
S_START:
if(cycle_cnt == CYCLE - 1)
next_state <= S_SEND_BYTE;
else
next_state <= S_START;
S_SEND_BYTE:
if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7)
next_state <= S_STOP;
else
next_state <= S_SEND_BYTE;
S_STOP:
if(cycle_cnt == CYCLE - 1)
next_state <= S_IDLE;
else
next_state <= S_STOP;
default:
next_state <= S_IDLE;
endcase
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
tx_data_ready <= 1'b0;
end
else if(state == S_IDLE)
if(tx_data_valid == 1'b1)
tx_data_ready <= 1'b0;
else
tx_data_ready <= 1'b1;
else if(state == S_STOP && cycle_cnt == CYCLE - 1)
tx_data_ready <= 1'b1;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
tx_data_latch <= 8'd0;
end
else if(state == S_IDLE && tx_data_valid == 1'b1)
tx_data_latch <= tx_data;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
bit_cnt <= 3'd0;
end
else if(state == S_SEND_BYTE)
if(cycle_cnt == CYCLE - 1)
bit_cnt <= bit_cnt + 3'd1;
else
bit_cnt <= bit_cnt;
else
bit_cnt <= 3'd0;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
cycle_cnt <= 16'd0;
else if((state == S_SEND_BYTE && cycle_cnt == CYCLE - 1) || next_state != state)
cycle_cnt <= 16'd0;
else
cycle_cnt <= cycle_cnt + 16'd1;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
tx_reg <= 1'b1;
else
case(state)
S_IDLE,S_STOP:
tx_reg <= 1'b1;
S_START:
tx_reg <= 1'b0;
S_SEND_BYTE:
tx_reg <= tx_data_latch[bit_cnt];
default:
tx_reg <= 1'b1;
endcase
end
endmodule
3.FPGA串口接收
此处串口接收使用的也是黑金的AX7035B的历程(帧头为FF 帧尾为FE 第一个数据为第一个波的波形 第二个数据为第一个波的频率 第三个数据为第二个波的波形 第四个数据为第二个波的频率)
reg [7:0] data1,data2,data3,data4;
reg rx_start;
wire rx_done;
reg [2:0] state;
uart_rx
#(
.CLK_FRE(50), //clock frequency(Mhz)
.BAUD_RATE(115200) //serial baud rate
)uart_rx
(
.clk(clk), //clock input
.rst_n(rst), //asynchronous reset input, low active
.rx_data(rx_data), //received serial data
.rx_data_ready(rx_start), //data receiver module ready
.rx_data_valid(rx_done), //received serial data is valid
.rx_pin(rx_pin) //serial data input
);
always@(posedge clk or negedge rst)
begin
if(!rst)
begin
state<=0;
data1<=0;
data2<=0;
data3<=0;
data4<=0;
end
else
begin
case(state)
3'b000:
begin
rx_start<=1;
if(rx_done)
begin
if(rx_data==8'hFF)
begin
rx_start<=0;
state<=state+1;
data1<=0;
data2<=0;
data3<=0;
data4<=0;
end
end
end
3'b001:
begin
rx_start<=1;
if(rx_done)
begin
rx_start<=0;
data1<=rx_data;
state<=state+1;
end
end
3'b010:
begin
rx_start<=1;
if(rx_done)
begin
rx_start<=0;
data2<=rx_data;
state<=state+1;
end
end
3'b011:
begin
rx_start<=1;
if(rx_done)
begin
rx_start<=0;
data3<=rx_data;
state<=state+1;
end
end
3'b100:
begin
rx_start<=1;
if(rx_done)
begin
rx_start<=0;
data4<=rx_data;
state<=state+1;
end
end
3'b101:
begin
rx_start<=1;
if(rx_done)
begin
if(rx_data==8'hFE)
begin
rx_start<=0;
state<=0;
wave1<=data1;
wave1_freq<=data2;
wave2<=data3;
wave2_freq<=data4;
end
else
begin
rx_start<=0;
state<=0;
data1<=0;
data2<=0;
data3<=0;
data4<=0;
end
end
end
endcase
end
end
module uart_rx
#(
parameter CLK_FRE = 50, //clock frequency(Mhz)
parameter BAUD_RATE = 115200 //serial baud rate
)
(
input clk, //clock input
input rst_n, //asynchronous reset input, low active
output reg[7:0] rx_data, //received serial data
output reg rx_data_valid, //received serial data is valid
input rx_data_ready, //data receiver module ready
input rx_pin //serial data input
);
//calculates the clock cycle for baud rate
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;
//state machine code
localparam S_IDLE = 1;
localparam S_START = 2; //start bit
localparam S_REC_BYTE = 3; //data bits
localparam S_STOP = 4; //stop bit
localparam S_DATA = 5;
reg[2:0] state;
reg[2:0] next_state;
reg rx_d0; //delay 1 clock for rx_pin
reg rx_d1; //delay 1 clock for rx_d0
wire rx_negedge; //negedge of rx_pin
reg[7:0] rx_bits; //temporary storage of received data
reg[15:0] cycle_cnt; //baud counter
reg[2:0] bit_cnt; //bit counter
assign rx_negedge = rx_d1 && ~rx_d0;
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
rx_d0 <= 1'b0;
rx_d1 <= 1'b0;
end
else
begin
rx_d0 <= rx_pin;
rx_d1 <= rx_d0;
end
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
state <= S_IDLE;
else
state <= next_state;
end
always@(*)
begin
case(state)
S_IDLE:
if(rx_negedge)
next_state <= S_START;
else
next_state <= S_IDLE;
S_START:
if(cycle_cnt == CYCLE - 1)//one data cycle
next_state <= S_REC_BYTE;
else
next_state <= S_START;
S_REC_BYTE:
if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7) //receive 8bit data
next_state <= S_STOP;
else
next_state <= S_REC_BYTE;
S_STOP:
if(cycle_cnt == CYCLE/2 - 1)//half bit cycle,to avoid missing the next byte receiver
next_state <= S_DATA;
else
next_state <= S_STOP;
S_DATA:
if(rx_data_ready) //data receive complete
next_state <= S_IDLE;
else
next_state <= S_DATA;
default:
next_state <= S_IDLE;
endcase
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_data_valid <= 1'b0;
else if(state == S_STOP && next_state != state)
rx_data_valid <= 1'b1;
else if(state == S_DATA && rx_data_ready)
rx_data_valid <= 1'b0;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_data <= 8'd0;
else if(state == S_STOP && next_state != state)
rx_data <= rx_bits;//latch received data
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
bit_cnt <= 3'd0;
end
else if(state == S_REC_BYTE)
if(cycle_cnt == CYCLE - 1)
bit_cnt <= bit_cnt + 3'd1;
else
bit_cnt <= bit_cnt;
else
bit_cnt <= 3'd0;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
cycle_cnt <= 16'd0;
else if((state == S_REC_BYTE && cycle_cnt == CYCLE - 1) || next_state != state)
cycle_cnt <= 16'd0;
else
cycle_cnt <= cycle_cnt + 16'd1;
end
//receive serial data bit data
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
rx_bits <= 8'd0;
else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 - 1)
rx_bits[bit_cnt] <= rx_pin;
else
rx_bits <= rx_bits;
end
endmodule
4.总结
这是与stm32通信模块的所有代码
module stm32_communication(
input clk,
input rst,
input clk_1M, //采样率1MHz
input [7:0] ADC,
input rx_pin,
output reg wave1,
output reg [7:0] wave1_freq,
output reg wave2,
output reg [7:0] wave2_freq,
output tx_pin
);
reg clk_100K_reg; //串口发送速率12.5Khz
reg [11:0] clk_100K_cnt;
wire clk_100K = clk_100K_reg;
always@(posedge clk or negedge rst)
begin
if(!rst)
begin
clk_100K_cnt<=0;
clk_100K_reg<=0;
end
else
begin
clk_100K_cnt<=clk_100K_cnt+1;
if(clk_100K_cnt==1999)
begin
clk_100K_cnt<=0;
clk_100K_reg<=~clk_100K_reg;
end
end
end
wire wr_en,rd_en,full,empty,wr_rst_busy,rd_rst_busy;
wire [10:0] rd_data_count,wr_data_count;
reg [7:0] w_data;
localparam W_IDLE = 1;
localparam W_FIFO = 2;
localparam R_IDLE = 1;
localparam R_FIFO = 2;
reg[2:0] write_state;
reg[2:0] next_write_state;
reg[2:0] read_state;
reg[2:0] next_read_state;
wire [7:0] rx_data,tx_data;
reg tx_vilid;
always@(posedge clk_1M or negedge rst)
begin
if(rst == 1'b0)
write_state <= W_IDLE;
else
write_state <= next_write_state;
end
always@(*)
begin
case(write_state)
W_IDLE:
if(empty == 1'b1) //FIFO is empty, start writing FIFO
next_write_state <= W_FIFO;
else
next_write_state <= W_IDLE;
W_FIFO:
if(full == 1'b1) //FIFO is full
next_write_state <= W_IDLE;
else
next_write_state <= W_FIFO;
default:
next_write_state <= W_IDLE;
endcase
end
assign wr_en = (next_write_state == W_FIFO) ? 1'b1 : 1'b0;
always@(posedge clk_1M or negedge rst)
begin
if(rst == 1'b0)
w_data <= 16'd0;
else
if (wr_en == 1'b1)
w_data <= ADC;
else
w_data <= ADC;
end
always@(posedge clk_1M or negedge rst)
begin
if(rst == 1'b0)
read_state <= R_IDLE;
else
read_state <= next_read_state;
end
always@(*)
begin
case(read_state)
R_IDLE:
if(full == 1'b1) //FIFO is full, starting read FIFO
next_read_state <= R_FIFO;
else
next_read_state <= R_IDLE;
R_FIFO:
if(empty == 1'b1) //FIFO is empty
next_read_state <= R_IDLE;
else
next_read_state <= R_FIFO;
default:
next_read_state <= R_IDLE;
endcase
end
reg [10:0] rd_data_count_last;
always@(posedge clk or negedge rst)
begin
if(!rst)
rd_data_count_last<=0;
else
begin
rd_data_count_last<=rd_data_count;
if(rd_data_count_last!=rd_data_count && rd_en)
tx_vilid<=1;
else
tx_vilid<=0;
end
end
assign rd_en = (next_read_state == R_FIFO) ? 1'b1 : 1'b0;
fifo fifo (
.rst(~rst), // input wire rst
.wr_clk(clk_1M), // input wire wr_clk
.rd_clk(clk_100K), // input wire rd_clk
.din(w_data), // input wire [7 : 0] din
.wr_en(wr_en), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(tx_data), // output wire [7 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.rd_data_count(rd_data_count), // output wire [10 : 0] rd_data_count
.wr_data_count(wr_data_count) // output wire [10 : 0] wr_data_count
);
reg [7:0] data1,data2,data3,data4;
reg rx_start;
wire rx_done;
reg [2:0] state;
uart_rx
#(
.CLK_FRE(50), //clock frequency(Mhz)
.BAUD_RATE(115200) //serial baud rate
)uart_rx
(
.clk(clk), //clock input
.rst_n(rst), //asynchronous reset input, low active
.rx_data(rx_data), //received serial data
.rx_data_ready(rx_start), //data receiver module ready
.rx_data_valid(rx_done), //received serial data is valid
.rx_pin(rx_pin) //serial data input
);
uart_tx#(
.CLK_FRE(50),
.BAUD_RATE(115200) //serial baud rate
)uart_tx(
.clk(clk), //clock input
.rst_n(rst), //asynchronous reset input, low active
.tx_data(tx_data), //data to send
.tx_data_valid(tx_vilid), //data to be sent is valid
.tx_pin(tx_pin) //serial data output
);
always@(posedge clk or negedge rst)
begin
if(!rst)
begin
state<=0;
data1<=0;
data2<=0;
data3<=0;
data4<=0;
end
else
begin
case(state)
3'b000:
begin
rx_start<=1;
if(rx_done)
begin
if(rx_data==8'hFF)
begin
rx_start<=0;
state<=state+1;
data1<=0;
data2<=0;
data3<=0;
data4<=0;
end
end
end
3'b001:
begin
rx_start<=1;
if(rx_done)
begin
rx_start<=0;
data1<=rx_data;
state<=state+1;
end
end
3'b010:
begin
rx_start<=1;
if(rx_done)
begin
rx_start<=0;
data2<=rx_data;
state<=state+1;
end
end
3'b011:
begin
rx_start<=1;
if(rx_done)
begin
rx_start<=0;
data3<=rx_data;
state<=state+1;
end
end
3'b100:
begin
rx_start<=1;
if(rx_done)
begin
rx_start<=0;
data4<=rx_data;
state<=state+1;
end
end
3'b101:
begin
rx_start<=1;
if(rx_done)
begin
if(rx_data==8'hFE)
begin
rx_start<=0;
state<=0;
wave1<=data1;
wave1_freq<=data2;
wave2<=data3;
wave2_freq<=data4;
end
else
begin
rx_start<=0;
state<=0;
data1<=0;
data2<=0;
data3<=0;
data4<=0;
end
end
end
endcase
end
end
endmodule
第二部分(stm32接收数据进行FFT识别波形以及频率并发送)
1.stm32串口接收
stm32使用的是串口中断(使用DMA会更好)
HAL_UART_Receive_IT(&huart2,rx_data,2048); //打开串口中断
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
for(int i=0;i<2048;i++)
{
wave_form[i]=rx_data[i]; //接收后转存出来
}
HAL_UART_Receive_IT(&huart2,rx_data,2048);
}
}
2.stm32进行FFT
stm32导入DSP库并使用FFT的方法在我之前的文章已经介绍过了
https://blog.csdn.net/2401_83129457/article/details/147152935?spm=1001.2014.3001.5501
接下来我就直接介绍如何通过频域来识别两种波形以及他们的频率
首先确定波的频率范围为20KHz-100KHz,步进为5KHz,所以我们可以只考虑20KHz-100KHz这个频率范围之间的能量。并且以5KHz的步进将能量聚集起来,得到能量最多的两个频段就是所需要的两个波形的频率,然后通过一个阈值比较,判断两个波的波形,较小的是三角波,较大的是正弦波
for (int i=35; i<1000;i++) //将5KHz以内的能量聚集起来
{
freq = (int)(((float)i*1000000/2048/5000)+0.5);
if(freq==freq_last)
{
v_max = FFT_output[i]*FFT_output[i]+v_max;
}
else
{
v_max = sqrt(v_max);
freq_change[cnt] = v_max;
fre[cnt] = freq;
freq_last=freq;
cnt++;
}
}
v_max=0;
cnt=0;
for(int i=0;i<100;i++) //计算出两个最大的能量的频率
{
if(freq_change[i]>max[0])
{
max[1]=max[0];
idx[1]=idx[0];
max[0]=freq_change[i];
idx[0]=2.5*(fre[i]-1);
}
else if(freq_change[i]>max[1] && freq_change[i]<max[0])
{
max[1]=freq_change[i];
idx[1]=2.5*(fre[i]-1);
}
}
if(max[0]>5.7) wave[0] = 0; //正弦波
else wave[0] = 1; //三角波
if(max[1]>5.7) wave[1] = 0;
else wave[1] = 1;
以下是stm32所有的代码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "key.h"
#include "OLED.h"
#include "LED.h"
#include "math.h"
#include "arm_math.h"
#include "arm_const_structs.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
float max[2];
uint16_t idx[2];
uint16_t freq,freq_last;
float FFT_input[4096],FFT_output[2048];
float freq_change[2048];
uint16_t fre[2048];
uint16_t cnt;
float v_max;
uint8_t wave[2];
uint8_t tx_data[6];
uint8_t flag;
uint8_t rx_data[2048];
uint8_t wave_form[2048];
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
char message[50];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_TIM2_Init();
MX_TIM4_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2,rx_data,2048);
OLED_Init();
OLED_PrintString(0,0,"Init Success",&font16x16,OLED_COLOR_NORMAL);
OLED_ShowFrame();
HAL_Delay(500);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
for (int i = 0; i < 2048; i++)
{
FFT_input[i * 2] = wave_form[i];
FFT_input[i * 2 + 1] = 0;
}
arm_cfft_f32(&arm_cfft_sR_f32_len2048, FFT_input, 0, 1);
arm_cmplx_mag_f32(FFT_input, FFT_output, 2048);
FFT_output[0] /= 2048;
for (int i = 1; i < 2048; i++)
{
FFT_output[i] /= 1024;
}
for (int i=35; i<1000;i++)
{
freq = (int)(((float)i*1000000/2048/5000)+0.5);
if(freq==freq_last)
{
v_max = FFT_output[i]*FFT_output[i]+v_max;
}
else
{
v_max = sqrt(v_max);
freq_change[cnt] = v_max;
fre[cnt] = freq;
freq_last=freq;
cnt++;
}
}
v_max=0;
cnt=0;
for (int i = 0; i < 25; i++)
{
printf("%d,%.2f\n",fre[i],freq_change[i]);
}
for(int i=0;i<100;i++)
{
if(freq_change[i]>max[0])
{
max[1]=max[0];
idx[1]=idx[0];
max[0]=freq_change[i];
idx[0]=2.5*(fre[i]-1);
}
else if(freq_change[i]>max[1] && freq_change[i]<max[0])
{
max[1]=freq_change[i];
idx[1]=2.5*(fre[i]-1);
}
}
if(max[0]>5.7) wave[0] = 0; //正弦波
else wave[0] = 1; //三角波
if(max[1]>5.7) wave[1] = 0;
else wave[1] = 1;
if(idx[0]>idx[1])
{
tx_data[0]=0xff;
tx_data[1]=wave[1];
tx_data[2]=idx[1];
tx_data[3]=wave[0];
tx_data[4]=idx[0];
tx_data[5]=0xfe;
}
else
{
tx_data[0]=0xff;
tx_data[1]=wave[0];
tx_data[2]=idx[0];
tx_data[3]=wave[1];
tx_data[4]=idx[1];
tx_data[5]=0xfe;
}
HAL_UART_Transmit(&huart2,tx_data,6,HAL_MAX_DELAY);
OLED_NewFrame();
sprintf(message,"%d : %d %.2f",wave[0],idx[0],max[0]);
OLED_PrintString(0,0,message,&font16x16,OLED_COLOR_NORMAL);
sprintf(message,"%d : %d %.2f",wave[1],idx[1],max[1]);
OLED_PrintString(0,20,message,&font16x16,OLED_COLOR_NORMAL);
OLED_ShowFrame();
for(uint8_t i=0;i<2;i++)
{
max[i]=0;
idx[i]=0;
wave[i]=0;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 6;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
for(int i=0;i<2048;i++)
{
wave_form[i]=rx_data[i];
}
HAL_UART_Receive_IT(&huart2,rx_data,2048);
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
3.stm32串口发送
由于A波的频率始终小于B波,所以我们要先判断那个是A波哪个是B波,然后通过串口发送给FPGA
if(idx[0]>idx[1])
{
x_data[0]=0xff;
tx_data[1]=wave[1];
tx_data[2]=idx[1];
tx_data[3]=wave[0];
tx_data[4]=idx[0];
tx_data[5]=0xfe;
}
else
{
tx_data[0]=0xff;
tx_data[1]=wave[0];
tx_data[2]=idx[0];
tx_data[3]=wave[1];
tx_data[4]=idx[1];
tx_data[5]=0xfe;
}
HAL_UART_Transmit(&huart2,tx_data,6,HAL_MAX_DELAY);
第三部分(FPGA得到波形与频率后生成波形)
需要一个DDS模块,使用ROM存储正弦波和三角波的点
module dds(
input clk,
input rst,
input wave1, //A波波形
input wave2, //B波波形
input [31:0] freq1, //A波频率
input [31:0] freq2, //B波频率
input [9:0] phase1, //A波相位
input [9:0] phase2, //B波相位
input [9:0] de_phase, //相位偏移
output [13:0]dataout1, //A'波
output [13:0]dataout2, //B'波
output [13:0]dataout2_delay //B'波偏移
);
//波形数据:
reg [13:0] wavedata1,wavedata2,wavedata3;
//波形信号:
wire [13:0] sindata1,sindata2,sindata3;
wire [13:0] tridata1,tridata2,tridata3;
//相位寄存器:
reg [31:0]frechange1,frechange2;
always @(posedge clk or negedge rst) begin
if(!rst)
begin
frechange1 <= 32'd0;
frechange2 <= 32'd0;
end
else
begin
frechange1 <= frechange1 + freq1;
frechange2 <= frechange2 + freq2;
end
end
//相位累加器:
reg [9:0]romaddr1,romaddr2,romaddr3;
always @(posedge clk or negedge rst) begin
if(!rst)
begin
romaddr1 <= 10'd0;
romaddr2 <= 10'd0;
end
else
begin
romaddr1 <= frechange1[31:22] + phase1;
romaddr2 <= frechange2[31:22] + phase2;
romaddr3 <= romaddr2 + de_phase;
end
end
ROM_sin romsin1 (
.clka(clk),
.addra(romaddr1),
.douta(sindata1)
);
ROM_tri romtri1 (
.clka(clk),
.addra(romaddr1),
.douta(tridata1)
);
ROM_sin romsin2 (
.clka(clk),
.addra(romaddr2),
.douta(sindata2)
);
ROM_tri romtri2 (
.clka(clk),
.addra(romaddr2),
.douta(tridata2)
);
ROM_sin romsin3 (
.clka(clk),
.addra(romaddr3),
.douta(sindata3)
);
ROM_tri romtri3 (
.clka(clk),
.addra(romaddr3),
.douta(tridata3)
);
always @(*) begin
case(wave1)
1'b0: wavedata1<= sindata1;
1'b1: wavedata1<= tridata1;
default: wavedata1<= sindata1;
endcase
end
always @(*) begin
case(wave2)
1'b0: wavedata2<= sindata2;
1'b1: wavedata2<= tridata2;
default: wavedata2<= sindata2;
endcase
end
always @(*) begin
case(wave2)
1'b0: wavedata3<= sindata3;
1'b1: wavedata3<= tridata3;
default: wavedata3<= sindata3;
endcase
end
assign dataout1 = wavedata1;
assign dataout2 = wavedata2;
assign dataout2_delay = wavedata3;
endmodule
dds ddsA(
.clk(clk),
.rst(rst),
.wave1(wave1),
.wave2(wave2),
.freq1(freq1*32'd85899),
.freq2(freq2*32'd85899),
.phase1(A_phase[21:12]),
.phase2(B_phase[21:12]),
.de_phase((((phase0+phase)*91)>>5)),
.dataout1(da1_data),
.dataout2(da2_data_start),
.dataout2_delay(da2_data)
);
第四部分(FPGA锁相)
1.鉴相
对AB两路信号与输入的C信号进行鉴相处理(即相乘再过一个低通滤波器,乘法器和低通滤波器都是使用的VIVADO的ip核)
mult_mix mult_mix_A (
.CLK(clk), // input wire CLK
.A(ADC_signed), // input wire [11 : 0] A
.B(DAC_A), // input wire [13 : 0] B
.P(mix_A) // output wire [25 : 0] P
);
LPF LPF_A (
.aclk(clk), // input wire aclk
.s_axis_data_tvalid(1'b1), // input wire s_axis_data_tvalid
.s_axis_data_tdata(A_filter), // input wire [23 : 0] s_axis_data_tdata
.m_axis_data_tdata(A_filter_out) // output wire [23 : 0] m_axis_data_tdata
);
mult_mix mult_mix_B (
.CLK(clk), // input wire CLK
.A(ADC_signed), // input wire [11 : 0] A
.B(DAC_B), // input wire [13 : 0] B
.P(mix_B) // output wire [25 : 0] P
);
LPF LPF_B (
.aclk(clk), // input wire aclk
.s_axis_data_tvalid(1'b1), // input wire s_axis_data_tvalid
.s_axis_data_tdata(B_filter), // input wire [23 : 0] s_axis_data_tdata
.m_axis_data_tdata(B_filter_out) // output wire [23 : 0] m_axis_data_tdata
);
2.环路滤波
环路滤波器就相当于是一个PI电路(即差距和积分电路,只需要调节它的Kp和Ki参数使得波形不抖动就算调节成功)
Loop_filter Loop_filter_A(
.clk(clk),
.rst(rst),
.i_pd(A_filter_out),
.o_frequency_df(A_phase)
);
Loop_filter Loop_filter_B(
.clk(clk),
.rst(rst),
.i_pd(B_filter_out),
.o_frequency_df(B_phase)
);
module Loop_filter(
input clk,
input rst,
input signed [23:0] i_pd,
output signed [23:0] o_frequency_df
);
reg signed [23:0] sum_d;
wire signed [23:0] pd_c2, pd_c1,sum;
assign pd_c1 = {{3{i_pd[23]}},i_pd[23:3]}; //c1
assign pd_c2 = {{12{i_pd[23]}},i_pd[23:12]}; //c2
always@(posedge clk or negedge rst)
begin
if(!rst)
sum_d <= 0;
else
sum_d <= sum;
end
assign sum = pd_c2 + sum_d;
assign o_frequency_df = sum_d + pd_c1;
endmodule
3.反馈
将输出的phase信号反馈到DDS模块上
.phase1(A_phase[21:12]),
.phase2(B_phase[21:12]),
第五部分(DAC输出)
.dataout1(da1_data),
.dataout2_delay(da2_data)
assign da1_clk = clk;
assign da1_wrt = clk;
assign da2_clk = clk;
assign da2_wrt = clk;
第六部分(移相)
通过前五个部分已经可以实现除开移相的所有功能
接下来我们要使用按键来控制A'和B‘的相位差并且能在数码管上显示
1.按键消抖
module key
(
input clk,
input reset,
input [3:0] key,
output [3:0] key_num
);
reg[31:0] timer;
reg[3:0] key_first;
reg[3:0] key_second;
reg[3:0] key_now;
assign key_num=key_now;
always@(posedge clk or negedge reset)
begin
if(reset==0)
begin
key_now<=4'b0000;
key_first<=4'b1111;
key_second<=4'b1111;
timer<=32'd0;
end
else
begin
if(key!=4'b1111)
begin
key_first<=key;
timer<=timer+32'd1;
end
else
begin
timer<=32'd0;
if(key_first==key_second)
begin
key_now<=~key_first;
key_second<=0;
end
else
key_now<=0;
end
if(timer==32'd999_999)
key_second<=key;
end
end
endmodule
2.按键设置相位差
需要用前三个按键将相位手动调零,然后按下第四个按键表示调零完毕,再通过前三个按键设置相位差
key key_inst
(
.clk(clk),
.reset(rst),
.key(key),
.key_num(key_num)
);
always@(posedge clk or negedge rst)
begin
if(!rst)
begin
phase<=0;
phase0<=0;
end
else
begin
if(key_num == 4'b0001)
begin
phase<=phase+1;
end
else if(key_num == 4'b0010)
begin
phase<=phase+5;
end
else if(key_num == 4'b0100)
begin
phase<=phase+30;
end
else if(key_num == 4'b1000)
begin
phase0<=phase;
phase<=0;
end
if(phase0 == 0)
begin
if(phase>360)
phase<=phase-360;
end
else
begin
if(phase>180)
phase<=phase-180;
end
end
end
3.数码管显示相位
module nixie(
input clk,
input rst,
input [7:0] data,
output [7:0] SMG_Data,
output [5:0] Scan_Sig
);
reg [5:0] sig;
assign Scan_Sig = sig;
reg [7:0] num;
reg [31:0] timer;
reg clk_low;
reg [9:0] count;
always@(posedge clk or negedge rst)
begin
if(!rst)
begin
count<=0;
clk_low<=0;
end
else
begin
count<=count+1;
if(count==49)
begin
count<=0;
clk_low <= ~clk_low;
end
end
end
always@(posedge clk_low or negedge rst)
begin
if(!rst)
begin
num<=8'd0;
timer<=0;
end
else
begin
timer<=timer+1;
if(timer==50)
begin
sig=6'b011111;
num = 8'h11;
end
else if(timer==100)
begin
sig=6'b101111;
num = 8'h11;
end
else if(timer==150)
begin
sig=6'b110111;
num = 8'h11;
end
else if(timer==200)
begin
sig=6'b111011;
num = 8'h11;
end
else if(timer==250)
begin
sig=6'b111101;
num = 8'h11;
end
else if(timer==300)
begin
sig=6'b111110;
num = 8'h11;
timer<=0;
end
if(sig == 6'b011111)
begin
num <= ~(8'h73);
end
else if(sig == 6'b101111)
begin
num <= ~(8'h74);
end
else if(sig == 6'b110111)
begin
num <= ~(8'h40);
end
else if(sig == 6'b111011)
begin
case(data%1000/100)
1:
num<=~(8'h06);
2:
num<=~(8'h5b);
3:
num<=~(8'h4f);
4:
num<=~(8'h66);
5:
num<=~(8'h6d);
6:
num<=~(8'h7d);
7:
num<=~(8'h07);
8:
num<=~(8'h7f);
9:
num<=~(8'h6f);
0:
num<=~(8'h3f);
endcase
end
else if(sig == 6'b111101)
begin
case(data%100/10)
1:
num<=~(8'h06);
2:
num<=~(8'h5b);
3:
num<=~(8'h4f);
4:
num<=~(8'h66);
5:
num<=~(8'h6d);
6:
num<=~(8'h7d);
7:
num<=~(8'h07);
8:
num<=~(8'h7f);
9:
num<=~(8'h6f);
0:
num<=~(8'h3f);
endcase
end
else if(sig == 6'b111110)
begin
case(data%10)
1:
num<=~(8'h06);
2:
num<=~(8'h5b);
3:
num<=~(8'h4f);
4:
num<=~(8'h66);
5:
num<=~(8'h6d);
6:
num<=~(8'h7d);
7:
num<=~(8'h07);
8:
num<=~(8'h7f);
9:
num<=~(8'h6f);
0:
num<=~(8'h3f);
endcase
end
end
end
assign SMG_Data = num;
endmodule
nixie nixie_inst(
.clk(clk),
.rst(rst),
.data(phase),
.SMG_Data(SMG_Data),
.Scan_Sig(Scan_Sig)
);
第七部分(FPGA代码总结)
以下是top模块的所有代码
module top(
input clk,
input rst,
input [11:0] ad9238_data_ch0,
input rx_pin,
input [3:0] key,
output [7:0] SMG_Data,
output [5:0] Scan_Sig,
output tx_pin,
output ad9238_clk_ch0,
output da1_clk,
output da1_wrt,
output [13:0] da1_data,
output da2_clk,
output da2_wrt,
output [13:0] da2_data
);
reg [7:0] phase,phase0;
wire [13:0] da2_data_start;
wire signed [11:0] ADC_signed = ad9238_data_ch0 - 12'd2047;
wire signed [13:0] DAC_A = da1_data - 14'd8191;
wire signed [13:0] DAC_B = da2_data_start - 14'd8191;
wire signed [25:0] mix_A,mix_B;
wire signed [23:0] A_filter = mix_A>>2,B_filter = mix_B>>2;
wire signed [23:0] A_filter_out,B_filter_out,A_phase,B_phase;
reg clk_1M_reg;
reg [7:0] clk_1M_cnt;
wire clk_1M = clk_1M_reg;
wire wave1,wave2;
wire [7:0] freq1,freq2;
wire [7:0] ADC_send = ad9238_data_ch0>>4;
wire [2:0] N = (freq1&&freq2) ? (freq2/freq1) : 1;
always@(posedge clk or negedge rst)
begin
if(!rst)
begin
clk_1M_cnt<=0;
clk_1M_reg<=0;
end
else
begin
clk_1M_cnt<=clk_1M_cnt+1;
if(clk_1M_cnt==24)
begin
clk_1M_cnt<=0;
clk_1M_reg<=~clk_1M_reg;
end
end
end
assign ad9238_clk_ch0 = clk_1M;
assign da1_clk = clk;
assign da1_wrt = clk;
assign da2_clk = clk;
assign da2_wrt = clk;
stm32_communication stm32_communication(
.clk(clk),
.rst(rst),
.clk_1M(clk_1M),
.ADC(ADC_send),
.rx_pin(rx_pin),
.wave1(wave1),
.wave1_freq(freq1),
.wave2(wave2),
.wave2_freq(freq2),
.tx_pin(tx_pin)
);
dds ddsA(
.clk(clk),
.rst(rst),
.wave1(wave1),
.wave2(wave2),
.freq1(freq1*32'd85899),
.freq2(freq2*32'd85899),
.phase1(A_phase[21:12]),
.phase2(B_phase[21:12]),
.de_phase((((phase0+phase)*91)>>5)),
.dataout1(da1_data),
.dataout2(da2_data_start),
.dataout2_delay(da2_data)
);
mult_mix mult_mix_A (
.CLK(clk), // input wire CLK
.A(ADC_signed), // input wire [11 : 0] A
.B(DAC_A), // input wire [13 : 0] B
.P(mix_A) // output wire [25 : 0] P
);
LPF LPF_A (
.aclk(clk), // input wire aclk
.s_axis_data_tvalid(1'b1), // input wire s_axis_data_tvalid
.s_axis_data_tdata(A_filter), // input wire [23 : 0] s_axis_data_tdata
.m_axis_data_tdata(A_filter_out) // output wire [23 : 0] m_axis_data_tdata
);
Loop_filter Loop_filter_A(
.clk(clk),
.rst(rst),
.i_pd(A_filter_out),
.o_frequency_df(A_phase)
);
mult_mix mult_mix_B (
.CLK(clk), // input wire CLK
.A(ADC_signed), // input wire [11 : 0] A
.B(DAC_B), // input wire [13 : 0] B
.P(mix_B) // output wire [25 : 0] P
);
LPF LPF_B (
.aclk(clk), // input wire aclk
.s_axis_data_tvalid(1'b1), // input wire s_axis_data_tvalid
.s_axis_data_tdata(B_filter), // input wire [23 : 0] s_axis_data_tdata
.m_axis_data_tdata(B_filter_out) // output wire [23 : 0] m_axis_data_tdata
);
Loop_filter Loop_filter_B(
.clk(clk),
.rst(rst),
.i_pd(B_filter_out),
.o_frequency_df(B_phase)
);
wire [3:0] key_num;
key key_inst
(
.clk(clk),
.reset(rst),
.key(key),
.key_num(key_num)
);
nixie nixie_inst(
.clk(clk),
.rst(rst),
.data(phase),
.SMG_Data(SMG_Data),
.Scan_Sig(Scan_Sig)
);
always@(posedge clk or negedge rst)
begin
if(!rst)
begin
phase<=0;
phase0<=0;
end
else
begin
if(key_num == 4'b0001)
begin
phase<=phase+1;
end
else if(key_num == 4'b0010)
begin
phase<=phase+5;
end
else if(key_num == 4'b0100)
begin
phase<=phase+30;
end
else if(key_num == 4'b1000)
begin
phase0<=phase;
phase<=0;
end
if(phase0 == 0)
begin
if(phase>360)
phase<=phase-360;
end
else
begin
if(phase>180)
phase<=phase-180;
end
end
end
//ila ila (
// .clk(clk), // input wire clk
// .probe0(mix_B), // input wire [25:0] probe0
// .probe1(B_filter), // input wire [23:0] probe1
// .probe2(B_filter_out), // input wire [23:0] probe2
// .probe3(B_phase[23:15]) // input wire [8:0] probe3
//);
endmodule
后记
完整代码:
更多推荐
所有评论(0)