因初学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<
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<
// // output mode = 00 (disconnected)
// 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<
// TIMSK2 |= (1<
//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<
} else {
//STEPPERS_DISABLE_PORT &= ~(1<
}
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<
TIM3->CR1|=0X01;
}
}
// Stepper shutdown
void st_go_idle()
{
// Disable stepper driver interrupt
//TIMSK1 &= ~(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<
} else {
//STEPPERS_DISABLE_PORT |= (1<
}
}
}
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<
// 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->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<
if (out_bits & (1<
}
st.counter_y += current_block->steps_y;
if (st.counter_y > 0) {
out_bits |= (1<
if (out_bits & (1<
}
// st.counter_z += current_block->steps_z;
// if (st.counter_z > 0) {
// out_bits |= (1<
// if (out_bits & (1<
// }
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