读书的时候受导师和专业的影响吧,对机床挺感兴趣的。今天开始,就聊一下国外的开源数控项目grbl。
早在16年的时候就有听说过gbl,无奈当时自己嵌入式水平太差,没能玩得转,后来就不了了之了。2021年,自己重新阅读了一番grbl源码,进行了仔细研究,也作了些笔记。我打算在博客上把自己的内容重新整理一下,分享给大家。说的不对地方,大家请指正。我建了一个qq群(966403026),有兴趣的话可以进群讨论。
我研究的grbl版本为0.8和1.1f,两个版本的架构并没有发生变化,只是新版本支持了jog运动、探针等新功能。推荐大家从0.8版本开始看,有了一个大致框架后,再看1.1版本的。因为新版本的grbl功能更多了,变量也更多了,会产生一些混淆,读起来麻烦。
我打算基于0.8版本开始讲解,因为这对介绍一个CNC系统完全够了。我去年写了3篇博客,介绍了grbl的速度前瞻和圆弧插补,是基于1.1版本讲解的:
grbl源码解析——圆弧插补
grbl源码解析——速度前瞻(1)
grbl源码解析——速度前瞻(2)
自己也把grbl0.8版本移植到了stm32f407上,我设置了免积分下载,有兴趣的话可以下载来看:
grbl0.8_stm32

软件架构

在这里插入图片描述

Bresenham算法

grbl的基本功能包括G代码解析、直线圆弧插补、速度前瞻、加减速算法、脉冲产生算法、串口通讯、通讯协议解析等组成。阅读源码的时候,大多数代码都很容易的,让我耗时最多的其实是脉冲产生算法和速度前瞻部分。我读书时接触的直线圆弧插补算法为时间分割算法,算出每个插补周期(假设为1ms)需要移动的位移量就行,只需要一个周期为1ms的定时器就行(需要fpga发送脉冲、或者走ethercat)。然而grbl却用了两个定时器来产生脉冲信号,这就把我整蒙了。也许是思维惯性的原因吧,grbl的脉冲产生原理我琢磨了好久才想明白了。
grbl的脉冲产生代码位于stepper.c,其运用了Bresenham算法。Bresenham算法是计算机图形学的基础内容,其实我阅读了计算机图形学中的相关内容,才把grbl的脉冲产生原理搞懂的。
大家可以百度或者B站搜“Bresenham。

// Stepper state variable. Contains running data and trapezoid variables.
typedef struct {
  // Used by the bresenham line algorithm
  int32_t counter_x,        // Counter variables for the bresenham line tracer
          counter_y, 
          counter_z;
  uint32_t event_count;
  uint32_t step_events_completed;  // The number of step events left in current motion

  // Used by the trapezoid generator
  uint32_t cycles_per_step_event;        // The number of machine cycles between each step event
  uint32_t trapezoid_tick_cycle_counter; // The cycles since last trapezoid_tick. Used to generate ticks at a steady
                                              // pace without allocating a separate timer
  uint32_t trapezoid_adjusted_rate;      // The current rate of step_events according to the trapezoid generator
  uint32_t min_safe_rate;  // Minimum safe rate for full deceleration rate reduction step. Otherwise halves step_rate.
} stepper_t;

stepper_t结构体里就是用于记录脉冲数据和t型加减速变量的。
其中与Bresenham相关的变量为counter_x、counter_y、counter_z、event_count、step_events_completed。

// This struct is used when buffering the setup for each linear movement "nominal" values are as specified in 
// the source g-code and may never actually be reached if acceleration management is active.
typedef struct {

  // Fields used by the bresenham algorithm for tracing the line
  uint8_t  direction_bits;            // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
  uint32_t steps_x, steps_y, steps_z; // Step count along each axis
  int32_t  step_event_count;          // The number of step events required to complete this block

  // Fields used by the motion planner to manage acceleration
  float nominal_speed;               // The nominal speed for this block in mm/min  
  float entry_speed;                 // Entry speed at previous-current block junction in mm/min
  float max_entry_speed;             // Maximum allowable junction entry speed in mm/min
  float millimeters;                 // The total travel of this block in mm
  uint8_t recalculate_flag;           // Planner flag to recalculate trapezoids on entry junction
  uint8_t nominal_length_flag;        // Planner flag for nominal speed always reached

  // Settings for the trapezoid generator
  uint32_t initial_rate;              // The step rate at start of block  
  uint32_t final_rate;                // The step rate at end of block
  int32_t rate_delta;                 // The steps/minute to add or subtract when changing speed (must be positive)
  uint32_t accelerate_until;          // The index of the step event on which to stop acceleration
  uint32_t decelerate_after;          // The index of the step event on which to start decelerating
  uint32_t nominal_rate;              // The nominal step rate for this block in step_events/minute

} block_t;

该结构体是速度规划缓存,其中与Bresenham相关的变量为steps_x, steps_y, steps_z,step_event_count和direction_bits。
Bresenham算法如下

 // 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();
    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 = 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
    }    
  } 

  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

    // While in block steps, check for de/ac-celeration events and execute them accordingly.
    if (st.step_events_completed < current_block->step_event_count) {
    	……
    }

假设x、y、z方向的位移量为(4,5,6),其存储于steps_x, steps_y, steps_z
。我们可以不用考虑正负值,因为步进电机由方向引脚和脉冲引脚两个输入端构成,位移量的正负由方向引脚的高低电平来表示,因此在grbl的Bresenham算法中,只考虑正值就行了。
首先是变量的初始化

      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;

即(counter_x ,counter_y ,counter_z)=(-3,-3,-3)
current_block->step_event_count = max(4,5,6)=6;其位于plan_buffer_line函数中

void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate) 
{
  // Prepare to set up new block
  block_t *block = &block_buffer[block_buffer_head];

  // Calculate target position in absolute steps
  int32_t target[3];
  target[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]);
  target[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]);
  target[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]);     

  // Compute direction bits for this block
  block->direction_bits = 0;
  if (target[X_AXIS] < pl.position[X_AXIS]) { block->direction_bits |= (1<<X_DIRECTION_BIT); }
  if (target[Y_AXIS] < pl.position[Y_AXIS]) { block->direction_bits |= (1<<Y_DIRECTION_BIT); }
  if (target[Z_AXIS] < pl.position[Z_AXIS]) { block->direction_bits |= (1<<Z_DIRECTION_BIT); }
  
  // Number of steps for each axis
  block->steps_x = labs(target[X_AXIS]-pl.position[X_AXIS]);
  block->steps_y = labs(target[Y_AXIS]-pl.position[Y_AXIS]);
  block->steps_z = labs(target[Z_AXIS]-pl.position[Z_AXIS]);
  block->step_event_count = max(block->steps_x, max(block->steps_y, block->steps_z));

  // Bail if this is a zero-length block
  if (block->step_event_count == 0) { return; };

接着依据下列代码开始循环运算,计算脉冲

  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

其计算流程如下
在这里插入图片描述
当step_events_completed值与step_event_count 相等时,则需要对缓存块重新初始化

// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by 
// runtime command execution in the main program, ensuring that the planner re-plans safely.
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
// Only the planner de/ac-celerations profiles and stepper rates have been updated.
void st_cycle_reinitialize()
{
  if (current_block != NULL) {
    // Replan buffer from the feed hold stop location.
    plan_cycle_reinitialize(current_block->step_event_count - st.step_events_completed);
    // Update initial rate and timers after feed hold.
    st.trapezoid_adjusted_rate = 0; // Resumes from rest
    set_step_events_per_minute(st.trapezoid_adjusted_rate);
    st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule.
    st.step_events_completed = 0;
    sys.state = STATE_QUEUED;
  } else {
    sys.state = STATE_IDLE;
  }
}

脉冲产生

脉冲产生一共需要用到两个定时器,一个控制运算周期(TIm3)、一个控制脉宽(TIm4)。我自己写了一个demo(基于stm32)用于测试Bresenham算法。
定时器初始化代码如下

void TIM3_Init(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	TIM_TimeBaseStructure.TIM_Period = 2000-1;
	TIM_TimeBaseStructure.TIM_Prescaler = 8400-1;		// 10kHz
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	// 清除定时器更新中断标志位
	TIM_ClearFlag(TIM3, TIM_FLAG_Update);
	// 开启定时器更新中断
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
	
	TIM_Cmd(TIM3, DISABLE);
}

void TIM4_Init(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	
	TIM_TimeBaseStructure.TIM_Period = 10000-1;
	TIM_TimeBaseStructure.TIM_Prescaler = 840-1;		// 1000kHz
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	// 清除定时器更新中断标志位
	TIM_ClearFlag(TIM4, TIM_FLAG_Update);
	// 开启定时器更新中断
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
	
	TIM_Cmd(TIM4, DISABLE);
}

Bresenham算法移植到stm32

// Step and direction port invert masks.
static uint8_t step_port_invert_mask = ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT));
static uint8_t dir_port_invert_mask = ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT));

struct Line {
	uint32_t steps_x, steps_y, steps_z;
	int32_t maximum_steps;
	uint8_t direction_bits;
	uint32_t rate;
};
struct Line line_temp;
struct Line *current_line = 0; 
volatile int32_t counter_x, counter_y, counter_z; // counter variables for the bresenham line tracer
uint32_t iterations; // The number of iterations left to complete the current_line
uint8_t out_bits = 0; // The next stepping-bits to be output

uint8_t axis0 = 0;
uint8_t axis1 = 0;
uint8_t axis2 = 0;

void st_buffer_line(int32_t steps_x, int32_t steps_y, int32_t steps_z, uint32_t microseconds)
{
	struct Line *line = &line_temp;
	line->steps_x = labs(steps_x);
	line->steps_y = labs(steps_y);
	line->steps_z = labs(steps_z); 
	line->maximum_steps = max(line->steps_x, max(line->steps_y, line->steps_z));
	// Bail if this is a zero-length line
	if (line->maximum_steps == 0) { return; };
	line->rate = microseconds/line->maximum_steps;
	uint8_t direction_bits = 0;
	if (steps_x < 0) { direction_bits |= (1<<X_DIRECTION_BIT); }
	if (steps_y < 0) { direction_bits |= (1<<Y_DIRECTION_BIT); }
	if (steps_z < 0) { direction_bits |= (1<<Z_DIRECTION_BIT); }
	line->direction_bits = direction_bits;
	
	current_line = &line_temp;
	counter_x = -(current_line->maximum_steps >> 1);
	counter_y = counter_x;
	counter_z = counter_x;
	iterations = current_line->maximum_steps;
	
	TIM_Cmd(TIM3, ENABLE);
}



void Stepper_MainISR(void)
{
	
	if(out_bits & (1<<X_STEP_BIT))
    {
        if(step_port_invert_mask & (1<<X_STEP_BIT))
        {
            GPIO_SetBits(GPIO_STEP_X_PORT, GPIO_STEP_X_PIN);
			axis0 = 1;
        }
        else
        {
            GPIO_ResetBits(GPIO_STEP_X_PORT, GPIO_STEP_X_PIN);
			axis0 = 0;
        }
    }
    if(out_bits & (1<<Y_STEP_BIT))
    {
        if(step_port_invert_mask & (1<<Y_STEP_BIT))
        {
            GPIO_SetBits(GPIO_STEP_Y_PORT, GPIO_STEP_Y_PIN);
			axis1 = 1;
        }
        else
        {
            GPIO_ResetBits(GPIO_STEP_Y_PORT, GPIO_STEP_Y_PIN);
			axis1 = 0;
        }

    }
    if(out_bits & (1<<Z_STEP_BIT))
    {
        if(step_port_invert_mask & (1<<Z_STEP_BIT))
        {
            GPIO_SetBits(GPIO_STEP_Z_PORT, GPIO_STEP_Z_PIN);
			axis2 = 1;
        }
        else
        {
            GPIO_ResetBits(GPIO_STEP_Z_PORT, GPIO_STEP_Z_PIN);
			axis2 = 0;
        }
    }
	
	TIM_Cmd(TIM4, ENABLE);
    //Bresenham算法
	if (current_line != 0) 
	{
		out_bits = current_line->direction_bits;
		counter_x += current_line->steps_x;
		if (counter_x > 0) {
			out_bits |= (1<<X_STEP_BIT);
			counter_x -= current_line->maximum_steps;
		}
		counter_y += current_line->steps_y;
		if (counter_y > 0) {
			out_bits |= (1<<Y_STEP_BIT);
			counter_y -= current_line->maximum_steps;
		}
		counter_z += current_line->steps_z;
		if (counter_z > 0) {
			out_bits |= (1<<Z_STEP_BIT);
			counter_z -= current_line->maximum_steps;
		}
		// If current line is finished, reset pointer 
		iterations -= 1;
		if (iterations <= 0) {
			current_line = 0;   		
		}
	}
	else 
	{
		out_bits = 0;
		TIM_Cmd(TIM3, DISABLE);	
	}
	
	//out_bits ^= 0;
}


void Stepper_PortResetISR(void)
{
    // Reset stepping pins (leave the direction pins)

    if(step_port_invert_mask & (1<<X_STEP_BIT))
    {
        GPIO_ResetBits(GPIO_STEP_X_PORT, GPIO_STEP_X_PIN);
		axis0 = 0;
    }
    else
    {
        GPIO_SetBits(GPIO_STEP_X_PORT, GPIO_STEP_X_PIN);
		axis0 = 1;
    }

    if(step_port_invert_mask & (1<<Y_STEP_BIT))
    {
        GPIO_ResetBits(GPIO_STEP_Y_PORT, GPIO_STEP_Y_PIN);
		axis1 = 0;
    }
    else
    {
        GPIO_SetBits(GPIO_STEP_Y_PORT, GPIO_STEP_Y_PIN);
		axis1 = 1;
    }

    if(step_port_invert_mask & (1<<Z_STEP_BIT))
    {
        GPIO_ResetBits(GPIO_STEP_Z_PORT, GPIO_STEP_Z_PIN);
		axis2 = 0;
    }
    else
    {
        GPIO_SetBits(GPIO_STEP_Z_PORT, GPIO_STEP_Z_PIN);
		axis2 = 1;
    }
	
	TIM_Cmd(TIM4, DISABLE);
}

中断服务函数

void TIM3_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
    {
		Stepper_MainISR();
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
	}
}

void TIM4_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
		Stepper_PortResetISR();
		TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
	}
}

demo的主函数

int main(void)
{
	//初始化延时函数
	delay_init(168); 
	// 设置中断组为0
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);	
	/* LED 端口初始化 */
	LED_GPIO_Config();	 
	/*初始化按键*/
	Key_GPIO_Config();
	//Configure step and direction interface pins
    GPIO_InitGPIO(GPIO_STEPPER);
	
	TIM3_Init();
	TIM4_Init();
	TIM_Cmd(TIM3, ENABLE);
	TIM_Cmd(TIM4, ENABLE);
	
	/* 轮询按键状态,若按键按下则反转LED */ 
	while(1)                            
	{	   
		st_buffer_line(1,2,3,0);
		delay_ms(2000);
		st_buffer_line(3,3,3,0);
		delay_ms(2000);
		st_buffer_line(2,4,6,0);
		delay_ms(2000);
	}
}

实验效果
在这里插入图片描述
tim3的中断为200ms触发一次(即pwm频率最高为5hz),tim4的中断为100ms触发一次(即pwm高电平持续100ms)。因此tim3决定pwm的频率。

pwm频率

上文提到,tim3决定了pwm的频率。TIM3->ARR = arr和TIM3->PSC=psc决定了tim3的频率。频率=84m/(arr*psc)。
注:arr为16位寄存器、psc为16位寄存器

st_buffer_line(1,2,10,500000);

line->rate = microseconds/line->maximum_steps;

config_step_timer(current_line->rate);

st_buffer_line(1,2,10,500000)表示发送10个脉冲,需要500000us。即发送x个脉冲,需要y us。可求出pwm频率=xm/y hz。
由频率=x
m/y hz 和 频率=84m/(arrpsc)。可得出arrpsc = 84*y/x = 84 * line->rate 。
因此,改写config_step_timer()函数。

// 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
//cycles 的单位是 cpu ticks per step即每个脉冲占用多少个cpu周期
//这个函数可以实现的最高pwm频率为1000khz(理论值),即TIM3->PSC=84-1、TIM3->ARR=1-1
//假设步进电机最高转速为3r/s、32细分。即pwm频率=3*32*200=19200hz。即每个脉冲为52.1us。16细分为104.2us。
//cycles的单位是microseconds/step
static uint32_t config_step_timer(uint32_t cycles)
{
  uint16_t ceiling;
  uint16_t prescaler;
  uint32_t actual_cycles;
  if (cycles <= 0xffffL) { 					//65536
    ceiling = cycles;
    prescaler = 1; // prescaler: 不分频
    actual_cycles = ceiling;
  } else if (cycles <= 0x7ffffL) { 			//65536*8
    ceiling = cycles >> 3;
    prescaler = 8; // prescaler: 8分频
    actual_cycles = ceiling * 8L;
  } else if (cycles <= 0x3fffffL) {			//65536*64
    ceiling =  cycles >> 6;
    prescaler = 64; // prescaler: 64分频
    actual_cycles = ceiling * 64L;
  } else if (cycles <= 0xffffffL) {			//65536*256
    ceiling =  (cycles >> 8);
    prescaler = 256; // prescaler: 256分频
    actual_cycles = ceiling * 256L;
  } else if (cycles <= 0x3ffffffL) {		//65536*1024
    ceiling = (cycles >> 10);
    prescaler = 1024; // prescaler: 1024分频
    actual_cycles = ceiling * 1024L;    
  } else {
    // Okay, that was slower than we actually go. Just set the slowest speed
    //超出1024的分频范围的话就用最长的定时,即最慢的速度,由于psc寄存器是16位的,65536/84=780.2。因此顶多780*84分频。
    ceiling = 0xffff;
    prescaler = 1024;
    actual_cycles = 0xffff * 1024;
  }
  // Set prescaler,avr的定时器为16mhz,stm32的tim3为84mhz
  //由公式可得arr*psc = 84*y/x = 84 * line->rate 。 将84赋值给TIM3->PSC、将rate(即cycles)赋值给TIM3->ARR
  TIM3->PSC = prescaler * 84 - 1;
  TIM3->ARR = ceiling-1;		//计数器自动重装值
  //设定pwm的占空比为周期的一半,当然也可以设定为1个固定值,比如10us。
  TIM4->PSC = TIM3->PSC;
  TIM4->ARR = (ceiling>>1) - 1;
  return(actual_cycles);
}

void st_buffer_line(int32_t steps_x, int32_t steps_y, int32_t steps_z, uint32_t microseconds)
{
	struct Line *line = &line_temp;
	line->steps_x = labs(steps_x);
	line->steps_y = labs(steps_y);
	line->steps_z = labs(steps_z); 
	line->maximum_steps = max(line->steps_x, max(line->steps_y, line->steps_z));
	// Bail if this is a zero-length line
	if (line->maximum_steps == 0) { return; };
	line->rate = microseconds/line->maximum_steps;
	uint8_t direction_bits = 0;
	if (steps_x < 0) { direction_bits |= (1<<X_DIRECTION_BIT); }
	if (steps_y < 0) { direction_bits |= (1<<Y_DIRECTION_BIT); }
	if (steps_z < 0) { direction_bits |= (1<<Z_DIRECTION_BIT); }
	line->direction_bits = direction_bits;
	
	current_line = &line_temp;
	config_step_timer(current_line->rate);
	counter_x = -(current_line->maximum_steps >> 1);
	counter_y = counter_x;
	counter_z = counter_x;
	iterations = current_line->maximum_steps;
	
	TIM_Cmd(TIM3, ENABLE);
}

实验结果

		st_buffer_line(1,2,10,500000);//最高频率为50ms,即20hz
		delay_ms(1000);
		st_buffer_line(1,5,10,500);//最高频率为50us,即20khz
		delay_ms(1000);
		st_buffer_line(5,6,7,7000000);//最高频率为1000000us,即1hz
		delay_ms(10000);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实验结果可知,满足要求。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐