自学fpga,练习一年半,终于从一名车间操作工转行到fpga工程师。入职后领导鉴于我的技术水平(毕竟练习时长不到两年半),所以安排了他口中的小任务——电机信号采样并输出电机转速,任务不难,但还是慌了,下面是我的项目历程。
一、任务分析。
电机的驱动脉冲是一对频率相同的周期性方波,这对周期性方波在相位上相差90度,根据A、B的相位情况判断转动方向,项目使用的电机速率是4000个脉冲一转。现在要输出当前电机的转速、方向,只需对A、B信号采样计数即可。
这样一个小项目,本来没啥可说的,只是一点涉及到抖动的过滤,实际上在信号传输中会产生很多噪声,这些都干扰了信号的采集,所以首先得对输入的信号滤波。
二、代码实现
首先对接收到的信号进行滤波,这里设置了一个阈值,即如果方波的带宽少于5us则视为杂波脉冲,就要将此杂波脉冲屏蔽,然后将滤波后的波形描绘出来。描绘出波形后,对此波形进行边沿计数(这里采取对上升沿计数),计数4000次。在开始对所描绘的波形上升沿计数的时候,同时开始对系统时钟的周期计数(系统时钟周期已知,这里按照开发板为20ns)。由获得4000个采样波形周期的总时间,确定电机的转动周期。
具体代码与注释如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/07/17 17:11:41
// Design Name:
// Module Name: get_signal
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module get_signal(
input sys_clock,
input rst_n,
input signalA,
input signalB
);
//参数定义
parameter SYSCLK_TIME=20; //系统时钟的周期
parameter DELAY_TIME=26'd25_000; //上电后延时一段时间
parameter NUMBER_pluse=4000;
//上电延时
reg [25:0]cnt_delay=0;//延时计数器,
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)
cnt_delay<=0;
else if (cnt_delay>= DELAY_TIME) //延时时间有待确认,这里设置成0.5ms,仿真时候可以简化
cnt_delay<=cnt_delay;
else
cnt_delay<=cnt_delay+1;
end
//将两个信号打两拍寄存
reg r0_signalA;
reg r1_signalA;
reg r1_signalB;
reg r0_signalB;
always@(posedge sys_clock )
begin //思考:这时候打拍子,在复位时,寄存器需要清零吗
r0_signalA<=signalA;
r1_signalA<=r0_signalA;
end
always@(posedge sys_clock )
begin
r0_signalB<=signalB;
r1_signalB<=r0_signalB;
end
//获得输入信号的上升沿(使用assign还是使用always块?根据testbench可以看出差别来)
reg raise_A; //A上升沿
reg raise_B; //B上升沿
reg fall_A; //A下降沿
reg fall_B; //B下降沿
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)
raise_A<=0;
else if ({r1_signalA,r0_signalA}== 2'b01)
raise_A<=1;
else
raise_A<=0;
end
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)
raise_B<=0;
else if ({r1_signalB,r0_signalB}== 2'b01)
raise_B<=1;
else
raise_B<=0;
end
//获得两输入信号的下降沿
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)
fall_A<=0;
else if ({r1_signalA,r0_signalA}== 2'b10)
fall_A<=1;
else
fall_A<=0;
end
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)
fall_B<=0;
else if ({r1_signalB,r0_signalB}== 2'b10)
fall_B<=1;
else
fall_B<=0;
end
/***************************** 第一步:先把滤波后的信号描绘出来*****************************/
//signalA
reg [15:0]cnt;
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)
cnt<=0;
else if (cnt_delay>= DELAY_TIME) begin
if (raise_A||fall_A)
cnt<=0;
else
cnt<=cnt+1;
end
else
cnt<=cnt;
end
reg signalA_sample;
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)
signalA_sample<=0;
else if (cnt==250)
signalA_sample<=signalA;
else
signalA_sample<=signalA_sample;
end
//signalB
reg [15:0]cntB;
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)
cntB<=0;
else if (cnt_delay>= DELAY_TIME) begin
if (raise_B||fall_B)
cntB<=0;
else
cntB<=cntB+1;
end
else
cntB<=cntB;
end
reg signalB_sample;
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)
signalB_sample<=0;
else if (cntB== 250)
signalB_sample<=signalB;
else
signalB_sample<=signalB_sample;
end
/*************************然后对滤波得到的信号脉冲计数,4000次,并得到总的时间**********************/
//signalA部分脉冲计数器
reg [15:0]cnt_sample_pulseA=0 ;
always@(posedge signalA_sample or negedge rst_n) begin
if (!rst_n)
cnt_sample_pulseA<=0;
else if (cnt_sample_pulseA==NUMBER_pluse+1)
cnt_sample_pulseA<=cnt_sample_pulseA;
else
cnt_sample_pulseA<=cnt_sample_pulseA+1;
end
//signalB部分脉冲计数器
reg [15:0]cnt_sample_pulseB=0 ;
always@(posedge signalB_sample or negedge rst_n) begin
if (!rst_n)
cnt_sample_pulseB<=0;
else if (cnt_sample_pulseB==NUMBER_pluse+1)
cnt_sample_pulseB<=cnt_sample_pulseB;
else
cnt_sample_pulseB<=cnt_sample_pulseB+1;
end
//触发计时计数器开始对系统时钟计时计数,直到出现4000个脉冲后不再计数,保持不变
reg[39:0]Motor_time; //电机转动一圈的总时间
reg[39:0]cnt1=0;
always@(posedge sys_clock or negedge rst_n) begin
if (!rst_n)begin
cnt1<=0;
Motor_time<=0;end
else if(cnt_sample_pulseA)begin //第一个有效脉冲出现后开始计数
if (cnt_sample_pulseA == NUMBER_pluse+1)begin //这里是PULSE_CNT 还是PULSE_CNT+1;
cnt1<=cnt1;
Motor_time<=(cnt1*SYSCLK_TIME);end //电机转动周期就是Motor_time乘以sys_clock的周期,乘法也可转化成移位(cnt1<<4+cnt1<<2)
else begin
cnt1<=cnt1+1;
Motor_time<=0;end
end
else
cnt1<=cnt1;
end
/**********************第三部分:比较两路信号的先后顺序*****************/
//延时完成后立即计时,分别到A、B上升沿来时停止计时
reg [20:0] cnt_Araise=0;
reg [20:0] cnt_Braise=0;
always@(posedge sys_clock or negedge rst_n)begin
if (!rst_n)
cnt_Araise<=0;
else if (cnt_delay>=DELAY_TIME)begin
if (cnt_sample_pulseA)
cnt_Araise<=cnt_Araise;
else
cnt_Araise<=cnt_Araise+1;end
else
cnt_Araise<=0;
end
always@(posedge sys_clock or negedge rst_n)begin
if (!rst_n)
cnt_Braise<=0;
else if (cnt_delay>=DELAY_TIME)begin
if (cnt_sample_pulseB)
cnt_Braise<=cnt_Braise;
else
cnt_Braise<=cnt_Braise+1;end
else
cnt_Braise<=0;
end
//最后比较信号信号顺序确定电机方向
wire Motor_direction;
assign Motor_direction=(cnt_sample_pulseA&cnt_sample_pulseB)?(!(cnt_Braise>cnt_Araise)):0; // 1代表正转,0代表反转
//探针
ila_0 your_instance_name (
.clk(sys_clock), // input wire clk
.probe0(signalA), // input wire [0:0] probe0
.probe1(signalB), // input wire [0:0] probe1
.probe2(Motor_direction), // input wire [0:0] probe2
.probe3(Motor_time) // input wire [39:0] probe3
);
endmodule
三、仿真
(1)这里给出功能仿真所用到的testbench如下。
这里主要产生了系统时钟,以及两路有90度相位差的带有毛刺的方波信号。
`timescale 1ns / 1ps
module test_bench( );
reg sys_clock;
reg rst_n;
reg signalA;
reg signalB;
get_signal get_signal(
.sys_clock(sys_clock),
.rst_n(rst_n),
.signalA(signalA),
.signalB(signalB)
);
initial sys_clock=0;
always #10 sys_clock=~sys_clock; //产生50M的系统时钟信号
initial signalA=0; //生成带毛刺噪声的10KHz信号
always
begin
#20000 signalA =~signalA;
#2000 signalA =~signalA;
#20000 signalA =~signalA;
#4000 signalA =~signalA;
#4000 signalA =~signalA;
#20000 signalA =~signalA;
#2000 signalA =~signalA;
#20000 signalA =~signalA;
#4000 signalA =~signalA;
#4000 signalA =~signalA;
end
initial begin
signalB=0;
# 25000
repeat(10000)
begin
#20000 signalB =~signalB;
#2000 signalB =~signalB;
#20000 signalB =~signalB;
#4000 signalB =~signalB;
#4000 signalB =~signalB;
#20000 signalB =~signalB;
#2000 signalB =~signalB;
#20000 signalB =~signalB;
#4000 signalB =~signalB;
#4000 signalB =~signalB;
end
end
initial begin
rst_n=1;
# 5000
rst_n=1;
# 10000000
$stop;
end
endmodule
(2)下面是仿真的结果,可以看出滤波效果达到预期,输出的电机转动周期也与预期相同。
今天的分享就这样,欢迎一起讨论进步哦。