| 
 
| GRBL五:定时器控制策略解析本文讲述的是根据grbl原始程序两个定时器控制脉冲输出的方式,由于在stm32上有PWM输出功能,所以可以用一个定时器的方式输出PWM来控制脉冲(因为pwm的占空比不影响步进速度,只需要固定占空就好,值修改脉冲周期就OK了),可以节约一个定时器,关于这种设计方式我只是有这个想法,本文未作实践。 GRBL的脉冲输出靠两个定时器协同控制输出的
 具体控制策略:第一个定时器控制脉冲周期(因为步进电机脉冲周期决定速度)
 第二个定时器控制一个周期中低电平的时间(脉冲宽度不重要,只要CPU能检测的到就好),相当于延时
 流程如下图所示
 
  
 所有代码都在stepper中定义
 第一步:初始化引脚和定时器
 在初始化定时器中没有给两个定时器设置分频和初值,也没有打开定时器开关
 用的STM32通用16位定时器3和4
 [cpp] view plain copy
 
 
 // Initialize and start the stepper motor subsystemvoid 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 shutdownvoid 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);}
 
 
 
 | 
 |