练习一年半,菜鸟fpga工程师入职后的第一个小项目——伺服电机驱动脉冲采样计数

自学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)下面是仿真的结果,可以看出滤波效果达到预期,输出的电机转动周期也与预期相同。

 

今天的分享就这样,欢迎一起讨论进步哦。

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值