15. 步进电机圆弧插补实现

您所在的位置:网站首页 3d打印机丝杆计算器在哪里设置 15. 步进电机圆弧插补实现

15. 步进电机圆弧插补实现

2024-07-14 14:15:01| 来源: 网络整理| 查看: 265

15. 步进电机圆弧插补实现¶ 15.1. 逐点比较法圆弧插补原理¶

逐点比较法的圆弧插补和直线插补一样,都分为偏差判别、坐标进给、偏差计算和终点判别四个步骤。逐点比较法圆弧插补中,一般以坐标原点为圆心,给出圆弧起点坐标和终点坐标, 以及圆弧的加工方向和所在的象限。为了降低复杂度,我们首先讲解位于第一象限的逆时针圆弧插补。

15.1.1. 偏差判别¶

设坐标轴中有一段逆时针方向的圆弧AB,圆形为坐标原点,起点坐标A(X0,Y0),终点坐标B(Xe,Ye),圆弧上有一加工动点P, 坐标P(Xi,Yi),如下图所示。

设A点到圆心的距离为R,动点P到圆心的距离为RP,根据上图给出的信息,使用圆的标准方程可得圆弧AB所在圆的方程和动点P所在圆的方程:

设动点P所在圆的半径与圆弧AB所在圆的半径之间的差值为插补的偏差值,记为Fi,则有式子:

从上面的式子中可以分析出,Fi的正负能够反映动点P与圆弧AB的位置偏差情况。

当Fi= 0时,RP= R,则动点P在圆弧AB上;

当Fi> 0时,RP> R,则动点P在圆弧AB的外侧;

当Fi< 0时,RP< R,则动点P在圆弧AB的内侧。

15.1.2. 坐标进给¶

逐点比较法的坐标进给有两个原则:一是减小加工动点相对于理论轨迹的位置偏差,二是进给方向总是平行与某个坐标轴。 根据这个原则以及上个步骤偏差判别的信息,可以得出第一象限逆时针圆弧插补的坐标进给方法:

Fi= 0,动点P在圆弧AB上,可向圆内进给一步,也可向圆外方向进给一步,通常规定向圆内即-X方向进给;

Fi> 0,动点P在圆弧AB的外侧,应该向圆内即-X方向进给一步;

Fi< 0,动点P在圆弧AB的内侧,应该向圆外即+Y方向进给一步;

当动点P在X轴上时,为减小误差,通常直接向+Y方向进给一步。

整个坐标进给的轨迹效果可以用一张图演示出来,如下图所示。

当然,图中只作为第一象限逆时针圆弧插补中刀具运动轨迹的演示,实际应用中的运动轨迹并不会像图里这么夸张,进给一步的长度是一个脉冲当量,以保证加工精度。

15.1.3. 偏差计算¶

在坐标进给之后得到新的动点坐标值,此时需要计算新的动点和理论轨迹之间的偏差值。从前面的讨论中我们知道了偏差值Fi的计算公式, 可以通过公式直接求出Fi。不过公式中有4次乘方运算,也可以说是4次乘法运算,虽然现在的各种控制器基本可以轻松的做乘法运算, 但是为了追求更高的运行效率,我们把当前的偏差计算公式做一点小小的优化,将其变为递推公式。

假设当Fi> 0时,加工动点P向圆内进给一步,生成一个新的动点Pi+1,坐标是(Xi+1, Yi+1), 则新动点的偏差值Fi+1计算公式为:

又因为动点Pi+1的坐标可由P点表示:

所以将由P点表示的Pi+1坐标代入Fi+1式中,可得:

上面最后得出的这个公式便是逐点比较法的第一象限逆时针圆弧插补偏差计算的递推公式,从式中可以看出, 偏差Fi+1的值只跟上一步进给的偏差Fi和上一步的动点坐标值Pi有关,且将四次乘方运算转为一次乘2运算,比原始公式更简单快速。

同理可得,当Fi< 0时,加工动点向+Y方向进给一步后的新偏差值递推公式:

需要指出的是,以上两个圆弧插补偏差计算公式中包含了上一个加工动点的坐标Xi和Yi,由于加工动点是变化的, 因此在计算偏差Fi+1的同时,还要计算出新的动点坐标Xi+1和Yi+1,为新的偏差值计算做准备,这是直线插补所不需要的。

15.1.4. 终点判别¶

常用的终点判别方法有三种,终点坐标法、投影法和总步长法。

终点坐标法。在启动插补之前,先定义X、Y两个方向的步长计数器,分别记录终点坐标在X、Y两个方向上的值。开始插补后当X、Y方向每进给一步, 就在相应的计数器中减1,直到两个计数器的值都减为0时,刀具抵达终点,停止插补。

投影法。在插补前,先比较出终点坐标的X、Y值中较大的一个,然后以较大的数值作为计数器的值,当对应的轴有进给时,计数器减1,直到计数器为0。 相当于终点坐标向值较大的轴做投影,所以叫投影法。

总步长法,即插补前,将终点坐标的X、Y值求和,得到一个总步长计数器,开始插补后,无论哪个轴进给一步,总步长计数器都减1,直到计数器等于0,停止插补。

以上三种终点判别的方法,全部使用坐标的绝对值进行计算。

15.2. 第一象限逆时针圆弧插补实验¶

上一节讲解了逐点比较法的第一象限逆时针圆弧插补原理,接下来结合实验程序讲解具体实现。本实验讲解如何实现第一象限逆时针圆弧插补。 学习本节内容时,请打开配套的“stepper—第一象限逆时针圆弧插补-逐点比较法”工程配合阅读。

15.2.1. 硬件设计¶

本实验的硬件设计部分与直线插补实验完全相同,在此不再赘述。如有不清楚的地方,请查看第一象限直线插补实验。

15.2.2. 软件设计¶

本次第一象限逆时针圆弧插补实验只讲解跟圆弧插补有关的部分核心代码,有些变量的设置,头文件的包含等并没有涉及到, 完整的代码请参考本章配套工程“stepper—第一象限逆时针圆弧插补-逐点比较法”。 我们创建了两个文件:bsp_circular_interpolation.c 和 bsp_circular_interpolation.h 文件用来存放圆弧插补程序及相关变量定义。

15.2.2.1. 编程要点¶

步进电机定时器 IO 配置

步进电机定时器时基、中断配置

在定时器中完成圆弧插补的4个步骤

通过对圆弧参数的设置实现第一象限圆弧插补

15.2.2.2. 软件分析¶

宏定义

bsp_stepper_init.h-宏定义¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 /*宏定义*/ /*******************************************************/ #define MOTOR_PUL_TIM TIM8 #define MOTOR_PUL_IRQn TIM8_UP_TIM13_IRQn #define MOTOR_PUL_IRQHandler TIM8_UP_TIM13_IRQHandler #define MOTOR_PUL_CLK_ENABLE() __TIM8_CLK_ENABLE() #define MOTOR_PUL_GPIO_AF GPIO_AF3_TIM8 /*********************X轴电机引脚定义*******************/ //Motor 方向 #define X_MOTOR_DIR_PIN GPIO_PIN_1 #define X_MOTOR_DIR_GPIO_PORT GPIOE #define X_MOTOR_DIR_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE() //Motor 使能 #define X_MOTOR_EN_PIN GPIO_PIN_0 #define X_MOTOR_EN_GPIO_PORT GPIOE #define X_MOTOR_EN_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE() //Motor 脉冲 #define X_MOTOR_PUL_PORT GPIOI #define X_MOTOR_PUL_PIN GPIO_PIN_5 #define X_MOTOR_PUL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() //定时器通道 #define X_MOTOR_PUL_CHANNEL TIM_CHANNEL_1 /*********************Y轴电机引脚定义*******************/ //Motor 方向 #define Y_MOTOR_DIR_PIN GPIO_PIN_8 #define Y_MOTOR_DIR_GPIO_PORT GPIOI #define Y_MOTOR_DIR_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() //Motor 使能 #define Y_MOTOR_EN_PIN GPIO_PIN_4 #define Y_MOTOR_EN_GPIO_PORT GPIOE #define Y_MOTOR_EN_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE() //Motor 脉冲 #define Y_MOTOR_PUL_PORT GPIOI #define Y_MOTOR_PUL_PIN GPIO_PIN_6 #define Y_MOTOR_PUL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() //定时器通道 #define Y_MOTOR_PUL_CHANNEL TIM_CHANNEL_2

由于直线插补需要两个步进电机才能完成,所以在bsp_stepper_init.h中新增第二个步进电机的相关IO口和外设的宏定义。 宏定义的具体内容与其他步进电机控制例程一致,故不再赘述。

步进电机初始化

bsp_stepper_init.h-结构体¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /* 步进电机结构体 */ typedef struct{ uint16_t pul_pin; //脉冲引脚 uint16_t dir_pin; //方向引脚 uint16_t en_pin; //使能引脚 uint32_t pul_channel; //输出脉冲的定时器通道 GPIO_TypeDef *pul_port; //脉冲引脚端口结构体 GPIO_TypeDef *dir_port; //方向引脚端口结构体 GPIO_TypeDef *en_port; //使能引脚端口结构体 }Stepper_TypeDef; /* 步进电机结构体数组 bsp_stepper_init.c */ Stepper_TypeDef step_motor[2] = { {X_MOTOR_PUL_PIN, X_MOTOR_DIR_PIN, X_MOTOR_EN_PIN, X_MOTOR_PUL_CHANNEL, X_MOTOR_PUL_PORT, X_MOTOR_DIR_GPIO_PORT, X_MOTOR_EN_GPIO_PORT}, {Y_MOTOR_PUL_PIN, Y_MOTOR_DIR_PIN, Y_MOTOR_EN_PIN, Y_MOTOR_PUL_CHANNEL, Y_MOTOR_PUL_PORT, Y_MOTOR_DIR_GPIO_PORT, Y_MOTOR_EN_GPIO_PORT}, };

在bsp_stepper_init.h中定义了一个结构体,内部包括控制步进电机所必须的引脚端口、编号和脉冲输出的定时器通道, 然后在bsp_stepper_init.c中定义了一个Stepper_TypeDef类型的数组,分别管理X、Y轴步进电机。这么做的目的是为了方便后续程序更方便的控制两个步进电机, 如需增加或减少步进电机的数量,直接增加或减少数组元素即可。

bsp_stepper_init.c-定时器初始化¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48static void TIM_PWMOUTPUT_Config(void) { TIM_OC_InitTypeDef TIM_OCInitStructure; /* 获取数组元素个数 */ uint8_t member_count = sizeof(step_motor)/sizeof(Stepper_TypeDef); /*使能定时器*/ MOTOR_PUL_CLK_ENABLE(); TIM_StepperHandle.Instance = MOTOR_PUL_TIM; /* 累计 TIM_Period个后产生一个更新或者中断*/ //当定时器从0计数到TIM_PERIOD,即为TIM_PERIOD次,为一个定时周期 TIM_StepperHandle.Init.Period = TIM_PERIOD; // 通用控制定时器时钟源TIMxCLK = HCLK = 168MHz // 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=28MHz TIM_StepperHandle.Init.Prescaler = TIM_PRESCALER-1; /*计数方式*/ TIM_StepperHandle.Init.CounterMode = TIM_COUNTERMODE_UP; /*采样时钟分频*/ TIM_StepperHandle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; TIM_StepperHandle.Init.RepetitionCounter = 0; /*初始化定时器为输出比较模式*/ HAL_TIM_Base_Init(&TIM_StepperHandle); /*PWM模式配置--这里配置为PWM模式2*/ TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM2; /*比较输出的计数值*/ TIM_OCInitStructure.Pulse = TIM_PERIOD; /*当定时器计数值小于CCR1_Val时为高电平*/ TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH; /*设置互补通道输出的极性*/ TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH; /*快速模式设置*/ TIM_OCInitStructure.OCFastMode = TIM_OCFAST_DISABLE; /*空闲电平*/ TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_RESET; /*互补通道设置*/ TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET; for(uint8_t i = 0; i = 0) { /* 偏差>=0,在圆弧外侧,应向圆内进给,计算偏差 */ interpolation_para.active_axis = x_axis; interpolation_para.deviation -= (2 * interpolation_para.startpoint_x + 1); } else { /* 偏差Instance, step_motor[last_axis].pul_channel, TIM_CCx_DISABLE); TIM_CCxChannelCmd(htim->Instance, step_motor[interpolation_para.active_axis].pul_channel, TIM_CCx_ENABLE); } /* 进给总步数减1 */ interpolation_para.endpoint_pulse--; /* 判断是否完成插补 */ if(interpolation_para.endpoint_pulse == 0) { /* 关闭定时器 */ TIM_CCxChannelCmd(htim->Instance, step_motor[last_axis].pul_channel, TIM_CCx_DISABLE); TIM_CCxChannelCmd(htim->Instance, step_motor[interpolation_para.active_axis].pul_channel, TIM_CCx_DISABLE); __HAL_TIM_MOE_DISABLE(htim); HAL_TIM_Base_Stop_IT(htim); interpolation_para.motionstatus = 0; } }

开启定时器中断后,后续的处理都直接放到中断回调函数中进行。

第12行:记录上一步进给的活动轴;

第15~30行:这一部分用来计算上一步加工动点的坐标,先判断活动轴是X还是Y,如果是X轴就继续判断上一步是正转还是反转, 正转+1,反转-1,Y轴同理;

第34~44行:这一部分代码根据上一步的动点坐标和偏差值计算新的偏差值;

第48~51行:比较上一步进给的轴和下一步进给的轴是否一致,如果不一致,需要切换PWM输出的通道;

第55行:完成一次插补,总的进给步数就减一,这里使用了总步长法进行终点判别;

第58~65行:如果总的进给步数为0,则表示插补走到终点,关闭定时器结束插补。

整个第一象限逆时针圆弧插补算法的核心内容就在这个定时器中断回调函数中实现了,算法全部的4个步骤都在其中有所体现。

main函数

main.c-main函数¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 /** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { HAL_InitTick(0); /* 初始化系统时钟为168MHz */ SystemClock_Config(); /*初始化USART 配置模式为 115200 8-N-1,中断接收*/ DEBUG_USART_Config(); printf("欢迎使用野火 电机开发板 步进电机 第一象限圆弧插补 例程\r\n"); /* LED初始化 */ LED_GPIO_Config(); /* 按键初始化 */ Key_GPIO_Config(); /*步进电机初始化*/ stepper_Init(); while(1) { /* 逆时针圆弧 */ if(Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON) { Circular_InterPolation_CCW(6400 * 10, 0, 0, 6400 * 10, 1000); } } }

main函数中主要就是一些外设的初始化,包括步进电机的定时器初始化。然后在while循环中轮询按键,通过按键控制步进电机做第一象限逆圆插补。 需要注意的是,由于逐点比较法的圆弧插补进给一次是一个步进脉冲,所以最后插补出来的圆弧大小跟步进电机的细分直接相关,在本实验中默认步进电机32细分, 在main函数中输入的终点坐标参数中也对应步进电机的32细分,6400个脉冲步进电机旋转一圈。

15.2.3. 实验现象¶

使用两轴丝杆滑台组成一个标准X-Y滑动平台,将步进电机连接好,下载程序到开发板后,按下开发板的按键,可以看到丝杆滑台上的滑块沿着程序设定的圆弧轨迹运动。

15.3. 任意象限圆弧插补原理¶

在前面的内容中,我们详细讲解了第一象限逆时针圆弧插补的原理和实现,并推导出了偏差计算公式,不过实际应用中的圆弧插补并不只有第一象限,也有可能在第二、三、四象限。 在每个象限中除了逆时针方向,还有可能会遇到顺时针方向。所以下面的内容将会讲解如何实现任意象限和方向的圆弧插补。

首先我们来看第一象限的顺时针圆弧插补。设坐标轴中有一段顺时针方向的圆弧AB,圆形为坐标原点,起点坐标A(X0,Y0), 终点坐标B(Xe,Ye),圆弧上有一加工动点P,坐标P(Xi,Yi),如下图所示。

根据上面逆时针圆弧插补的思路,可以推出第一象限顺时针圆弧插补的偏差判别、偏差值计算公式和XY坐标进给方向。如下表所示。

综合这张计算表我们可以发现几个特点,如果把第一象限顺时针圆弧的X、Y轴插补方向对调,就变成了第一象限逆时针圆弧的插补方向, 这样顺时针圆弧的插补问题就变成了逆时针的插补问题,两个方向的偏差计算公式形式保持不变,只是符号和对应的X、Y轴变化。

实际上和直线插补类似,逐点比较法的圆弧插补同样可以利用第一象限逆圆插补推出所有象限和方向的插补情况。 设SR1、SR2、SR3、SR4为第一、二、三、四象限的顺时针圆弧,NR1、NR2、NR3、NR4第一、二、三、四象限的逆时针圆弧,一共8种线形。 根据他们各自的偏差判别、偏差值计算公式和XY坐标进给方向,可以归纳为2组,这里由于篇幅限制,不展开推导每种线形的偏差计算公式, 而是分别用两张图表来说明,对此感兴趣的可以自己尝试推导。

首先第一组是NR1、NR3、SR2、SR4,这一组的共同点如下:

当Fi≥ 0时,X轴进给,NR1、SR4走-X方向,SR2、NR3走+X方向;

当Fi< 0时,Y轴进给,NR1、SR2走+Y方向,NR3、SR4走-Y方向。

设都从各自的圆弧起点开始插补,则第一组的圆弧插补方向在一张坐标系图中可得:

第一组的偏差计算公式与第一象限逆时针圆弧插补相同,只是X、Y坐标值使用绝对值参与计算。 将这一组的偏差计算公式和进给方向总结为一张表格,如下表所示。

然后第二组是SR1、SR3、NR2、NR4,这一组的共同点如下:

当Fi≥ 0时,Y轴进给,SR1、NR2走-Y方向,SR3、NR4走+Y方向;

当Fi< 0时,X轴进给,SR1、NR4走+X方向,NR2、SR3走-X方向。

同样,第二组的圆弧插补方向在坐标系图中表示为:

第二组的偏差计算公式与第二组相反,并且X、Y坐标值使用绝对值参与计算。 第二组的偏差计算公式和进给方向总结为一张表格,如下表所示。

15.4. 任意象限双向圆弧插补实验¶

上一节讲解了逐点比较法的任意象限双向圆弧插补原理,接下来结合实验程序讲解具体实现。 学习本节内容时,请打开配套的“stepper—任意象限双向圆弧插补-逐点比较法”工程配合阅读。

15.4.1. 硬件设计¶

本实验的硬件设计部分与直线插补实验完全相同,在此不再赘述。如有不清楚的地方,请查看第一象限直线插补实验。

15.4.2. 软件设计¶

本次任意象限双向圆弧插补实验只讲解跟圆弧插补有关的部分核心代码,有些变量的设置,头文件的包含等并没有涉及到, 完整的代码请参考本章配套工程“stepper—任意象限双向圆弧插补-逐点比较法”。 我们创建了两个文件:bsp_circular_interpolation.c 和 bsp_circular_interpolation.h 文件用来存放圆弧插补程序及相关变量定义。

15.4.2.1. 编程要点¶

步进电机定时器 IO 配置

步进电机定时器时基、中断配置

在定时器中完成圆弧插补的4个步骤

判断圆弧起点终点坐标,实现任意象限圆弧插补。

15.4.2.2. 软件分析¶

任意象限双向圆弧插补相关参数

bsp_circular_interpolation.h-圆弧插补相关参数¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 /* 坐标轴枚举 */ typedef enum{ x_axis = 0U, y_axis }Axis_TypeDef; /* 坐标轴象限枚举 */ typedef enum{ quadrant_1st = 0U, quadrant_2nd, quadrant_3rd, quadrant_4th }Quadrant_TypeDef; /* 圆弧插补参数结构体 */ typedef struct{ __IO int32_t startpoint[2]; //起点坐标X、Y __IO int32_t endpoint_x; //终点坐标X __IO int32_t endpoint_y; //终点坐标Y __IO uint32_t endpoint_pulse; //到达终点位置需要的脉冲数 __IO uint32_t active_axis; //当前运动的轴 __IO int32_t deviation; //偏差参数F __IO int8_t devi_sign[2]; //偏差方程的运算符号,正负 __IO uint8_t motionstatus : 1; //插补运动状态 __IO uint8_t dir_x : 1; //X轴运动方向 __IO uint8_t dir_y : 1; //Y轴运动方向 __IO uint8_t dir_interpo : 1; //插补整体运动方向 __IO uint8_t crood_pos : 2; //起点坐标所在的象限 }CircularInterpolation_TypeDef;

本实验的步进电机相关宏定义、引脚和定时器初始化与之前讲过的实验完全相同,所以在此不再赘述,重点讲解任意象限圆弧插补的实现过程。

在上述代码中分别定义了坐标轴和坐标象限的枚举,和一个圆弧插补相关参数的结构体。CircularInterpolation_TypeDef结构体相比第一象限逆圆插补,修改和增加了一些成员变量, 其中startpoint[2]数组变量用来存放起点的坐标(x,y),devi_sign[2]数组中的两个元素分别保存X轴偏差方程和Y轴偏差方程的运算符号,为了判断圆弧插补的方向和所在象限, 增加了dir_interpo和crood_pos。

任意象限双向圆弧插补

bsp_circular_interpolation.c-判断插补进给方向¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 /** * @brief 设置进给方向 * @param coord_x * @param coord_y * @retval 无 */ static void Set_Feed_DIR(int32_t coord_x, int32_t coord_y, uint8_t dir) { /* 记录插补运动方向 */ circular_para.dir_interpo = dir; if(dir == CW) { if(coord_x > 0)/* x正半轴 */ { if(coord_y > 0)/* 第一象限 */ { circular_para.crood_pos = quadrant_1st; circular_para.dir_x = CW; circular_para.dir_y = CCW; circular_para.devi_sign[x_axis] = 1; circular_para.devi_sign[y_axis] = -1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CCW); } else/* 第四象限 */ { circular_para.crood_pos = quadrant_4th; circular_para.dir_x = CCW; circular_para.dir_y = CCW; circular_para.devi_sign[x_axis] = -1; circular_para.devi_sign[y_axis] = -1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CCW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CCW); } } else if(coord_x = 0)/* 第二象限 */ { circular_para.crood_pos = quadrant_2nd; circular_para.dir_x = CW; circular_para.dir_y = CW; circular_para.devi_sign[x_axis] = 1; circular_para.devi_sign[y_axis] = 1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CW); } else/* 第三象限 */ { circular_para.crood_pos = quadrant_3rd; circular_para.dir_x = CCW; circular_para.dir_y = CW; circular_para.devi_sign[x_axis] = -1; circular_para.devi_sign[y_axis] = 1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CCW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CW); } } else if(coord_x == 0)/* x=0,当前点在Y轴上 */ { if(coord_y > 0)/* 第一象限 */ { circular_para.crood_pos = quadrant_1st; circular_para.dir_x = CW; circular_para.dir_y = CCW; circular_para.devi_sign[x_axis] = 1; circular_para.devi_sign[y_axis] = -1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CCW); } else if(coord_y 0)/* x正半轴 */ { if(coord_y >= 0)/* 第一象限 */ { circular_para.crood_pos = quadrant_1st; circular_para.dir_x = CCW; circular_para.dir_y = CW; circular_para.devi_sign[x_axis] = -1; circular_para.devi_sign[y_axis] = 1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CCW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CW); } else/* 第四象限 */ { circular_para.crood_pos = quadrant_4th; circular_para.dir_x = CW; circular_para.dir_y = CW; circular_para.devi_sign[x_axis] = 1; circular_para.devi_sign[y_axis] = 1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CW); } } else if(coord_x 0)/* 第二象限 */ { circular_para.crood_pos = quadrant_2nd; circular_para.dir_x = CCW; circular_para.dir_y = CCW; circular_para.devi_sign[x_axis] = -1; circular_para.devi_sign[y_axis] = -1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CCW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CCW); } else/* 第三象限 */ { circular_para.crood_pos = quadrant_3rd; circular_para.dir_x = CW; circular_para.dir_y = CCW; circular_para.devi_sign[x_axis] = 1; circular_para.devi_sign[y_axis] = -1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CCW); } } else if(coord_x == 0)/* x=0,当前点在Y轴上 */ { if(coord_y > 0)/* 第二象限 */ { circular_para.crood_pos = quadrant_2nd; circular_para.dir_x = CCW; circular_para.dir_y = CCW; circular_para.devi_sign[x_axis] = -1; circular_para.devi_sign[y_axis] = -1; MOTOR_DIR(step_motor[x_axis].dir_port, step_motor[x_axis].dir_pin, CCW); MOTOR_DIR(step_motor[y_axis].dir_port, step_motor[y_axis].dir_pin, CCW); } else if(coord_y = 0) { switch(circular_para.dir_interpo) { case CW:/* 顺时针 */ switch(circular_para.crood_pos) { case quadrant_1st: case quadrant_3rd: circular_para.active_axis = y_axis; break; case quadrant_2nd: case quadrant_4th: circular_para.active_axis = x_axis; break; } break; case CCW:/* 逆时针 */ switch(circular_para.crood_pos) { case quadrant_1st: case quadrant_3rd: circular_para.active_axis = x_axis; break; case quadrant_2nd: case quadrant_4th: circular_para.active_axis = y_axis; break; } break; } } else /* 偏差小于0,向圆外进给 */ { switch(circular_para.dir_interpo) { case CW:/* 顺时针 */ switch(circular_para.crood_pos) { case quadrant_1st: case quadrant_3rd: circular_para.active_axis = x_axis; break; case quadrant_2nd: case quadrant_4th: circular_para.active_axis = y_axis; break; } break; case CCW:/* 逆时针 */ switch(circular_para.crood_pos) { case quadrant_1st: case quadrant_3rd: circular_para.active_axis = y_axis; break; case quadrant_2nd: case quadrant_4th: circular_para.active_axis = x_axis; break; } break; } } /* 根据插补运动方向和进给方向计算出新的偏差 */ circular_para.deviation += 2 * circular_para.devi_sign[circular_para.active_axis] * circular_para.startpoint[circular_para.active_axis] + 1; /* 下一步的活动轴与上一步的不一致时,需要换轴 */ if(last_axis != circular_para.active_axis) { TIM_CCxChannelCmd(htim->Instance, step_motor[last_axis].pul_channel, TIM_CCx_DISABLE); TIM_CCxChannelCmd(htim->Instance, step_motor[circular_para.active_axis].pul_channel, TIM_CCx_ENABLE); } /* 进给总步数减1 */ circular_para.endpoint_pulse--; /* 判断是否完成插补 */ if(circular_para.endpoint_pulse == 0) { /* 关闭定时器 */ TIM_CCxChannelCmd(htim->Instance, step_motor[last_axis].pul_channel, TIM_CCx_DISABLE); TIM_CCxChannelCmd(htim->Instance, step_motor[circular_para.active_axis].pul_channel, TIM_CCx_DISABLE); __HAL_TIM_MOE_DISABLE(htim); HAL_TIM_Base_Stop_IT(htim); circular_para.motionstatus = 0; } }

开启定时器中断后,后续的处理都直接放到中断回调函数中进行。

第12行:记录上一步进给的活动轴;

第15~30行:这一部分用来计算上一步加工动点的坐标,先判断活动轴是X还是Y,如果是X轴就继续判断上一步是正转还是反转, 正转+1,反转-1,Y轴同理;

第34~95行:这一部分代码根据上一步的插补偏差大小、插补方向和动点所在象限,得出正在活动的轴;

第99~100行:这一行实现了任意象限圆弧插补的偏差计算公式,与上一节总结出的任意象限圆弧插补偏差计算公式几乎相同,只是多了对公式中正负号的控制, devi_sign变量便是公式中的正负号,由Set_Feed_DIR函数得出,这样可以仅用一行代码实现任意象限圆弧插补的所有公式;

第103~106行:比较上一步进给的轴和下一步进给的轴是否一致,如果不一致,需要切换PWM输出的通道;

第110行:完成一次插补,总的进给步数就减一,这里使用了总步长法进行终点判别;

第113~120行:如果总的进给步数为0,则表示插补走到终点,关闭定时器结束插补。

主函数

main.c-main函数¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 /** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { HAL_InitTick(0); /* 初始化系统时钟为168MHz */ SystemClock_Config(); /*初始化USART 配置模式为 1156400 8-N-1,中断接收*/ DEBUG_USART_Config(); printf("欢迎使用野火 电机开发板 步进电机 任意象限圆弧插补 例程\r\n"); /* LED初始化 */ LED_GPIO_Config(); /* 按键初始化 */ Key_GPIO_Config(); /*步进电机初始化*/ stepper_Init(); while(1) { /* 顺时针圆弧 */ if(Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON) { Circular_InterPolation(6400 * 10, 0, 0, -6400 * 10, 1000, CW); while(circular_para.motionstatus); Circular_InterPolation(0, -6400 * 10, -6400 * 10, 0, 1000, CW); while(circular_para.motionstatus); Circular_InterPolation(-6400 * 10, 0, 0, 6400 * 10, 1000, CW); while(circular_para.motionstatus); Circular_InterPolation(0, 6400 * 10, 6400 * 10, 0, 1000, CW); } /* 逆时针圆弧 */ if(Key_Scan(KEY3_GPIO_PORT, KEY3_PIN) == KEY_ON) { Circular_InterPolation(6400 * 10, 0, 0, 6400 * 10, 1000, CCW); while(circular_para.motionstatus); Circular_InterPolation(0, 6400 * 10, -6400 * 10, 0, 1000, CCW); while(circular_para.motionstatus); Circular_InterPolation(-6400 * 10, 0, 0, -6400 * 10, 1000, CCW); while(circular_para.motionstatus); Circular_InterPolation(0, -6400 * 10, 6400 * 10, 0, 1000, CCW); } } }

main函数中主要就是一些外设的初始化,包括步进电机的定时器初始化。然后在while循环中轮询按键,通过按键控制步进电机做圆弧插补。 按下KEY2,做一个从第一象限开始的顺时针圆弧插补,总的运动轨迹为正圆;按下KEY3,做一个从第一象限开始的逆时针圆弧插补,运动轨迹同样为正圆。

15.4.3. 实验现象¶

使用两轴丝杆滑台组成一个标准X-Y滑动平台,将步进电机连接好,下载程序到开发板后,按下开发板的按键,可以看到丝杆滑台上的滑块沿着程序设定的圆弧轨迹运动。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭