开源改变世界!!

添加经验 PWM->RPM 查找表和平滑的主轴控制 #914

推推 grbl 2年前 (2023-01-23) 124次浏览

打开
doppelhub 打开了这个问题 2016 年 2 月 24 日 · 8条评论
打开

添加经验 PWM->RPM 查找表和平滑的主轴控制#914

doppelhub 打开了这个问题 2016 年 2 月 24 日 · 8条评论

注释

添加经验 PWM->RPM 查找表和平滑的主轴控制 #914

我建议向 spindle_control.c 添加两个功能:
1:缓慢斜坡到 pwm 设定点。大多数 PWM 电机控制器都会忠实地尝试瞬间启动或停止电机,从而导致大量电流涌入。请参阅第 139 至 149 行,了解我提出的概念验证。由于“S”命令是阻塞的,我没有看到任何使用 delay_ms() 的问题。请注意,此平滑功能仅在 PWM 输出处于活动状态时才有效,并且仅适用于“S”命令。例如:

//电机速度以前是S10000
M5
S0

…立即停止 PWM 训练,因为 PWM 输出在速度下降之前被禁用。

//电机速度以前是S10000
S0
M5

…根据需要平滑地增加 PWM 占空比。

2:我希望“S”速度与实际(经验)RPM 相关联。我添加了一个查找表,允许您更改输出计数器寄存器值。请参阅查找表的第 63 至 98 行,该查找表在第 137 行进行了索引。注意:LUT 值反映了我的经验数据……您需要注释掉第 137 行,直到用户替换了 LUT 值。该查找表存储在闪存中,不消耗任何内存。

是的,现在我的主轴平稳地倾斜并且在我停止时不会拉动 100A!

完成 spindle_control.c:

/*
  spindle_control.c - spindle control methods
  Part of Grbl v0.9

  Copyright (c) 2012-2014 Sungeun K. Jeon

  Grbl is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Grbl is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Grbl.  If not, see <http://www.gnu.org/licenses/>.
*/
/* 
  This file is based on work from Grbl v0.8, distributed under the 
  terms of the MIT-license. See COPYING for more details.  
    Copyright (c) 2009-2011 Simen Svale Skogsrud
    Copyright (c) 2012 Sungeun K. Jeon
*/ 

#include "system.h"
#include "spindle_control.h"
#include "protocol.h"
#include "gcode.h"


void spindle_init()
{    
  // On the Uno, spindle enable and PWM are shared. Other CPUs have seperate enable pin.
  #ifdef VARIABLE_SPINDLE
    SPINDLE_PWM_DDR |= (1<<SPINDLE_PWM_BIT); // Configure as PWM output pin.
    #ifndef CPU_MAP_ATMEGA328P 
      SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin.
    #endif     
  #else
    SPINDLE_ENABLE_DDR |= (1<<SPINDLE_ENABLE_BIT); // Configure as output pin.
  #endif
  SPINDLE_DIRECTION_DDR |= (1<<SPINDLE_DIRECTION_BIT); // Configure as output pin.
  spindle_stop();
}


void spindle_stop()
{
  // On the Uno, spindle enable and PWM are shared. Other CPUs have seperate enable pin.
  #ifdef VARIABLE_SPINDLE
    TCCRA_REGISTER &= ~(1<<COMB_BIT); // Disable PWM. Output voltage is zero.
    #ifndef CPU_MAP_ATMEGA328P 
      SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low.
    #endif
  #else
    SPINDLE_ENABLE_PORT &= ~(1<<SPINDLE_ENABLE_BIT); // Set pin to low.
  #endif  
}


//LUT to find corresponding PWM->RPM register value
//Substitutes requested 8bit PWM counter value with empirical RPM data
    static const __flash uint8_t lookup[256] = {
        0,  0,  59, 67, 73, 78, 82, 86,
        89, 93, 96, 98, 101,    103,    106,    108,
        110,    112,    114,    116,    118,    119,    121,    123,
        124,    126,    127,    129,    130,    132,    133,    134,
        136,    137,    138,    139,    141,    142,    143,    144,
        145,    146,    147,    148,    149,    150,    151,    152,
        153,    154,    155,    156,    157,    158,    159,    160,
        161,    162,    162,    163,    164,    165,    166,    166,
        167,    168,    169,    170,    170,    171,    172,    173,
        173,    174,    175,    176,    176,    177,    178,    178,
        179,    180,    180,    181,    182,    182,    183,    184,
        184,    185,    186,    186,    187,    187,    188,    189,
        189,    190,    190,    191,    192,    192,    193,    193,
        194,    194,    195,    196,    196,    197,    197,    198,
        198,    199,    199,    200,    200,    201,    201,    202,
        203,    203,    204,    204,    205,    205,    206,    206,
        207,    207,    208,    208,    208,    209,    209,    210,
        210,    211,    211,    212,    212,    213,    213,    214,
        214,    215,    215,    215,    216,    216,    217,    217,
        218,    218,    219,    219,    219,    220,    220,    221,
        221,    222,    222,    222,    223,    223,    224,    224,
        224,    225,    225,    226,    226,    226,    227,    227,
        228,    228,    228,    229,    229,    230,    230,    230,
        231,    231,    231,    232,    232,    233,    233,    233,
        234,    234,    234,    235,    235,    236,    236,    236,
        237,    237,    237,    238,    238,    238,    239,    239,
        240,    240,    240,    241,    241,    241,    242,    242,
        242,    243,    243,    243,    244,    244,    244,    245,
        245,    245,    246,    246,    246,    247,    247,    247,
        248,    248,    248,    249,    249,    249,    250,    250,
        250,    251,    251,    251,    252,    252,    252,    252,
        253,    253,    253,    254,    254,    254,    255,    255
        };


void spindle_run(uint8_t direction, float rpm) 
{
  if (sys.state == STATE_CHECK_MODE) { return; }

  // Empty planner buffer to ensure spindle is set when programmed.
  protocol_auto_cycle_start();  //temp fix for M3 lockup
  protocol_buffer_synchronize(); 

  // Halt or set spindle direction and rpm. 
  if (direction == SPINDLE_DISABLE) {

    spindle_stop();

  } else {

    if (direction == SPINDLE_ENABLE_CW) {
      SPINDLE_DIRECTION_PORT &= ~(1<<SPINDLE_DIRECTION_BIT);
    } else {
      SPINDLE_DIRECTION_PORT |= (1<<SPINDLE_DIRECTION_BIT);
    }

    #ifdef VARIABLE_SPINDLE
      // TODO: Install the optional capability for frequency-based output for servos.
      #define SPINDLE_RPM_RANGE (SPINDLE_MAX_RPM-SPINDLE_MIN_RPM)
      TCCRA_REGISTER = (1<<COMB_BIT) | (1<<WAVE1_REGISTER) | (1<<WAVE0_REGISTER);
      TCCRB_REGISTER = (TCCRB_REGISTER & 0b11111000) | 0x02; // set to 1/8 Prescaler
      if ( rpm < SPINDLE_MIN_RPM ) { rpm = 0; } 
      else { 
        rpm -= SPINDLE_MIN_RPM; 
        if ( rpm > SPINDLE_RPM_RANGE ) { rpm = SPINDLE_RPM_RANGE; } // Prevent uint8 overflow
      }
      uint8_t setpoint_pwm = floor( rpm*(255.0/SPINDLE_RPM_RANGE) + 0.5);
      #ifdef MINIMUM_SPINDLE_PWM
        if (setpoint_pwm < MINIMUM_SPINDLE_PWM) { setpoint_pwm = MINIMUM_SPINDLE_PWM; }
      #endif

    setpoint_pwm=lookup[setpoint_pwm]; //LUT to find corresponding PWM->RPM register value

    uint8_t current_pwm = OCR_REGISTER; //get previous PWM register value
    while(current_pwm != setpoint_pwm) {
        if(current_pwm > setpoint_pwm) {
            current_pwm -= 1;
        } else {
            current_pwm += 1;
        }

        OCR_REGISTER = current_pwm; // Set PWM pin output
        delay_ms(2);
    }

      #ifndef CPU_MAP_ATMEGA328P // On the Uno, spindle enable and PWM are shared.
        SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
      #endif
    #else   
      SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
    #endif

  }
}
添加经验 PWM->RPM 查找表和平滑的主轴控制 #914

我完全同意。我一直在朝着同样的想法努力,但时间让我放慢了脚步。通用电机是高度非线性的,不喜欢在全电压下停止。这两种选择都将是一个很大的好处。

每个路由器都有不同的曲线需要放入查找表中。也许是一个小实用程序,可以帮助用户确定该曲线,然后将其合并到 spindle_control.c 例程中。

添加经验 PWM->RPM 查找表和平滑的主轴控制 #914
成员

@doppelhub: 感谢您的代码建议。我将深入探讨我对它们的看法。首先,delay_ms() 绝对不能与主轴控制一起使用,因为实时主轴覆盖将被破坏(编写,工作,但未发布)。如果可能的话,使用阻塞延迟函数不是一个好主意。

其次,您不是第一个建议更精确的主轴控制的人。基本上我的回答是,确定主轴的行为方式不是 Grbl 的工作。只是它应该输出一致且可预测的信号以指示开/关和所需的 RPM。一个单独的主轴控制器应该是执行任务以确保主轴以正确的 RPM 运行并且运行平稳以避免您提到的放大器问题的控制器。

也就是说,我认识到有时用户拥有他们无法更改的主轴控制器,因此唯一的解决方案是更改主轴控制输出,就像您在代码提案中所做的那样。这是一个解决方案,但不是适用于所有人的通用解决方案。有很多方法可以解决这个问题,因此有很多解决方案。目前,我认为使用单独的控制器来解释 Grbl 的信号仍然是明智的,这可以通过一个简单的 5-10 美元的 Arduino Mini 和一个快速的 Arduino IDE 程序来完成。

@rwensley:为了扩展每个路由器和通用电机都是不同且高度非线性的想法,这进一步巩固了主轴控制器应该分开的想法。可以针对每个特定主轴(开环或闭环)轻松修改、更新和配置的专用 MCU 是最佳选择。它消除了在主 Grbl 固件中产生错误或使事情过于复杂的风险。它允许无限的灵活性。这也允许使用闭环主轴控制器,以确保主轴在负载下保持正确的 RPM,这肯定需要一个强大的专用控制器来以高刷新率监控主轴。

添加经验 PWM->RPM 查找表和平滑的主轴控制 #914
作者

感谢您的信息回复!
Sonny,只要我不使用实时主轴覆盖,delay_ms() 就可以了,对吗?
我问是因为我编写这段代码是为了更新我们已经发货的 1500 台 CNC 机器……我们当然不想添加额外的硬件。

仅供参考:我还创建了自动将“S”速度校准为实际 RPM 的代码(在 grbl 外部)。为此,在制造过程中,我们临时将一个霍尔传感器连接到引脚 9 上的计数器。代码随后遍历每个 PWM 寄存器值,记录频率,然后构建 LUT。

添加经验 PWM->RPM 查找表和平滑的主轴控制 #914
成员

@doppelhub: 对于 delay_ms() ,我不能保证它不会成为问题,尤其是在未来的 v1.0 中。就帮助您支持 1500 台 CNC 机器而言,我的大部分时间都花在支持官方 Grbl 赞助商上,其余时间用于开发 v1.0 和帮助用户。如果您继续这样做,我以后无法确保向后兼容。我也不能确保在 v1.0 中会有任何空间用于此代码,因为它希望完全使用 328p 中所有剩余的闪存空间。

添加经验 PWM->RPM 查找表和平滑的主轴控制 #914
作者

这对我们来说有点短,但我们仍在使用 0.9g,并且不打算很快更新。我查看了 0.9g 的架构,并没有看到 delay_ms() 在改变主轴速度时有任何方法可以帮助我们。我接受风险。仅供参考:我们在 V&V 中没有发现此代码有任何问题。

另外,我们需要做什么才能成为官方赞助商?可能比我去年寄给你的 30 美元还多,对吧 ;)?可能是时候进行另一次捐赠了。一如既往,感谢您对 grbl 的承诺!

添加经验 PWM->RPM 查找表和平滑的主轴控制 #914

您必须手动将暂停添加到 gcode 以考虑主轴旋转时间,这是正常程序吗?

添加经验 PWM->RPM 查找表和平滑的主轴控制 #914
作者

是的。如果您的主轴需要一段时间才能旋转起来,那么您将需要停留。grbl 根本不等待…只是更新 PWM,然后执行下一行代码。

添加经验 PWM->RPM 查找表和平滑的主轴控制 #914

…这就是为什么在专业世界中你有一条名为“SPINDLE_AT_SPEED”的线从主轴控制器返回到通用 CNC 控制器以暗示后者它可以在 S 字后恢复执行(IIRC G 代码明确提到如果控制器可以在技术上检测到当前的主轴速度,则它应该这样做)。

喜欢 (0)