007-可调脉冲数触发之FPGA实现(Zynq也可驱动,带启动停止及完成中断输出)

本文主要是介绍007-可调脉冲数触发之FPGA实现(Zynq也可驱动,带启动停止及完成中断输出),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 一、设计思路
  • 二、代码及仿真
    • 1.资源消耗
    • 2.具体代码
    • 3.仿真波形
  • 总结


前言

此代码是在做显微镜高速聚焦系统中自己写的步进电机电机驱动源码,为了达到最快的驱动速度,因此选用脉冲触发方式进行驱动。在电机驱动的过程中往往需要对脉冲进行使能,启动,配置好输出N个脉冲,设置电机转动的方向,发送脉冲的过程中发送急停信号,停止当前的脉冲输出以及脉冲输出完后反馈回来中断触发信号。经过实测代码能够满足步进电机的驱动需求,且能够在驱动完毕后反馈中断信号提示脉冲信号已经输出完毕。

此代码适用的地方主要在需要脉冲触发的应用场景,最终输出两个信号出去(输出脉冲和电机方向电平),若有需要可以把脉冲触发跟运动坐标系建立起来,内部加一个计数器做大致位置的驱动,但为了避免步进电机丢步带来的影响,建议还是加上闭环的编码器进行坐标系的建立,我采用的是张大头步进电机的闭环驱动,实测效果OK,120KHz全功率运行没问题,噪声较小。


一、设计思路

拟设定五路输入信号,三路输出信号以及两个全局信号加时钟复位信号

sys_clk:时钟信号(通常情况下100M左右)
sys_rst:复位信号(低电平复位)
motor_enable:此路脉冲输出使能信号(低电平失能,高电平使能)
motor_start:脉冲开始输出信号(默认低电平,拉高后脉冲开始输出)
motor_pulse:脉冲个数信号(根据需求赋值)
motor_stop:脉冲急停信号(默认低电平,拉高后脉冲停止输出)
motor_direct:脉冲方向信号(低电平代表一个方向,高电平代表另外一个方向)
motor_exti:完成脉冲输出后中断信号(持续时间可定)
motor_step:外接至脉冲输出引脚
motor_dir:外接至步进电机驱动器方向引脚

主要代码通过状态机实现,状态机采用一段式状态机。状态机分为三个状态,当motor_enable信号拉高了高电平时,代表这路脉冲驱动信号有效,可以进行脉冲的输出。

通过case语句判断当前的状态的阶段,若处于第一个状态(idle)就对启动信号的上升沿进行判断,我们会在motor_start信号输入进行后打一个拍,然后判断其上升沿,当上升沿信号拉高后,代表脉冲即将输出,满足这个条件后状态机状态跳往第二个状态。若满足不了这个状态则持续为先前的状态。

进入到one第二个状态后,外部调用一个always语句,判断当前的状态是否跟上一个状态不同,且当前的状态属于第二个状态。如果判断成功就拉高pulse_status信号,这个信号处于高电平时,脉冲的计数器cnt_pulse_us就开始计数,当计数到指定脉宽的计数值后,翻转输出电平。脉宽计数值通过全局变量PULSE_WIDTH设定,比如基频为100MHz,当PULSE_WIDTH为50时,脉冲的工作频率为100M/50/2为1MHz。每计数到PULSE_WIDTH-1后,now_pulse_num就会自加到我们设定的脉冲个数*2(因为涉及到电平的翻转),当到达这个设定脉冲数后,pulse_status就会拉低,cnt_pulse_us就不会再计数,同时now_pulse_num归零,motor_step引脚输出低电平。从第二个状态跳转到第三个状态的条件也是这个。

第三个状态只持续一个时钟周期,在这个时钟周期内将rst_status拉高,同时state的状态转换成idle状态。等待下一个启动信号的来临。rst_status是整体的复位信号,也是输出中断信号的触发信号。当复位信号为1的时候,motor_step信号拉低,中断信号motor_exti信号拉高,pulse_status信号拉低。当motor_exti信号被拉高后,cnt_exti_us信号就会不断计数,当计数到EXTI_WIDTH-1时,拉低motor_exti信号。相当于将motor_exti信号拉高了一段时间然后再拉低。在外部我们可以设置PULSE_WIDTH和EXTI_DIDTH信号,以求获得较好我们可控的脉冲频率和中断信号时长。

以上都是无干扰情况下的正常运作流程,如果motor_stop信号在脉冲输出的时候被拉高了,检测到上升沿时,我们将下一个状态设置为下一个状态,且将pulse_status、cnt_exti_us、motor_step、now_pulse_num拉低,motor_exti信号拉高,即可实现急停停止输出脉冲的功能。

二、代码及仿真

1.资源消耗

在这里插入图片描述

2.具体代码

代码如下

`timescale 1ns / 1ps
/************************************
小董出品,必属精品
2023/8/18
************************************/module motor_control#(parameter EXTI_WIDTH  = 50  ,parameter PULSE_WIDTH = 5000  
)
(input    wire            sys_clk     ,//时钟信号input    wire            sys_rst     ,//复位信号input    wire            motor_stop  ,//电机急停信号,停止脉冲输出input    wire            motor_start ,//触发输出脉冲信号,比如摄像头数据采集完毕input    wire            motor_enable,//电机使能信号,只有使能的时候才能运行input    wire   [15:0]   motor_pulse ,//单次行程总脉冲数input    wire            motor_direct,//单次行程方向output   reg             motor_exti  ,//电机完成单次行程后中断信号output   reg             motor_step  ,//实际输出脉冲引脚output   wire            motor_dir    //实际输出方向引脚);
localparam    idle   =   3'b001 ;
localparam    one    =   3'b010 ;
localparam    two    =   3'b100 ;
wire               start_pose   ;//上升沿触发信号
wire               stop_pose    ;//上升沿触发信号reg                motor_start_r;//motor_start信号打了一拍
reg                motor_stop_r ;//motor_stop信号打了一拍
reg                motor_enable_r;//motor_enable信号打了一拍reg                rst_status   ;//复位标志位reg     [2:0]      state        ;//当前状态
reg     [2:0]      state_r      ;//上一次状态reg     [15:0]     now_pulse_num;//当前脉冲数
reg                pulse_status ;//脉冲输出开始标志位reg     [15:0]     cnt_pulse_us ;//脉冲一半周期定时计数,根据基频来
reg     [15:0]     cnt_exti_us  ;//中断信号定时计数,根据基频来//motor_start打拍
always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)motor_start_r <= 1'b0;elsemotor_start_r <= motor_start;//motor_enable打拍
always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)motor_enable_r <= 1'b0;elsemotor_enable_r <= motor_enable;//motor_stop打拍
always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)motor_stop_r   <= 1'b0;elsemotor_stop_r   <= motor_stop;//motor_start信号上升沿判断		
assign start_pose = ~motor_start_r&&motor_start;	
assign stop_pose  = ~motor_stop_r&&motor_stop;
//state打拍	
always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)state_r <= 'd0;elsestate_r <= state;	//状态机逻辑	
always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)beginstate        <= idle;rst_status   <= 1'b0;endelse if(stop_pose == 1'b1)beginstate        <= idle;rst_status   <= 1'b0;		endelse if(motor_enable_r==1'b1)case(state)idle:	if(start_pose)beginstate 	  <= one;end		elsebeginrst_status<= 1'b0;state 	  <= state;end	one :  if(now_pulse_num == 2*motor_pulse)beginstate 	  <= two;endelse beginstate 	  <= state;end		two :  beginrst_status<= 1'b1;state 	  <= idle;end			default:;endcase				elsebeginstate <= idle;end	always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)pulse_status <= 1'b0;else if(stop_pose==1'b1)pulse_status <= 1'b0;else if(state_r!=one&&state==one)pulse_status <= 1'b1;else if(now_pulse_num == 2*motor_pulse)pulse_status <= 1'b0;	else if(rst_status==1'b1)pulse_status <= 1'b0;elsepulse_status <= pulse_status;always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)motor_exti <= 1'b0;else if(stop_pose==1'b1)motor_exti <= 1'b1;else if(rst_status==1'b1)motor_exti <= 1'b1;else if(cnt_exti_us == EXTI_WIDTH-1'b1)motor_exti <= 1'b0;	elsemotor_exti <= motor_exti;always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)cnt_exti_us <= 'd0;else if(stop_pose==1'b1)cnt_exti_us <= 1'b0;else if(cnt_exti_us== EXTI_WIDTH-1'b1)cnt_exti_us <= 'd0;else if(motor_exti==1'b1)cnt_exti_us <= cnt_exti_us + 1'b1;elsecnt_exti_us <= cnt_exti_us; always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)cnt_pulse_us <= 1'b0;else if(stop_pose==1'b1)cnt_pulse_us <= 1'b0;else if(cnt_pulse_us == PULSE_WIDTH-1'b1)cnt_pulse_us <= 1'b0;else if(pulse_status == 1'b1)cnt_pulse_us <= cnt_pulse_us + 1'b1;elsecnt_pulse_us <= 1'b0;always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)motor_step <= 1'b0;else if(stop_pose==1'b1)motor_step <= 1'b0;else if(cnt_pulse_us == PULSE_WIDTH-1'b1)motor_step <= ~ motor_step;else if(now_pulse_num == 2*motor_pulse )motor_step <= 1'b0;else if(rst_status==1'b1)motor_step <= 1'b0;else motor_step <= motor_step;  	always@(posedge sys_clk or negedge sys_rst)if(!sys_rst)now_pulse_num <= 1'b0;else if(stop_pose==1'b1)now_pulse_num <= 1'b0;else if(cnt_pulse_us == PULSE_WIDTH-1'b1)now_pulse_num <= now_pulse_num + 1'b1;else if(now_pulse_num == 2*motor_pulse)now_pulse_num <= 1'b0;else if(rst_status==1'b1)now_pulse_num <= 1'b0;elsenow_pulse_num <= now_pulse_num;  //电机运动方向与输入设定方向相同
assign motor_dir  = motor_direct;	endmodule

3.仿真波形

在这里插入图片描述
在这里插入图片描述
可见脉冲脉宽为100个时钟周期,中断周期为50个时钟周期。在原先的参数设定中,PULSE_WIDTH为50,EXTI_WIDTH为50,符合设计要求。且发出脉冲为3个脉冲。


总结

今天吃了手撕鸡+豆腐,味道也很不错,手撕鸡挺柴的,晚上吃的鱼香肉丝拌面,味道确实不错,下次可以继续尝尝。

这篇关于007-可调脉冲数触发之FPGA实现(Zynq也可驱动,带启动停止及完成中断输出)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/592462

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount