打印
[开源硬件]

GRBL五:STM32代码移植——定时器控制策略解析

[复制链接]
3720|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
TXQDM|  楼主 | 2017-7-8 13:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
        GRBL五:定时器控制策略解析本文讲述的是根据grbl原始程序两个定时器控制脉冲输出的方式,由于在stm32上有PWM输出功能,所以可以用一个定时器的方式输出PWM来控制脉冲(因为pwm的占空比不影响步进速度,只需要固定占空就好,值修改脉冲周期就OK了),可以节约一个定时器,关于这种设计方式我只是有这个想法,本文未作实践。
GRBL的脉冲输出靠两个定时器协同控制输出的
具体控制策略:第一个定时器控制脉冲周期(因为步进电机脉冲周期决定速度)
                         第二个定时器控制一个周期中低电平的时间(脉冲宽度不重要,只要CPU能检测的到就好),相当于延时
流程如下图所示


所有代码都在stepper中定义
第一步:初始化引脚和定时器
在初始化定时器中没有给两个定时器设置分频和初值,也没有打开定时器开关
用的STM32通用16位定时器3和4
[cpp] view plain copy

  • // 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<<STEPPERS_DISABLE_BIT;
  •   //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<<WGM13);
  • //  TCCR1B |=  (1<<WGM12);
  • //  TCCR1A &= ~(1<<WGM11);
  • //  TCCR1A &= ~(1<<WGM10);
  • //
  • //  // output mode = 00 (disconnected)
  • //  TCCR1A &= ~(3<<COM1A0);
  • //  TCCR1A &= ~(3<<COM1B0);
  •     //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<<TOIE2); // Enable Timer2 Overflow interrupt
  • //  #ifdef STEP_PULSE_DELAY
  • //    TIMSK2 |= (1<<OCIE2A); // Enable Timer2 Compare Match A interrupt
  • //  #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()主要是关定时器
[cpp] view plain copy

  • // 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_BIT);
  •     STEPPERS_DISABLE_EN;
  •   } else {
  •     //STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
  •     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<<OCIE1A);
  •     //bit0=1开启定时器,bit6,5=00边沿,bit4=0向上计数 1向下,bit9,8=00无分频 01=2*  10=4*
  •     TIM3->CR1|=0X01;
  •   }
  • }

[cpp] view plain copy

  • // Stepper shutdown
  • void st_go_idle()
  • {
  •   // Disable stepper driver interrupt
  •   //TIMSK1 &= ~(1<<OCIE1A);
  •   //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_BIT);
  •       STEPPERS_DISABLE_DIS;
  •     } else {
  •       //STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT);
  •       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的赋值
因为定时器三设置的是周期时间,就是电机的速度控制,由于有加减速,这个时间肯定是不定的,通过计算随时改变的,那就是一个函数中通过参数设置,执行一个周期后计算该参数赋给寄存器
[cpp] view plain copy

  • //设置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);
  • }

[cpp] view plain copy

  • // 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<<CS10)) | (prescaler<<CS10);
  •   TIM3->PSC=prescaler; //设置分频
  •   // Set ceiling
  •   TIM3->ARR=ceiling; //设置计数值
  •   return(actual_cycles);
  • }



相关帖子

沙发
TXQDM|  楼主 | 2017-7-8 13:29 | 只看该作者
定时器值=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代表电机每分钟需要的脉冲个数,也就是速度
[cpp] view plain copy

  • 填入的值=系统每秒钟跳动的次数(次数/s) 除以 电机运动速度(steps/s)  
  •   //注意steps_per_minute/60为steps/s,正好与分子上72MHZ的单位:跳动次数/s相对应,注意这里跳动次数与steps的区别,跳动次数指的是单片机定时计数器每秒钟的跳动次数,steps指的是每秒钟电机需要几个脉冲,相除之后的值就代表每个脉冲单片机跳动次数,正好就是需要填入的值,哇塞太巧妙了  


static uint32_t config_step_timer(uint32_t cycles)这个函数就是具体的填入值方法,分频和余量的方式

因为很有可能设置时间超出了16位定时器的时间,所以对应分频设置可以设置最大的时间


第四步:两个定时器的中断服务函数,在中断服务函数中进行的参数的赋值,定时器的赋值,定时器的开关等
这是定时器控制最重要的程序
[cpp] view plain copy

  • // "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<<CS21); // Begin timer2. Full speed, 1/8 prescaler  
  •       //设置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<<X_STEP_BIT);  
  •           st.counter_x -= st.event_count;  
  •           if (out_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }  
  •           else { sys.position[X_AXIS]++; }  
  •         }  
  •         st.counter_y += current_block->steps_y;  
  •         if (st.counter_y > 0) {  
  •           out_bits |= (1<<Y_STEP_BIT);  
  •           st.counter_y -= st.event_count;  
  •           if (out_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }  
  •           else { sys.position[Y_AXIS]++; }  
  •         }  
  •     //    st.counter_z += current_block->steps_z;  
  •     //    if (st.counter_z > 0) {  
  •     //      out_bits |= (1<<Z_STEP_BIT);  
  •     //      st.counter_z -= st.event_count;  
  •     //      if (out_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }  
  •     //      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的,这个值就代表时刻变化着的速度
这里速度是如何实时计算的,有待于好好研究,因为速度是变化的,是梯形状的,是启动停止时候缓慢改变的


   
                        

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

110

主题

593

帖子

11

粉丝