GRBL五:定时器控制策略解析(转载)

因初学GRBL,网上搜集了一些GRBL的资料,怕遗忘,所以转载过来,如有侵权请联系,立即删除。

原文地址:https://blog.csdn.net/zhangjikuan/article/details/46697657

本文讲述的是根据grbl原始程序两个定时器控制脉冲输出的方式,由于在stm32上有PWM输出功能,所以可以用一个定时器的方式输出PWM来控制脉冲(因为pwm的占空比不影响步进速度,只需要固定占空就好,值修改脉冲周期就OK了),可以节约一个定时器,关于这种设计方式我只是有这个想法,本文未作实践。
GRBL的脉冲输出靠两个定时器协同控制输出的
具体控制策略:第一个定时器控制脉冲周期(因为步进电机脉冲周期决定速度)
                         第二个定时器控制一个周期中低电平的时间(脉冲宽度不重要,只要CPU能检测的到就好),相当于延时
流程如下图所示

所有代码都在stepper中定义
第一步:初始化引脚和定时器
在初始化定时器中没有给两个定时器设置分频和初值,也没有打开定时器开关
用的STM32通用16位定时器3和4
// Initialize and start the stepper motor subsystem
void st_init()
{
  // Configure directions of interface pins
//  STEPPING_DDR |= STEPPING_MASK;
//  STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask;
//  STEPPERS_DISABLE_DDR |= 1<   //ZJK ADD FOR STEP_IO INIT     
  GPIO_InitTypeDef GPIO_InitStructure;
  //X_STEP:PA8
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  X_STEP_PORT = (X_STEP_PORT & ~X_STEP_MASK) | (settings.invert_mask&X_STEP_MASK);
  //Y_STEP:PD15
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
  Y_STEP_PORT = (Y_STEP_PORT & ~Y_STEP_MASK) | (settings.invert_mask&Y_STEP_MASK);
  //X_DIR :PA6
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  X_DIRECTION_PORT = (X_DIRECTION_PORT & ~X_DIRECTION_MASK) | (settings.invert_mask&X_DIRECTION_MASK);
  //Y_DIR :PA2
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  Y_DIRECTION_PORT = (Y_DIRECTION_PORT & ~Y_DIRECTION_MASK) | (settings.invert_mask&Y_DIRECTION_MASK);
  //X_RES :PA3
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  //Y_RES :PA4
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure); 
  //END ZJK STEPP_IO INIT
 
  // waveform generation = 0100 = CTC
//  TCCR1B &= ~(1< //  TCCR1B |=  (1< //  TCCR1A &= ~(1< //  TCCR1A &= ~(1< //
//  // output mode = 00 (disconnected)
//  TCCR1A &= ~(3< //  TCCR1A &= ~(3<  
    //timer3 init 通用定时器
    RCC->APB1ENR|=1<<1;//使能TIMER3时钟
    //设置允许更新中断,必须同时设置了才能更新中断
    TIM3->DIER|=1<<0;//允许中断更新
    TIM3->DIER|=1<<6;//触发中断使能
    //设置NVIC 
    //NVIC_PreemptionPriority:抢占优先级
    //NVIC_SubPriority       :响应优先级
    //NVIC_Channel           :中断编号
    //NVIC_Group             :中断分组 0~4
    //注意优先级不能超过设定的组的范围!否则会有意想不到的错误
    //组划分:
    //组0:0位抢占优先级,4位响应优先级
    //组1:1位抢占优先级,3位响应优先级
    //组2:2位抢占优先级,2位响应优先级
    //组3:3位抢占优先级,1位响应优先级
    //组4:4位抢占优先级,0位响应优先级
    //抢占:低优先级的中断执行的同时高优先级可以将其抢回,高结束后再执行低
    //响应:同时中断时谁先响应(高优先级,数小的)
    //NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先
    //中断分组设置,设置NVIC相关寄存器,使能中断
    //sys.c中,直接调用
    MY_NVIC_Init(1,2,TIM3_IRQChannel,2);    //抢占1,响应2,组2
 
 
  // Configure Timer 2
//  TCCR2A = 0; // Normal operation
//  TCCR2B = 0; // Disable timer until needed.
//  TIMSK2 |= (1< //  #ifdef STEP_PULSE_DELAY
//    TIMSK2 |= (1< //  #endif
    //timer4 init 通用定时器
    RCC->APB1ENR|=1<<2;//使能TIMER4时钟
    //设置允许更新中断,必须同时设置了才能更新中断
    TIM4->DIER|=1<<0;//允许中断更新
    TIM4->DIER|=1<<6;//触发中断使能
    //设置NVIC 
    //NVIC_PreemptionPriority:抢占优先级
    //NVIC_SubPriority       :响应优先级
    //NVIC_Channel           :中断编号
    //NVIC_Group             :中断分组 0~4
    //注意优先级不能超过设定的组的范围!否则会有意想不到的错误
    //组划分:
    //组0:0位抢占优先级,4位响应优先级
    //组1:1位抢占优先级,3位响应优先级
    //组2:2位抢占优先级,2位响应优先级
    //组3:3位抢占优先级,1位响应优先级
    //组4:4位抢占优先级,0位响应优先级
    //抢占:低优先级的中断执行的同时高优先级可以将其抢回,高结束后再执行低
    //响应:同时中断时谁先响应(高优先级,数小的)
    //NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先
    //中断分组设置,设置NVIC相关寄存器,使能中断
    //sys.c中,直接调用
    MY_NVIC_Init(0,1,TIM4_IRQChannel,2);    //抢占0,响应1,组2
 
  // Start in the idle state, but first wake up to check for keep steppers enabled option.
  st_wake_up();
  st_go_idle();
}
第二步:设置定时器4的初值在st_wake_up中计算的,在TIM3的中断服务函数中赋值的
               st_wake_up()主要是开定时器3,计算定时器4的初值
               st_go_idle()主要是关定时器
// Stepper state initialization. Cycle should only start if the st.cycle_start flag is
// enabled. Startup init and limits call this function but shouldn't start the cycle.
void st_wake_up() 
{
  // Enable steppers by resetting the stepper disable port
  if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { 
    //STEPPERS_DISABLE_PORT |= (1<     STEPPERS_DISABLE_EN;
  } else { 
    //STEPPERS_DISABLE_PORT &= ~(1<     STEPPERS_DISABLE_DIS;
  }
  if (sys.state == STATE_CYCLE) {
    // Initialize stepper output bits
    out_bits = (0) ^ (settings.invert_mask); 
    // Initialize step pulse timing from settings. Here to ensure updating after re-writing.
    #ifdef STEP_PULSE_DELAY
      // Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope.
      step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3);
      // Set delay between direction pin write and step command.
      OCR2A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
    #else // Normal operation
      // Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
      //设置一个周期中低电平时间,也就是TIM4的计时时间,因为TIM4中拉高的输出脚,所以它计时的时间其实为拉低时候的延时时间
      //>>3是因为定时器八分频
      //取反因为AVR用的普通定时器模式,计数器从填入的值开始计时直到溢出产生溢出中断,所以填入的值=256-实际的定时值
      //step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
      //张纪宽修改bug,因为STM32的定时器模式跟AVR不同,STM32是从0开始计数,计数到设定值后进入中断,所以不需要取反
       step_pulse_time = ((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3;
    #endif
    // Enable stepper driver interrupt
    //TIMSK1 |= (1<     //bit0=1开启定时器,bit6,5=00边沿,bit4=0向上计数 1向下,bit9,8=00无分频 01=2*  10=4*
    TIM3->CR1|=0X01;
  }
}
// Stepper shutdown
void st_go_idle() 
{
  // Disable stepper driver interrupt
  //TIMSK1 &= ~(1<   //bit0=1关闭定时器
    TIM3->CR1&=~0X01;
  // Disable steppers only upon system alarm activated or by user setting to not be kept enabled.
  if ((settings.stepper_idle_lock_time != 0xff) || bit_istrue(sys.execute,EXEC_ALARM)) {
    // Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
    // stop and not drift from residual inertial forces at the end of the last movement.
    delay_ms(settings.stepper_idle_lock_time);
    if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { 
      //STEPPERS_DISABLE_PORT &= ~(1<       STEPPERS_DISABLE_DIS;
    } else { 
      //STEPPERS_DISABLE_PORT |= (1<       STEPPERS_DISABLE_EN; 
    }   
  }
}
TIM4定时时间计算方法:TIM4控制一个周期中低电平时间的,相当于就是延时时间,因为在TIM4中断函数里拉高了
step引脚,进入此中断服务函数证明定时结束,定时结束将引脚拉高,作用就是延时低电平时间
step_pulse_time = ((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3;
settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS;//setting default中赋值的
  #define DEFAULT_STEP_PULSE_MICROSECONDS 10  //default.h中   这个值代表延时的时间,单位us
因为步进电机靠频率大小控制速度,所以脉宽没要求,只要cpu能检测出来就好了
左移三位代表TIM4设置的8分频

第三步:设置定时器3的赋值
因为定时器三设置的是周期时间,就是电机的速度控制,由于有加减速,这个时间肯定是不定的,通过计算随时改变的,那就是一个函数中通过参数设置,执行一个周期后计算该参数赋给寄存器
//设置TIM3定时时间,也就是一个周期的时间
//参数steps_per_minute其实为频率,次数/min   f=次数/60  每秒钟多少下
//所以其他都是定制,主要就是steps_per_minute的值决定了周期,这里的参数其实就是速度,steps/min,因为脉冲周期最终决定的就是运动速度,在这里把两者联系了起来
static void set_step_events_per_minute(uint32_t steps_per_minute) 
{
  if (steps_per_minute < MINIMUM_STEPS_PER_MINUTE) { steps_per_minute = MINIMUM_STEPS_PER_MINUTE; }
  //st.cycles_per_step_event = config_step_timer((TICKS_PER_MICROSECOND*1000000*60)/steps_per_minute); //(TICKS_PER_MICROSECOND*1000000*60)超出32位,所以改变了下顺序
  //计算定时器填入的值,不是定时时间
  //填入的值=系统每秒钟跳动的次数(次数/s) 除以 电机运动速度(steps/s)
  //注意steps_per_minute/60为steps/s,正好与分子上72MHZ的单位:跳动次数/s相对应,注意这里跳动次数与steps的区别,跳动次数指的是单片机定时计数器每秒钟的跳动次数,steps指的是每秒钟电机需要几个脉冲,相除之后的值就代表每个脉冲单片机跳动次数,正好就是需要填入的值,哇塞太巧妙了
  st.cycles_per_step_event = config_step_timer(TICKS_PER_MICROSECOND*1000000/steps_per_minute*60);
}
// Configures the prescaler and ceiling of timer 1 to produce the given rate as accurately as possible.
// Returns the actual number of cycles per interrupt
//设置周期时间    TIM3的定时时间就是周期时间
static uint32_t config_step_timer(uint32_t cycles)
{
  uint16_t ceiling;
  uint16_t prescaler;
  uint32_t actual_cycles;
  if (cycles <= 0xffffL) {
    ceiling = cycles;
    prescaler = 72-1; // prescaler: 0
    actual_cycles = ceiling;
  } else if (cycles <= 0x7ffffL) {
    ceiling = cycles >> 3;
    prescaler = 72*8-1; // prescaler: 8
    actual_cycles = ceiling * 8L;
  } else if (cycles <= 0x3fffffL) {
    ceiling =  cycles >> 6;
    prescaler = 72*64-1; // prescaler: 64
    actual_cycles = ceiling * 64L;
  } else if (cycles <= 0xffffffL) {
    ceiling =  (cycles >> 8);
    prescaler = 72*256-1; // prescaler: 256
    actual_cycles = ceiling * 256L;
  } else if (cycles <= 0x3ffffffL) {
    ceiling = (cycles >> 10);
    //prescaler = 72*1024-1; // prescaler: 1024    这里超了十六位,设置错误
    prescaler = 65535; //不精确了
    actual_cycles = ceiling * 1024L;    
  } else {
    // Okay, that was slower than we actually go. Just set the slowest speed
    ceiling = 0xffff;
    prescaler = 65535;
    actual_cycles = 0xffff * 1024;
  }
  // Set prescaler    设置时钟分频
  //TCCR1B = (TCCR1B & ~(0x07<   TIM3->PSC=prescaler; //设置分频
  // Set ceiling
  TIM3->ARR=ceiling; //设置计数值
  return(actual_cycles);
}
定时器值=TICKS_PER_MICROSECOND*1000000/steps_per_minute*60   
本来是TICKS_PER_MICROSECOND*1000000*60 /steps_per_minute 由于这样会超出32bit范围,所以修改成上面了
TICKS_PER_MICROSECOND*1000000*60代表每分钟系统时钟跳动次数
steps_per_minute代表电机每分钟需要的脉冲个数,也就是速度
填入的值=系统每秒钟跳动的次数(次数/s) 除以 电机运动速度(steps/s)
  //注意steps_per_minute/60为steps/s,正好与分子上72MHZ的单位:跳动次数/s相对应,注意这里跳动次数与steps的区别,跳动次数指的是单片机定时计数器每秒钟的跳动次数,steps指的是每秒钟电机需要几个脉冲,相除之后的值就代表每个脉冲单片机跳动次数,正好就是需要填入的值,哇塞太巧妙了
static uint32_t config_step_timer(uint32_t cycles)这个函数就是具体的填入值方法,分频和余量的方式
因为很有可能设置时间超出了16位定时器的时间,所以对应分频设置可以设置最大的时间

第四步:两个定时器的中断服务函数,在中断服务函数中进行的参数的赋值,定时器的赋值,定时器的开关等
这是定时器控制最重要的程序
// "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. It is executed at the rate set with
// config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. 
// It is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port after each pulse. 
// The bresenham line tracer algorithm controls all three stepper outputs simultaneously with these two interrupts.
//ISR(TIMER1_COMPA_vect)
//定时器3中断服务函数
/**********中断服务函数完成的工作:
1.TIM3定时时间就是一个周期时间
2.TIM3把信号拉低开始定时接着打开TIM4定时拉低时间,TIM4时间到拉高信号TIM3定时到拉低信号
3.数据操作:计算下一个周期的时间,XYZ的位置,运动的方向,需要的加速度速度等参数
****************************************************************************************/
void TIM3_IRQHandler(void) //中断服务函数名一定正确
{
    if(TIM3->SR&0X0001)//再次判断是否更新事件
    {            
      if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
      /****************************************************************************
        *从这里开始是方向和输出(拉低)控制,开TIM4拉低延时时间定时器,一个周期的开始,TIM3从0开始计数
      ****************************************************************************/
      // Set the direction pins a couple of nanoseconds before we step the steppers
      //STEPPING_PORT = (STEPPING_PORT & ~DIRECTION_MASK) | (out_bits & DIRECTION_MASK);
      //change by zjk
       X_DIRECTION_PORT = (X_DIRECTION_PORT & ~X_DIRECTION_MASK) | (out_bits & X_DIRECTION_MASK);
       Y_DIRECTION_PORT = (Y_DIRECTION_PORT & ~Y_DIRECTION_MASK) | (out_bits & Y_DIRECTION_MASK);
      //end zjk
      // Then pulse the stepping pins
      #ifdef STEP_PULSE_DELAY
        step_bits = (STEPPING_PORT & ~STEP_MASK) | out_bits; // Store out_bits to prevent overwriting.
      #else  // Normal operation
        //STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | out_bits;
        //change by zjk
        X_STEP_PORT = (X_STEP_PORT & ~X_STEP_MASK) | (out_bits& X_STEP_MASK);
        Y_STEP_PORT = (Y_STEP_PORT & ~Y_STEP_MASK) | (out_bits& Y_STEP_MASK);
        //end zjk
      #endif
      // Enable step pulse reset timer so that The Stepper Port Reset Interrupt can reset the signal after
      // exactly settings.pulse_microseconds microseconds, independent of the main Timer1 prescaler.
//      TCNT2 = step_pulse_time; // Reload timer counter
//      TCCR2B = (1<       //设置TIM4为八分频,TIM4开始计时
      TIM4->PSC=72*8-1; //设置分频 八分频
        TIM4->ARR=step_pulse_time; //设置计数值
 
      busy = true;
      // Re-enable interrupts to allow ISR_TIMER2_OVERFLOW to trigger on-time and allow serial communications
      // regardless of time in this handler. The following code prepares the stepper driver for the next
      // step interrupt compare and will always finish before returning to the main program.
      //sei(); //开中断
      //bit0=1开启定时器,bit6,5=00边沿,bit4=0向上计数 1向下,bit9,8=00无分频 01=2*  10=4*
      TIM4->CR1|=0X01;
      /**********************************************************************************
        从这里往下就是数据的操作
      **********************************************************************************/
      // If there is no current block, attempt to pop one from the buffer
      if (current_block == NULL) {
        // Anything in the buffer? If so, initialize next motion.
        //从主线程中获取信息,在中断服务函数中计算
        current_block = plan_get_current_block();
        /*********************
        数据操作1:计算下个周期大小
        *********************/
        if (current_block != NULL) {
          if (sys.state == STATE_CYCLE) {
            // During feed hold, do not update rate and trap counter. Keep decelerating.
            //st.trapezoid_adjusted_rate决定周期大小,(st.trapezoid_adjusted_rate/60)代表速度
            st.trapezoid_adjusted_rate = current_block->initial_rate;//初始速度,也就是启动时候的速度
            set_step_events_per_minute(st.trapezoid_adjusted_rate); // Initialize cycles_per_step_event
            st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule.
          }
          st.min_safe_rate = current_block->rate_delta + (current_block->rate_delta >> 1); // 1.5 x rate_delta
          st.counter_x = -(current_block->step_event_count >> 1);
          st.counter_y = st.counter_x;
          st.counter_z = st.counter_x;
          st.event_count = current_block->step_event_count;
          st.step_events_completed = 0;     
        } else {
          st_go_idle();
          bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end
        }    
      } 
      /*********************
        数据操作2:计算位置信息
      *********************/
      if (current_block != NULL) {
        // Execute step displacement profile by bresenham line algorithm
        out_bits = current_block->direction_bits;
        st.counter_x += current_block->steps_x;
        if (st.counter_x > 0) {
          out_bits |= (1<           st.counter_x -= st.event_count;
          if (out_bits & (1<           else { sys.position[X_AXIS]++; }
        }
        st.counter_y += current_block->steps_y;
        if (st.counter_y > 0) {
          out_bits |= (1<           st.counter_y -= st.event_count;
          if (out_bits & (1<           else { sys.position[Y_AXIS]++; }
        }
    //    st.counter_z += current_block->steps_z;
    //    if (st.counter_z > 0) {
    //      out_bits |= (1<     //      st.counter_z -= st.event_count;
    //      if (out_bits & (1<     //      else { sys.position[Z_AXIS]++; }
    //    }
        
        st.step_events_completed++; // Iterate step events
        /*********************
        数据操作3:由加速度计算下一个周期大小,步进电机周期决定速度
        *********************/
        // While in block steps, check for de/ac-celeration events and execute them accordingly.
        if (st.step_events_completed < current_block->step_event_count) {
          if (sys.state == STATE_HOLD) {
            // Check for and execute feed hold by enforcing a steady deceleration from the moment of 
            // execution. The rate of deceleration is limited by rate_delta and will never decelerate
            // faster or slower than in normal operation. If the distance required for the feed hold 
            // deceleration spans more than one block, the initial rate of the following blocks are not
            // updated and deceleration is continued according to their corresponding rate_delta.
            // NOTE: The trapezoid tick cycle counter is not updated intentionally. This ensures that 
            // the deceleration is smooth regardless of where the feed hold is initiated and if the
            // deceleration distance spans multiple blocks.
            if ( iterate_trapezoid_cycle_counter() ) {                    
              // If deceleration complete, set system flags and shutdown steppers.
              if (st.trapezoid_adjusted_rate <= current_block->rate_delta) {
                // Just go idle. Do not NULL current block. The bresenham algorithm variables must
                // remain intact to ensure the stepper path is exactly the same. Feed hold is still
                // active and is released after the buffer has been reinitialized.
                st_go_idle();
                bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program that feed hold is complete.
              } else {
                st.trapezoid_adjusted_rate -= current_block->rate_delta;
                set_step_events_per_minute(st.trapezoid_adjusted_rate);
              }      
            }
            
          } else {
            // The trapezoid generator always checks step event location to ensure de/ac-celerations are 
            // executed and terminated at exactly the right time. This helps prevent over/under-shooting
            // the target position and speed. 
            // NOTE: By increasing the ACCELERATION_TICKS_PER_SECOND in config.h, the resolution of the 
            // discrete velocity changes increase and accuracy can increase as well to a point. Numerical 
            // round-off errors can effect this, if set too high. This is important to note if a user has 
            // very high acceleration and/or feedrate requirements for their machine.
            if (st.step_events_completed < current_block->accelerate_until) {
              // Iterate cycle counter and check if speeds need to be increased.
              if ( iterate_trapezoid_cycle_counter() ) {
                st.trapezoid_adjusted_rate += current_block->rate_delta;
                if (st.trapezoid_adjusted_rate >= current_block->nominal_rate) {
                  // Reached nominal rate a little early. Cruise at nominal rate until decelerate_after.
                  st.trapezoid_adjusted_rate = current_block->nominal_rate;
                }
                set_step_events_per_minute(st.trapezoid_adjusted_rate);
              }
            } else if (st.step_events_completed >= current_block->decelerate_after) {
              // Reset trapezoid tick cycle counter to make sure that the deceleration is performed the
              // same every time. Reset to CYCLES_PER_ACCELERATION_TICK/2 to follow the midpoint rule for
              // an accurate approximation of the deceleration curve. For triangle profiles, down count
              // from current cycle counter to ensure exact deceleration curve.
              if (st.step_events_completed == current_block-> decelerate_after) {
                if (st.trapezoid_adjusted_rate == current_block->nominal_rate) {
                  st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Trapezoid profile
                } else {  
                  st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK-st.trapezoid_tick_cycle_counter; // Triangle profile
                }
              } else {
                // Iterate cycle counter and check if speeds need to be reduced.
                if ( iterate_trapezoid_cycle_counter() ) {  
                  // NOTE: We will only do a full speed reduction if the result is more than the minimum safe 
                  // rate, initialized in trapezoid reset as 1.5 x rate_delta. Otherwise, reduce the speed by
                  // half increments until finished. The half increments are guaranteed not to exceed the 
                  // CNC acceleration limits, because they will never be greater than rate_delta. This catches
                  // small errors that might leave steps hanging after the last trapezoid tick or a very slow
                  // step rate at the end of a full stop deceleration in certain situations. The half rate 
                  // reductions should only be called once or twice per block and create a nice smooth 
                  // end deceleration.
                  if (st.trapezoid_adjusted_rate > st.min_safe_rate) {
                    st.trapezoid_adjusted_rate -= current_block->rate_delta;
                  } else {
                    st.trapezoid_adjusted_rate >>= 1; // Bit shift divide by 2
                  }
                  if (st.trapezoid_adjusted_rate < current_block->final_rate) {
                    // Reached final rate a little early. Cruise to end of block at final rate.
                    st.trapezoid_adjusted_rate = current_block->final_rate;
                  }
                  set_step_events_per_minute(st.trapezoid_adjusted_rate);
                }
              }
            } else {
              // No accelerations. Make sure we cruise exactly at the nominal rate.
              if (st.trapezoid_adjusted_rate != current_block->nominal_rate) {
                st.trapezoid_adjusted_rate = current_block->nominal_rate;
                set_step_events_per_minute(st.trapezoid_adjusted_rate);
              }
            }
          }            
        } else {   
          // If current block is finished, reset pointer 
          current_block = NULL;
          plan_discard_current_block();
        }
      }
      out_bits ^= settings.invert_mask;  // Apply step and direction invert mask    
      busy = false;
    }
  TIM3->SR&=~(1<<0); //状态寄存器清除
}
 
// This interrupt is set up by ISR_TIMER1_COMPAREA when it sets the motor port bits. It resets
// the motor port after a short period (settings.pulse_microseconds) completing one step cycle.
// NOTE: Interrupt collisions between the serial and stepper interrupts can cause delays by
// a few microseconds, if they execute right before one another. Not a big deal, but can
// cause issues at high step rates if another high frequency asynchronous interrupt is 
// added to Grbl.
//ISR(TIMER2_OVF_vect)
//定时器4中断服务函数
void TIM4_IRQHandler(void) //中断服务函数名一定正确
{
    if(TIM4->SR&0X0001)//再次判断是否更新事件
    {
 
      // Reset stepping pins (leave the direction pins)
      //STEPPING_PORT = (STEPPING_PORT & ~STEP_MASK) | (settings.invert_mask & STEP_MASK); 
      X_STEP_PORT = (X_STEP_PORT & ~X_STEP_MASK) | (settings.invert_mask & X_STEP_MASK);
      Y_STEP_PORT = (Y_STEP_PORT & ~Y_STEP_MASK) | (settings.invert_mask & Y_STEP_MASK);
      //TCCR2B = 0; // Disable Timer2 to prevent re-entering this interrupt when it's not needed. 
      //bit0=1关闭定时器
      TIM4->CR1&=~0X01;
    }
    TIM4->SR&=~(1<<0); //状态寄存器清除
}
在定时器3的中断服务函数中开的定时器4,定时器4中关的自己,一条G代码执行完关闭的定时器3
由此可见,定时器4是通过抢占的方式中断的,所以要设置定时器4的中断优先级要高于TIM3
定时器3的定时间是从主程序里获得赋值到st.trapezoid_adjusted_rate的,这个值就代表时刻变化着的速度
这里速度是如何实时计算的,有待于好好研究,因为速度是变化的,是梯形状的,是启动停止时候缓慢改变的
 ———————————————— 
版权声明:本文为CSDN博主「zhangjikuan」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhangjikuan/article/details/46697657

你可能感兴趣的:(GRBL五:定时器控制策略解析(转载))