FPGA驱动步进电机-Sin曲线加速

FPGA驱动步进电机-Sin曲线加速

  • 基本实现原理
  • 实际仿真的波形
  • 程序

以下由特权同学的FPGA文档摘取

Sin 曲线控制 step 脉冲信号生成的功能框图如下所示。
在这里插入图片描述

基本实现原理

①判断步进电机驱动的目标频率 stepper_delay_target 与当前频率 stepper_delay_current的值是否一致,若一致,则不做任何加速、减速操作,保持当前速度运行;若目标频率高于当前频率,则执行加速;若目标频率低于当前频率,则执行减速。

②在加速或减速控制开启状态下,1ms 分频计数逻辑每个 1ms 产生一个高脉冲,用于切换当前的速度。

③在每 1ms,步进电机的速度都会加速或减速一定的频率值,这个频率值和匀加速总是“固定”不同,它是变化的。在低频时,速度变化量较大,而高速时,速度变化量较小。这个速度到底如何变化?变化多少?我们现实用 matlab 产生了一个 0‐pi 的 sin 曲线,并且将曲线所对应的 1024 个 16bit 数据存储到了 FPGA 的片内 ROM 里(实际使用中,0~0.5pi 部分的上升曲线作为步进电机加速的频率曲线,而 0.5pi~pi 的下降曲线作为步进电机减速的频率曲线)。如图所示,X 取值为 0~511 的曲线(左侧曲线,取 0~0.5pi 的 512 个正弦值放大 32768倍,并且挪到 0 点以上)作为加速曲线,X 取值为 512~1023 的曲线(右侧曲线,取 0.5pi~pi的 512 个正弦值放大 32768 倍)作为减速曲线。

那么这两段曲线如何使用?
在这里插入图片描述
由于加速或减速所用到的 sin 查表值都是 512 个,所以我们加速或减速所需经过的中间频率都是 512 个。对于加速操作,目标频率 stepper_delay_target,加速起始频率stepper_delay_current 的差值,需要经过 x 个中间加速频率点(x 从 0 递增到 511),我们可以算得当前加速的 delta_speed_pulse = (stepper_delay_target ‐stepper_delay_current ) * (x 为地址对应的 ROM 数据)/32768,即最终用(stepper_delay_target ‐ delta_speed_pulse/32768)作为当前频率点。换句话说,就是用 sin 曲线作为整个加速过程的频率差值,用目标频率减去这个差值,就可以得到从其实频率逐渐靠近目标频率的加速过程,并且这个加速过程和图示左侧的曲线一样越来越平滑。对于减速操作也类似,只不过它是用停止频率或减速目标频率加上当前 sin 曲线算出的频率差值作为当前中间频率点。

④实际的 FPGA coding 中,必须把“频率”换算为“周期”,便于计数。这个换算很简单,用 1s 时间除以“频率”值即可。而由于“周期”必须换算为“时钟脉冲个数”为单位,所以我们把这个除法运算中的“除数”也换算为“时钟脉冲个数”为单位,因此 1s/20ns(时钟周期为 20ns)即除数 50_000_000。

⑤算出了加速或减速过程中不断变化的步进电机驱动的脉冲周期stepper_delay_current_period,就能够产生步进电机驱动的脉冲了。

实际仿真的波形

在这里插入图片描述
以上匀加速/减速操作下,设定步进电机的启动频率为 500Hz,可以稳定加速到 5KHz,加速时间需要 512*1ms = 512ms

程序

module stepper_motor_controller(input clk,		//50MHzinput rst_n,	//复位信号,低电平有效output stepper_motor_reset_n,	//步进电机复位信号,低电平有效output reg stepper_motor_en_n,				//步进电机驱动信号输出有效信号,低电平有效output reg stepper_motor_clk,				//步进电机的前进脉冲,上升沿有效发起一个步进电机转动input stepper_work_en,	//步进电机使能input[19:0] stepper_delay_target,	//步进电机两次stpe之间的延时,取值必须大于2,目标延时值output reg[19:0] stepper_delay_current	//步进电机两次stpe之间的延时,取值必须大于2,当前延时值);parameter START_FRE = 20'd1000;	//步进电机起步频率,单位Hz
parameter TIMER_1MS = 20'd50_000;	//1ms定时计数最大值,时钟为50MHz(20ns)
parameter CLK_NUMER_PERIORD = 32'd50_000_000;	//除数,单位20ns//---------------------------------------------------------------------------
reg speed_up_en;		//加速功能开启
reg[1:0] speed_up_en_r; //加速寄存器打一拍
reg speed_down_en; 		//减速功能开启
reg[1:0] speed_down_en_r;//减速寄存器打一拍
reg[19:0] stepper_cnt; wire pos_speed_up_en;	//speed_up_en上升沿,表示开始启动加速	
wire pos_speed_down_en;	//speed_down_en上升沿,表示开始启动减速 //---------------------------------------------------------------------------
//复位
assign stepper_motor_reset_n = rst_n; //---------------------------------------------------------------------------
//1s加速/减速定时
reg[19:0] xcnt;always @(posedge clk or negedge rst_n)if(!rst_n) xcnt <= 20'd0;else if(!speed_up_en && !speed_down_en) xcnt <= 20'd0;else if(xcnt < (TIMER_1MS-1)) xcnt <= xcnt+1'b1;else xcnt <= 20'd0;	wire motor_speed_change = (xcnt == (TIMER_1MS-1));	//ms定时信号,高电平有效,切换速度//---------------------------------------------------------------------------
//例化ROM,存储sin输出1024个0-pi的结果,放大1024倍
reg[9:0] rom_addr;
wire[15:0] rom_data;blk_mem_gen_0	uut_blk_mem_gen_0 (	//IP核.address ( rom_addr ),.clock ( clk ),.q ( rom_data ));		//---------------------------------------------------------------------------
//匀加速、减速控制 //加速控制使能
always @(posedge clk or negedge rst_n)if(!rst_n) speed_up_en <= 1'b0; else if(motor_speed_change && (rom_addr == 10'd511)) speed_up_en <= 1'b0;	else if(((stepper_delay_current < stepper_delay_target) && stepper_work_en)) speed_up_en <= 1'b1;else speed_up_en <= 1'b0;always @(posedge clk)speed_up_en_r <= {speed_up_en_r[0],speed_up_en};	//记录电平变化assign pos_speed_up_en = ~speed_up_en_r[1] & speed_up_en_r[0];	//speed_up_en上升沿,表示开始启动加速//减速控制使能
always @(posedge clk or negedge rst_n)if(!rst_n) speed_down_en <= 1'b0; else if(motor_speed_change && (rom_addr == 10'd1023)) speed_down_en <= 1'b0;  else if(stepper_work_en) beginif(stepper_delay_current > stepper_delay_target) speed_down_en <= 1'b1; else speed_down_en <= 1'b0;endelse beginif(stepper_delay_current > START_FRE) speed_down_en <= 1'b1;else speed_down_en <= 1'b0;	endalways @(posedge clk)	speed_down_en_r <= {speed_down_en_r[0],speed_down_en};assign pos_speed_down_en = ~speed_down_en_r[1] & speed_down_en_r[0];	//speed_down_en上升沿,表示开始启动减速	
wire neg_speed_down_en = speed_down_en_r[1] & ~speed_down_en_r[0];//---------------------------------------------------------------------------
//ROM地址产生,对应加速/减速参数选择控制always @(posedge clk or negedge rst_n)if(!rst_n) rom_addr <= 10'd0;else if(pos_speed_up_en) rom_addr <= 10'd0;else if(pos_speed_down_en) rom_addr <= 10'd512;else if(motor_speed_change && (speed_up_en || speed_down_en)) rom_addr <= rom_addr+1'b1;else ;	//---------------------------------------------------------------------------
//乘法运算
wire[35:0] mult_result;
reg[19:0] mult_datab;
reg[19:0] speed_up_start;//锁存加速起始频率
always @(posedge clk or negedge rst_n)if(!rst_n) speed_up_start <= 20'd0;else if(pos_speed_up_en) speed_up_start <= stepper_delay_current;//计算当前运行频率和目标频率差
always @(posedge clk or negedge rst_n)if(!rst_n) mult_datab <= 20'd0;else if(pos_speed_up_en) mult_datab <= stepper_delay_target - stepper_delay_current;	//加速else if(pos_speed_down_en) beginif(!stepper_work_en) mult_datab <= stepper_delay_current - START_FRE;				//停止的减速else mult_datab <= stepper_delay_current - stepper_delay_target;					//运行中减速end//频率差*(加速频率/32768)
mult_gen_0	uut_mult_gen_0 (.clock ( clk ),.dataa ( rom_data ),.datab ( mult_datab ),.result ( mult_result ));wire[19:0] delta_speed_down_pulse = mult_result[34:15];	//加速或减速频率差值
wire[19:0] delta_speed_up_pulse = mult_result[34:15];	//加速或减速频率差值//---------------------------------------------------------------------------
//步进电机使能控制always @(posedge clk or negedge rst_n)if(!rst_n) stepper_motor_en_n <= 1'b1;else if(stepper_work_en) stepper_motor_en_n <= 1'b0;else if(!stepper_work_en && (stepper_delay_current == START_FRE)) stepper_motor_en_n <= 1'b1;//---------------------------------------------------------------------------
//步进电机的step产生//步进电机当前频率产生
always @(posedge clk or negedge rst_n)if(!rst_n) stepper_delay_current <= START_FRE;else if(neg_speed_down_en) beginif(stepper_work_en) stepper_delay_current <= stepper_delay_target;else stepper_delay_current <= START_FRE;endelse if(motor_speed_change) beginif(stepper_work_en) beginif(speed_up_en) stepper_delay_current <= speed_up_start + delta_speed_up_pulse;else if(speed_down_en) stepper_delay_current <= stepper_delay_target + delta_speed_down_pulse;else stepper_delay_current <= stepper_delay_target;endelse beginif(speed_down_en) stepper_delay_current <= START_FRE + delta_speed_down_pulse;else stepper_delay_current <= START_FRE;endendwire[19:0] stepper_delay_current_period;	
wire[31:0] div_result;		div_gen_0	uut_div_gen_0 (.clock ( clk ),.denom ( stepper_delay_current ),.numer ( CLK_NUMER_PERIORD ),.quotient ( div_result ),.remain (  ));assign stepper_delay_current_period = div_result[19:0];	reg[19:0] r_stepper_delay_current_period;	//步进电机驱动周期锁存
always @(posedge clk or negedge rst_n)if(!rst_n) r_stepper_delay_current_period <= 1'd0;else if(stepper_cnt == 20'd0) r_stepper_delay_current_period <= stepper_delay_current_period;	//步进电机时钟频率的计数
always @(posedge clk or negedge rst_n)if(!rst_n) stepper_cnt <= 20'd0;else if(stepper_cnt < r_stepper_delay_current_period[19:0]) stepper_cnt <= stepper_cnt+1'b1;else stepper_cnt <= 20'd0;//步进电机时钟频率产生	
always @(posedge clk or negedge rst_n)if(!rst_n) stepper_motor_clk <= 1'b0;else if(stepper_cnt < {1'b0,r_stepper_delay_current_period[19:1]}) stepper_motor_clk <= 1'b0;else stepper_motor_clk <= 1'b1;endmodule

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/118353.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

360智慧生活旗舰产品率先接入“360智脑”能力实现升级

10月25日&#xff0c;360智慧生活秋季新品及视觉云方案发布会在深圳召开。360智能硬件产品&#xff0c;诸如 360可视门铃、360智能摄像机、360行车记录仪、360儿童手表和家庭防火墙等&#xff0c;都在各自的行业有着举足轻重得地位&#xff0c;而这次发布的系列新品&#xff0c…

2023高频前端面试题-vue

1. 什么是 M V VM Model-View-ViewModel 模式 Model 层: 数据模型层 通过 Ajax、fetch 等 API 完成客户端和服务端业务模型的同步。 View 层: 视图层 作为视图模板存在&#xff0c;其实 View 就是⼀个动态模板。 ViewModel 层: 视图模型层 负责暴露数据给 View 层&…

mysql 命令行安装

一、安装包下载 1、下载压缩包 &#xff08;1&#xff09;公众号获取 关注微信公众号【I am Walker】&#xff0c;回复“mysql”获取 &#xff08;2&#xff09;官网下载 安装地址MySQL :: Download MySQL Community Server ​ ​ 二、解压 下载完之后进行解压&…

vscode代码快捷输入

Vscode代码片段快捷输入 常用的代码片段为了避免重复输入,可以使用Vsco的中用户代码片段进行设置,这样就可以实现快捷输入. 操作流程 如下 打开vscode的设置 2. 找到用户代码片段 3. 选择模板 4. 然后写入代码片段即可 上面的代码片段可以设置多个,看自己 重点关注的是 prefi…

勒索病毒最新变种.locked1勒索病毒来袭,如何恢复受感染的数据?

引言&#xff1a; 在当今数字化时代&#xff0c;网络威胁不断进化&#xff0c;.locked1勒索病毒就是其中一种常见的恶意软件。这种病毒会加密您的文件&#xff0c;然后勒索赎金以解锁它们。本文将详细介绍.locked1勒索病毒&#xff0c;包括如何恢复被加密的数据文件和如何预防…

微信小程序投票管理系统:打造智能、便捷的投票体验

前言 随着社交网络的兴起和移动互联网的普及&#xff0c;人们对于参与和表达意见的需求越来越强烈。在这个背景下&#xff0c;微信小程序投票管理系统应运而生。它为用户提供了一个智能、便捷的投票平台&#xff0c;使用户可以轻松创建和参与各种类型的投票活动。本文将详细介…

嚼一嚼Halcon中的3D手眼标定

文章目录 一、问题概述1、何为手眼标定&#xff1f;2、手眼标定的2种形式1&#xff09;眼在手上&#xff08;eye in hand&#xff09;&#xff1a;即相机固定在机械臂末端2&#xff09;眼在手外&#xff08;eye to hand&#xff09;&#xff1a;即相机固定在机械臂以外的地方 3…

微信小程序:点击按钮出现右侧弹窗

效果 代码 wxml <!-- 弹窗信息 --> <view class"popup-container" wx:if"{{showPopup}}"><view class"popup-content"><!-- 弹窗内容 --><text>这是一个右侧弹窗</text></view> </view> <…

Linux:实用操作

Linux&#xff1a;实用操作 1. 各类小技巧1.1 controlc(ctrl c) 强制停止1.2 可以通过快捷键&#xff1a;control d(ctrl d)&#xff0c;退出账户的登录1.3 历史命令搜索1.4 光标移动快捷键 2. 软件安装2.1 介绍2.2 yum命令(需要root权限)在这里插入图片描述 3. systemctl4.…

Java CC 解析 SQL 语法示例

示例&#xff1a;SimpleSelectParser 解析 select 11; 输出 2&#xff1b; 0&#xff09;总结 编写 JavaCC 模板&#xff0c;*.jj 文件。 编译生成代码文件。 移动代码文件到对应的包下。 调用生成的代码文件。 1&#xff09;JavaCC 模板 main/javacc/SimpleSelectParse…

【疯狂Java】数组

1、一维数组 (1)初始化 ①静态初始化&#xff1a;只指定元素&#xff0c;不指定长度 new 类型[] {元素1,元素2,...} int[] intArr; intArr new int[] {5,6,7,8}; ②动态初始化&#xff1a;只指定长度&#xff0c;不指定元素 new 类型[数组长度] int[] princes new in…

【Overload游戏引擎细节分析】Lambert材质Shader分析

一、经典光照模型&#xff1a;Phong模型 现实世界的光照是极其复杂的&#xff0c;而且会受到诸多因素的影响&#xff0c;这是以目前我们所拥有的处理能力无法模拟的。经典光照模型冯氏光照模型(Phong Lighting Model)通过单独计算光源成分得到综合光照效果&#xff0c;然后添加…

城市正视图(Urban Elevations, ACM/ICPC World Finals 1992, UVa221)rust解法

如图5-4所示&#xff0c;有n&#xff08;n≤100&#xff09;个建筑物。左侧是俯视图&#xff08;左上角为建筑物编号&#xff0c;右下角为高度&#xff09;&#xff0c;右侧是从南向北看的正视图。 输入每个建筑物左下角坐标&#xff08;即x、y坐标的最小值&#xff09;、宽度…

React之diff原理

一、是什么 跟Vue一致&#xff0c;React通过引入Virtual DOM的概念&#xff0c;极大地避免无效的Dom操作&#xff0c;使我们的页面的构建效率提到了极大的提升 而diff算法就是更高效地通过对比新旧Virtual DOM来找出真正的Dom变化之处 传统diff算法通过循环递归对节点进行依…

嵌入式中的MCU、ARM、DSP、FPGA

目录 “角色扮演” MCU ARM 特点 DSP 特点 FPGA 特点 应用 “角色扮演” MCU&#xff08;Microcontroller Unit&#xff09;、ARM&#xff08;Advanced RISC Machine&#xff09;、DSP&#xff08;Digital Signal Processor&#xff09;和FPGA&#xff08;Field-Progr…

PHP 危险函数2-代码执行语句

代码执行语句 eval() 不是函数&#xff0c;不能被动态调用&#xff0c;并且需要以 ;结束 直接输出&#xff0c;不执行 <?php$code"phpinfo();";echo $code;?>eval() 语句执行 <?php$code"phpinfo();";eval($code); // eval 不是函数&am…

【TGRS 2023】RingMo: A Remote Sensing Foundation ModelWith Masked Image Modeling

RingMo: A Remote Sensing Foundation Model With Masked Image Modeling, TGRS 2023 论文&#xff1a;https://ieeexplore.ieee.org/stamp/stamp.jsp?tp&arnumber9844015 代码&#xff1a;https://github.com/comeony/RingMo MindSpore/RingMo-Framework (gitee.com) …

软件测试之【单元测试、系统测试、集成测试】

一、单元测试的概念 单元测试&#xff08;Unit Testing&#xff09;是对软件基本组成单元进行的测试&#xff0c;如函数&#xff08;function或procedure&#xff09;或一个类的方法&#xff08;method&#xff09;。当然这里的基本单元不仅仅指的是一个函数或者方法&#xff…

Python:实现日历功能

背景 日常生活中&#xff0c;每天都要用到日历&#xff0c;日历成为我们生活中的必需品&#xff0c;那么如何制作日历呢&#xff0c;其实方法有很多&#xff0c;可以直接在excel中制作&#xff0c;也可以手画等等。 学习过编程的朋友&#xff0c;能否想到用Python编写一…

【Pytorch】Pytorch学习笔记02 - 单变量时间序列 LSTM

目录 说明简单神经网络LSTM原理Pytorch LSTM生成数据初始化前向传播方法训练模型自动化模型构建 总结参考文献 说明 这篇文章主要介绍如何使用PyTorch的API构建一个单变量时间序列 LSTM。文章首先介绍了LSTM&#xff0c;解释了它们在时间序列数据中的简单性和有效性。然后&…