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 endmodule 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.ZEEKLOG.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; endmodule3.反馈
将输出的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 后记
完整代码: