2023 电赛 H 题信号分离装置 FPGA+STM32 解法
介绍 2023 年电赛 H 题信号分离装置的 FPGA 与 STM32 联合设计方案。系统通过高速 ADC 采集混合信号 C,经 FPGA 传输至 STM32 进行 FFT 分析以识别波形类型及频率,再反馈回 FPGA 利用 DDS 重构波形并采用锁相环消除相位漂移,最终通过 DAC 输出分离后的 A'和 B'信号。支持按键移相及数码管显示,实现了信号的实时处理与同步输出。

介绍 2023 年电赛 H 题信号分离装置的 FPGA 与 STM32 联合设计方案。系统通过高速 ADC 采集混合信号 C,经 FPGA 传输至 STM32 进行 FFT 分析以识别波形类型及频率,再反馈回 FPGA 利用 DDS 重构波形并采用锁相环消除相位漂移,最终通过 DAC 输出分离后的 A'和 B'信号。支持按键移相及数码管显示,实现了信号的实时处理与同步输出。

本方案除要求一使用的增益为一的加法器外,其余均由 FPGA+STM32 实现。

一种方法是对 C 进行傅里叶变换然后识别出两路波形分别是什么然后将频域分离出来随后进行傅里叶逆变换再输出就行。另一种方法是本文采用的方案,由于题目不考虑波形的相位和峰峰值与原波形的关系,所以我们可以将输入的 C 信号进行傅里叶变换识别出两路波形的频率。并且由于 A、B 波峰峰值固定为 1V,而正弦波为单音信号,频谱集中,三角波存在三次谐波,导致基波幅值被削弱,使得可以通过比较频谱中基波的幅值大小来识别输入的波形是三角波还是正弦波。然后就能得到两路波的波形以及频率,随后就可以使用 DDS 重构出 A'和 B'波形。但此方法存在一个问题,重构波形与信号发生器波形时钟不同步,由于不同时钟的微小差异,会使得相位差在不停的累积就会产生信号漂移,所以我们需要设计一个锁相环将生成的信号与原信号的相位锁住就不会再产生漂移了,移相只需要改变 ROM 表的地址就可以实现。
C 信号——>高速 ADC——>FPGA——>FIFO——>串口——>stm32——>FFT——>识别波形和频率——>串口——>DDS——>锁相环——>高速 DAC——>A',B'
此处使用的 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_valid;
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_valid<=1; else tx_valid<=0; end end
assign rd_en = (next_read_state == R_FIFO) ? 1'b1 : 1'b0;
fifo fifo ( .rst(~rst), .wr_clk(clk_1M), .rd_clk(clk_100K), .din(w_data), .wr_en(wr_en), .rd_en(rd_en), .dout(tx_data), .full(full), .empty(empty), .rd_data_count(rd_data_count), .wr_data_count(wr_data_count) );
此处串口发送基于 AX7035B 例程(在 FIFO 中每读一个数就通过串口发送出去)
uart_tx#( .CLK_FRE(50), .BAUD_RATE(115200) )uart_tx( .clk(clk), .rst_n(rst), .tx_data(tx_data), .tx_data_valid(tx_valid), .tx_pin(tx_pin) );
module uart_tx #( parameter CLK_FRE = 50, parameter BAUD_RATE = 115200 ) ( input clk, input rst_n, input[7:0] tx_data, input tx_data_valid, output reg tx_data_ready, output tx_pin ); localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE; localparam S_IDLE = 1; localparam S_START = 2; localparam S_SEND_BYTE = 3; localparam S_STOP = 4; reg[2:0] state; reg[2:0] next_state; reg[15:0] cycle_cnt; reg[2:0] bit_cnt; reg[7:0] tx_data_latch; reg tx_reg; 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
此处串口接收基于 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), .BAUD_RATE(115200) )uart_rx ( .clk(clk), .rst_n(rst), .rx_data(rx_data), .rx_data_ready(rx_start), .rx_data_valid(rx_done), .rx_pin(rx_pin) );
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, parameter BAUD_RATE = 115200 ) ( input clk, input rst_n, output reg[7:0] rx_data, output reg rx_data_valid, input rx_data_ready, input rx_pin ); localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE; localparam S_IDLE = 1; localparam S_START = 2; localparam S_REC_BYTE = 3; localparam S_STOP = 4; localparam S_DATA = 5; reg[2:0] state; reg[2:0] next_state; reg rx_d0; reg rx_d1; wire rx_negedge; reg[7:0] rx_bits; reg[15:0] cycle_cnt; reg[2:0] bit_cnt; 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) next_state <= S_REC_BYTE; else next_state <= S_START; S_REC_BYTE: if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7) next_state <= S_STOP; else next_state <= S_REC_BYTE; S_STOP: if(cycle_cnt == CYCLE/2 - 1) next_state <= S_DATA; else next_state <= S_STOP; S_DATA: if(rx_data_ready) 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; 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 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
这是与 stm32 通信模块的所有代码
module stm32_communication( input clk, input rst, input clk_1M, 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; 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_valid; 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) next_write_state <= W_FIFO; else next_write_state <= W_IDLE; W_FIFO: if(full == 1'b1) 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) next_read_state <= R_FIFO; else next_read_state <= R_IDLE; R_FIFO: if(empty == 1'b1) 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_valid<=1; else tx_valid<=0; end end assign rd_en = (next_read_state == R_FIFO) ? 1'b1 : 1'b0; fifo fifo ( .rst(~rst), .wr_clk(clk_1M), .rd_clk(clk_100K), .din(w_data), .wr_en(wr_en), .rd_en(rd_en), .dout(tx_data), .full(full), .empty(empty), .rd_data_count(rd_data_count), .wr_data_count(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), .BAUD_RATE(115200) )uart_rx ( .clk(clk), .rst_n(rst), .rx_data(rx_data), .rx_data_ready(rx_start), .rx_data_valid(rx_done), .rx_pin(rx_pin) ); uart_tx#( .CLK_FRE(50), .BAUD_RATE(115200) )uart_tx( .clk(clk), .rst_n(rst), .tx_data(tx_data), .tx_data_valid(tx_valid), .tx_pin(tx_pin) ); 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 使用的是串口中断(建议 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); } }
stm32 导入 DSP 库并使用 FFT 的方法已在前文介绍。接下来我就直接介绍如何通过频域来识别两种波形以及他们的频率。首先确定波的频率范围为 20KHz-100KHz,步进为 5KHz,所以我们可以只考虑 20KHz-100KHz 这个频率范围之间的能量。并且以 5KHz 的步进将能量聚集起来,得到能量最多的两个频段就是所需要的两个波形的频率,然后通过一个阈值比较,判断两个波的波形,较小的是三角波,较大的是正弦波
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<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 核心代码
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "key.h"
#include "OLED.h"
#include "LED.h"
#include "math.h"
#include "arm_math.h"
#include "arm_const_structs.h"
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];
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init(); MX_I2C1_Init(); MX_TIM2_Init(); MX_TIM4_Init(); MX_TIM3_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init();
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);
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<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; }
}
}
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
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(); }
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(); }
}
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); }
}
void Error_Handler(void) {
__disable_irq(); while (1) { }
}
由于 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);
需要一个 DDS 模块,使用 ROM 存储正弦波和三角波的点
module dds( input clk, input rst, input wave1, input wave2, input [31:0] freq1, input [31:0] freq2, input [9:0] phase1, input [9:0] phase2, input [9:0] de_phase, output [13:0]dataout1, output [13:0]dataout2, output [13:0]dataout2_delay ); 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) );
对 AB 两路信号与输入的 C 信号进行鉴相处理(即相乘再过一个低通滤波器,乘法器和低通滤波器都是使用的 VIVADO 的 ip 核)
mult_mix mult_mix_A ( .CLK(clk), .A(ADC_signed), .B(DAC_A), .P(mix_A) ); LPF LPF_A ( .aclk(clk), .s_axis_data_tvalid(1'b1), .s_axis_data_tdata(A_filter), .m_axis_data_tdata(A_filter_out) ); mult_mix mult_mix_B ( .CLK(clk), .A(ADC_signed), .B(DAC_B), .P(mix_B) ); LPF LPF_B ( .aclk(clk), .s_axis_data_tvalid(1'b1), .s_axis_data_tdata(B_filter), .m_axis_data_tdata(B_filter_out) );
环路滤波器就相当于是一个 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]}; assign pd_c2 = {{12{i_pd[23]}},i_pd[23:12]}; 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
将输出的 phase 信号反馈到 DDS 模块上
.phase1(A_phase[21:12]), .phase2(B_phase[21:12]),
.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'的相位差并且能在数码管上显示
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
需要用前三个按键将相位手动调零,然后按下第四个按键表示调零完毕,再通过前三个按键设置相位差
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
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) );
以下是 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), .A(ADC_signed), .B(DAC_A), .P(mix_A) ); LPF LPF_A ( .aclk(clk), .s_axis_data_tvalid(1'b1), .s_axis_data_tdata(A_filter), .m_axis_data_tdata(A_filter_out) ); 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), .A(ADC_signed), .B(DAC_B), .P(mix_B) ); LPF LPF_B ( .aclk(clk), .s_axis_data_tvalid(1'b1), .s_axis_data_tdata(B_filter), .m_axis_data_tdata(B_filter_out) ); 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 endmodule

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online