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

news2024/9/27 21:18:42

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,		//50MHz
		input 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) begin
		if(stepper_delay_current > stepper_delay_target) speed_down_en <= 1'b1; 
		else speed_down_en <= 1'b0;
	end
	else begin
		if(stepper_delay_current > START_FRE) speed_down_en <= 1'b1;
		else speed_down_en <= 1'b0;	
	end
	
always @(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) begin
		if(!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) begin
		if(stepper_work_en) stepper_delay_current <= stepper_delay_target;
		else stepper_delay_current <= START_FRE;
	end
	else if(motor_speed_change) begin
		if(stepper_work_en) begin
			if(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;
		end
		else begin
			if(speed_down_en) stepper_delay_current <= START_FRE + delta_speed_down_pulse;
			else stepper_delay_current <= START_FRE;
		end
	end

wire[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.coloradmin.cn/o/1130506.html

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

相关文章

Java IDEA controller导出CSV,excel

Java IDEA controller导出CSV&#xff0c;excel 导出excel/csv&#xff0c;亲测可共用一个方法&#xff0c;代码逻辑里判断设置不同的表头及contentType&#xff1b;导出excel导出csv 优化&#xff1a;有数据时才可以导出参考 导出excel/csv&#xff0c;亲测可共用一个方法&…

【Jenkins 安装】

一&#xff1a;安装文件夹准备 在/home/admin 界面下新建三个文件夹&#xff0c;用来安装tomcat、maven 1.打开&#xff0c;/home/admin目录 cd /home/admin 2.新建三个文件夹 mkdir tomcat mkdir maven 二&#xff1a;安装tomcat 1.打开tomcat目录进行tomcat的安装 访问:h…

Xfigure综合膳食营养粉美丽上线,大健康行业竞争呈现多元化

10月21日&#xff0c;“美丽健康 营养为先”2023全民营养健康科学论坛暨悦小妖2023秋季新品发布会在杭州召开&#xff0c;会上就当下的国民营养健康问题提出了许多建设性的观点&#xff0c;新发布的Xfigure是行业内少有的提倡营养为主的特膳类产品。 拥抱趋势&#xff0c;全新突…

redis持久化之RDB(Redis DataBase)

1 : 总体介绍 Redis是一个基于内存的数据库&#xff0c;它的数据是存放在内存中&#xff0c;内存有个问题就是关闭服务或者断电会丢 失。 Redis的数据也支持写到硬盘中&#xff0c;这个过程就叫做持久化 1.1 。 Redis提供了2种不同形式的持久化方式。 RDB&#xff08;Redis Da…

uboot移植之DDR初始化参数更改说明

一. 简介 裸机篇开发时&#xff0c;DDR初始化是 imxdownload软件完成的。imxdownload软件在 二进制文件 u-boot.bin 前面加上头部(IVT、DCD等数据)。这其中所加的头部信息就包括 DDR初始化内容。 u-boot.bin 就是编译出来的 uboot 二进制文件。 uboot 是个裸机程序&#x…

JUnit5参数化测试的几种方式!

参数化测试一直是津津乐道的话题&#xff0c;我们都知道JMeter有四种参数化方式&#xff1a;用户自定义变量、用户参数、CSV文件、函数助手&#xff0c;那么JUnit5有哪些参数化测试的方式呢&#xff1f; 依赖 JUnit5需要添加junit-jupiter-params依赖才能使用参数化&#xff…

Java项目_家庭记账(简易版)

文章目录 简介代码实现 简介 该项目主要用来练习&#xff0c;Java的变量&#xff0c;运算符&#xff0c;分支结构和循环结构的知识点。 程序界面如下&#xff1a; 登记收入 登记支出 收支明细 程序退出 代码实现 package project;import java.util.Scanner;import sta…

【疑问解决】- 源码Enmu枚举类的toString里面的name是哪里来的,什么时候传入的?

起因是听课到 该段的输出boy输出什么&#xff1f; 答案就是输出BOY&#xff0c;但韩老师解释的有点笼统。 但是我看了一眼源码关于这个name确实有点没头绪 public abstract class Enum<E extends Enum<E>>implements Comparable<E>, Serializable {/*** T…

可自由搭建的能源管理平台,轻松实现高效节能

随着科技的不断发展&#xff0c;能源问题越来越重要。为了提高能源的利用效率&#xff0c;减少能源浪费&#xff0c;能源用能企业纷纷开始注重能源管理工作&#xff0c;并想要一款可以进行高效管理的工具。智慧能源管理平台&#xff0c;是一款可自由搭建的能源管理平台&#xf…

【从0到1设计一个网关】自研网关的架构搭建

文章目录 项目骨架搭建领域模型与DDD核心上下文模型封装静态配置的加载组件生命周期项目骨架搭建 这里我使用的IDE工具是IDEA。 从上文中我们了解到,我们的项目大概有五个模块,Client,Common,Register Center,Config Center,Core这五个模块。 下面开始具体骨架的搭建,…

Mysql在ubuntu22.04上安装配置

更新并下载Mysql sudo apt update sudo apt install mysql-server启动Mysql服务 sudo systemctl start mysql安全配置 包括设置密码、删除匿名用户、禁止远程root登录等&#xff0c;按提示进行即可。 sudo mysql_secure_installation是否设置密码&#xff1a;是 三种强度密…

使用 DDPO 在 TRL 中微调 Stable Diffusion 模型

引言 扩散模型 (如 DALL-E 2、Stable Diffusion) 是一类文生图模型&#xff0c;在生成图像 (尤其是有照片级真实感的图像) 方面取得了广泛成功。然而&#xff0c;这些模型生成的图像可能并不总是符合人类偏好或人类意图。因此出现了对齐问题&#xff0c;即如何确保模型的输出与…

重装win11,个人记录详细步骤-干货

重装win11&#xff0c;个人记录详细步骤-干货 下载镜像-windows官网 https://www.microsoft.com/zh-cn/software-download/windows11%20 安装的选这个就行 虽然他这里写的是家庭版&#xff0c;进去里面就可以选择其他版本 重装win11有个前提 系统最低要求 本文列出了 Windo…

Databend 开源周报第 116 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 特性预览&#…

AR道具贴纸SDK,创新技术解决方案

增强现实&#xff08;AR&#xff09;技术已经成为企业提升用户体验&#xff0c;增强品牌影响力的重要工具。美摄AR道具贴纸SDK&#xff0c;作为一款领先的AR技术产品&#xff0c;致力于为企业提供一站式的技术解决方案&#xff0c;帮助企业轻松实现AR应用的快速开发和部署。 一…

使用Vscode创建一个C_Hello程序

Vscode用来学习C语言语法确实很方便。问题是安装好了&#xff0c;不会用&#xff0c;或编译失败&#xff0c;也是常有的事情&#xff0c;其中一个原因就是不会创建工作区。下面介绍使用Vscode创建一个C语言工作区。有时候看着很简单&#xff0c;时间久了&#xff0c;我竟然忘记…

Element UI + Vue 新增和编辑共用表单校验无法清除问题(已解决)

问题描述 在新增和编辑过程中大部分情况下 两个表单是一致的&#xff0c;而且编辑也有回显需要&#xff0c;所有绝大多数情况下 都是一个表单两个用处&#xff0c;但是随之而来出现了一个无法清除校验的问题&#xff0c;在先点击编辑后再点击新增会出现校验红字&#xff1a; …

centos搭建elastic集群

1、环境可以在同一台集群上搭建elastic&#xff0c;也可以在三台机器上搭建&#xff0c;这次演示的是在同一台机器搭建机器。 2、下载elastic &#xff1a;https://www.elastic.co/cn/downloads/past-releases#elasticsearch 2、​​​​​​ tar -zxvf elasticsearch-xxx-版…

ubuntu18.04双系统安装(2023最新最详细)以及解决重启后发现进不了Ubuntu问题

目录 一.简介 二.安装教程 1.首先确定了电脑的引导格式是UEFIGPT还是BIOSMBR 2. 使用Windows磁盘管理划分足够的磁盘空间 3. 开始安装 三.重启后发现自动进入WIN10系统了&#xff0c;进不了Ubuntu&#xff1f; 一.简介 Linux是一种自由和开放源代码的操作系统内核&#x…

【嵌入式项目应用】__JSON数据格式(无脑操作移植使用)

目录 一、什么是JSON 二、JSON是什么样子的呢&#xff1f; 三、常用类型 四、和XML的比较 五、CJSON库&#xff1f; 六、CJSON库组包与解析示例 1. 确定协议数据 2. 组JSON数据包实例 操作实例&#xff1a; 完整代码&#xff1a; 3. 解析JSON数据包示例 操作实例 完…