STM32 HAL库 实现控制步进电机 正转、反转、T型加减速

主要是在项目中实现的一些方法,在此做个记录,以便后续个人复习与总结

一、CubeMX的配置

1.RCC配置

STM32 HAL库 实现控制步进电机 正转、反转、T型加减速_第1张图片

2.SYS配置

STM32 HAL库 实现控制步进电机 正转、反转、T型加减速_第2张图片

3.TIM配置,因为用到了三个步进电机,所以使能步进电机的三个通道

(这里用两个定时器主要是为了学习配置多个定时器)

STM32 HAL库 实现控制步进电机 正转、反转、T型加减速_第3张图片

3.1各通道的配置STM32 HAL库 实现控制步进电机 正转、反转、T型加减速_第4张图片 3.2 记得打开中断

4.配置串口USART(因为要实现上位机发送指令控制步进电机)

STM32 HAL库 实现控制步进电机 正转、反转、T型加减速_第5张图片

 5.GPIO配置

STM32 HAL库 实现控制步进电机 正转、反转、T型加减速_第6张图片

STM32 HAL库 实现控制步进电机 正转、反转、T型加减速_第7张图片 STM32 HAL库 实现控制步进电机 正转、反转、T型加减速_第8张图片

 二、KEIL5 程序编写

1.motor.c文件

#include "Motor.h"
#include 

X_speedRampData X_srd               = {STOP,CW,0,0,0,0,0};         // X加减速曲线变量
__IO int32_t  X_step_position     = 0;           // X当前位置
__IO uint8_t  X_MotionStatus      = 0;           //X是否在运动?0:停止,1:运动

Y_speedRampData Y_srd               = {STOP,CW,0,0,0,0,0};         // Y加减速曲线变量
__IO int32_t  Y_step_position     = 0;           // Y当前位置
__IO uint8_t  Y_MotionStatus      = 0;           //Y是否在运动?0:停止,1:运动

P_speedRampData P_srd               = {STOP,CW,0,0,0,0,0};         // P加减速曲线变量
__IO int32_t  P_step_position     = 0;           // P当前位置
__IO uint8_t  P_MotionStatus      = 0;           //P是否在运动?0:停止,1:运动

extern TIM_HandleTypeDef htim2;
extern TIM_HandleTypeDef htim3;

/**
  * 函数功能: 相对位置运动:运动给定的步数
  * 输入参数: step:移动的步数 (正数为顺时针,负数为逆时针).
              accel  加速度,实际值为accel*0.025*rad/sec^2
              decel  减速度,实际值为decel*0.025*rad/sec^2
              speed  最大速度,实际值为speed*0.05*rad/sec
  * 返 回 值: 无
  * 说    明: 以给定的步数移动步进电机,先加速到最大速度,然后在合适位置开始
  *           减速至停止,使得整个运动距离为指定的步数。如果加减速阶段很短并且
  *           速度很慢,那还没达到最大速度就要开始减速
  */
/***X电机梯形加减速***/
void X_STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed)
{  
  __IO uint16_t X_tim_count;
  // X达到最大速度时的步数
  __IO uint32_t X_max_s_lim;
  // X必须要开始减速的步数(如果加速没有达到最大速度)
  __IO uint32_t X_accel_lim;

  if(step < 0) // 步数为负数
  {
    X_srd.dir = CCW; // 逆时针方向旋转
    X_STEPMOTOR_DIR_REVERSAL();
    step =-step;   // 获取步数绝对值
  }
  else
  {
    X_srd.dir = CW; // 顺时针方向旋转
    X_STEPMOTOR_DIR_FORWARD();
  }
  
  if(step == 1)    // 步数为1
  {
    X_srd.accel_count = -1;   // 只移动一步
    X_srd.run_state = DECEL;  // 减速状态.
    X_srd.step_delay = 1000;	// 短延时	
  }
  else if(step != 0)  // 如果目标运动步数不为0
  {
    // 我们的驱动器用户手册有详细的计算及推导过程

    // 设置最大速度极限, 计算得到min_delay用于定时器的计数器的值。
    // min_delay = (alpha / tt)/ w
    X_srd.min_delay = (int32_t)(A_T_x10/speed);

    // 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.1rad/sec^2
    // step_delay = 1/tt * sqrt(2*alpha/accel)
    // step_delay = ( tfreq*0.676/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100
    X_srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel))/10);

    // 计算多少步之后达到最大速度的限制
    // max_s_lim = speed^2 / (2*alpha*accel)
    X_max_s_lim = (uint32_t)(speed*speed/(A_x200*accel/10));
    // 如果达到最大速度小于0.5步,我们将四舍五入为0
    // 但实际我们必须移动至少一步才能达到想要的速度
    if(X_max_s_lim == 0){
      X_max_s_lim = 1;
    }

    // 计算多少步之后我们必须开始减速
    // n1 = (n1+n2)decel / (accel + decel)
    X_accel_lim = (uint32_t)(step*decel/(accel+decel));
    // 我们必须加速至少1步才能才能开始减速.
    if(X_accel_lim == 0){
      X_accel_lim = 1;
    }

    // 使用限制条件我们可以计算出减速阶段步数
    if(X_accel_lim <= X_max_s_lim){
      X_srd.decel_val = X_accel_lim - step;
    }
    else{
      X_srd.decel_val = -(X_max_s_lim*accel/decel);
    }
    // 当只剩下一步我们必须减速
    if(X_srd.decel_val == 0){
      X_srd.decel_val = -1;
    }

    // 计算开始减速时的步数
    X_srd.decel_start = step + X_srd.decel_val;

    // 如果最大速度很慢,我们就不需要进行加速运动
    if(X_srd.step_delay <= X_srd.min_delay){
      X_srd.step_delay = X_srd.min_delay;
      X_srd.run_state = RUN;
    }
    else{
      X_srd.run_state = ACCEL;
    }    
    // 复位加速度计数值
    X_srd.accel_count = 0;
  }
  X_MotionStatus = 1; // 电机为运动状态
  X_tim_count=__HAL_TIM_GET_COUNTER(&htim3);
  __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,X_tim_count+X_srd.step_delay); // 设置定时器比较值
	HAL_TIM_OC_Start_IT(&htim3,TIM_CHANNEL_1);
  TIM_CCxChannelCmd(TIM3,TIM_CHANNEL_1, TIM_CCx_ENABLE);// 使能定时器通道 
  X_STEPMOTOR_OUTPUT_ENABLE();
}
/***Y电机梯形加减速***/
void Y_STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed)
{  
  __IO uint16_t Y_tim_count;// X达到最大速度时的步数
  __IO uint32_t Y_max_s_lim;// X必须要开始减速的步数(如果加速没有达到最大速度)
  __IO uint32_t Y_accel_lim;


  if(step < 0) // 步数为负数
  {
    Y_srd.dir = CCW; // 逆时针方向旋转
    Y_STEPMOTOR_DIR_REVERSAL();
    step =-step;   // 获取步数绝对值
  }
  else
  {
    Y_srd.dir = CW; // 顺时针方向旋转
    Y_STEPMOTOR_DIR_FORWARD();
  }
  
  if(step == 1)    // 步数为1
  {
    Y_srd.accel_count = -1;   // 只移动一步
    Y_srd.run_state = DECEL;  // 减速状态.
    Y_srd.step_delay = 1000;	// 短延时	
  }
  else if(step != 0)  // 如果目标运动步数不为0
  {
    // 我们的驱动器用户手册有详细的计算及推导过程

    // 设置最大速度极限, 计算得到min_delay用于定时器的计数器的值。
    // min_delay = (alpha / tt)/ w
    Y_srd.min_delay = (int32_t)(A_T_x10/speed);

    // 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.1rad/sec^2
    // step_delay = 1/tt * sqrt(2*alpha/accel)
    // step_delay = ( tfreq*0.676/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100
    Y_srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel))/10);

    // 计算多少步之后达到最大速度的限制
    // max_s_lim = speed^2 / (2*alpha*accel)
    Y_max_s_lim = (uint32_t)(speed*speed/(A_x200*accel/10));
    // 如果达到最大速度小于0.5步,我们将四舍五入为0
    // 但实际我们必须移动至少一步才能达到想要的速度
    if(Y_max_s_lim == 0){
      Y_max_s_lim = 1;
    }

    // 计算多少步之后我们必须开始减速
    // n1 = (n1+n2)decel / (accel + decel)
    Y_accel_lim = (uint32_t)(step*decel/(accel+decel));
    // 我们必须加速至少1步才能才能开始减速.
    if(Y_accel_lim == 0){
      Y_accel_lim = 1;
    }

    // 使用限制条件我们可以计算出减速阶段步数
    if(Y_accel_lim <= Y_max_s_lim){
      Y_srd.decel_val = Y_accel_lim - step;
    }
    else{
      Y_srd.decel_val = -(Y_max_s_lim*accel/decel);
    }
    // 当只剩下一步我们必须减速
    if(Y_srd.decel_val == 0){
      Y_srd.decel_val = -1;
    }

    // 计算开始减速时的步数
    Y_srd.decel_start = step + Y_srd.decel_val;

    // 如果最大速度很慢,我们就不需要进行加速运动
    if(Y_srd.step_delay <= Y_srd.min_delay){
      Y_srd.step_delay = Y_srd.min_delay;
      Y_srd.run_state = RUN;
    }
    else{
      Y_srd.run_state = ACCEL;
    }    
    // 复位加速度计数值
    Y_srd.accel_count = 0;
  }
  Y_MotionStatus = 1; // 电机为运动状态
  Y_tim_count=__HAL_TIM_GET_COUNTER(&htim3);
  __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,Y_tim_count+Y_srd.step_delay); // 设置定时器比较值
	HAL_TIM_OC_Start_IT(&htim3,TIM_CHANNEL_2);
  TIM_CCxChannelCmd(TIM3,TIM_CHANNEL_2, TIM_CCx_ENABLE);// 使能定时器通道 
  Y_STEPMOTOR_OUTPUT_ENABLE();
}

/***P电机梯形加减速***/
void P_STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed)
{  
  __IO uint16_t P_tim_count;
  // X达到最大速度时的步数
  __IO uint32_t P_max_s_lim;
  // X必须要开始减速的步数(如果加速没有达到最大速度)
  __IO uint32_t P_accel_lim;

  if(step < 0) // 步数为负数
  {
    P_srd.dir = CCW; // 逆时针方向旋转
    P_STEPMOTOR_DIR_REVERSAL();
    step =-step;   // 获取步数绝对值
  }
  else
  {
    P_srd.dir = CW; // 顺时针方向旋转
    P_STEPMOTOR_DIR_FORWARD();
  }
  
  if(step == 1)    // 步数为1
  {
    P_srd.accel_count = -1;   // 只移动一步
    P_srd.run_state = DECEL;  // 减速状态.
    P_srd.step_delay = 1000;	// 短延时	
  }
  else if(step != 0)  // 如果目标运动步数不为0
  {
    // 我们的驱动器用户手册有详细的计算及推导过程

    // 设置最大速度极限, 计算得到min_delay用于定时器的计数器的值。
    // min_delay = (alpha / tt)/ w
    P_srd.min_delay = (int32_t)(A_T_x10/speed);

    // 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.1rad/sec^2
    // step_delay = 1/tt * sqrt(2*alpha/accel)
    // step_delay = ( tfreq*0.676/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100
    P_srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel))/10);

    // 计算多少步之后达到最大速度的限制
    // max_s_lim = speed^2 / (2*alpha*accel)
    P_max_s_lim = (uint32_t)(speed*speed/(A_x200*accel/10));
    // 如果达到最大速度小于0.5步,我们将四舍五入为0
    // 但实际我们必须移动至少一步才能达到想要的速度
    if(P_max_s_lim == 0){
      P_max_s_lim = 1;
    }

    // 计算多少步之后我们必须开始减速
    // n1 = (n1+n2)decel / (accel + decel)
    P_accel_lim = (uint32_t)(step*decel/(accel+decel));
    // 我们必须加速至少1步才能才能开始减速.
    if(P_accel_lim == 0){
      P_accel_lim = 1;
    }

    // 使用限制条件我们可以计算出减速阶段步数
    if(P_accel_lim <= P_max_s_lim){
      P_srd.decel_val = P_accel_lim - step;
    }
    else{
      P_srd.decel_val = -(P_max_s_lim*accel/decel);
    }
    // 当只剩下一步我们必须减速
    if(P_srd.decel_val == 0){
      P_srd.decel_val = -1;
    }

    // 计算开始减速时的步数
    P_srd.decel_start = step + P_srd.decel_val;

    // 如果最大速度很慢,我们就不需要进行加速运动
    if(P_srd.step_delay <= P_srd.min_delay){
      P_srd.step_delay = P_srd.min_delay;
      P_srd.run_state = RUN;
    }
    else{
      P_srd.run_state = ACCEL;
    }    
    // 复位加速度计数值
    P_srd.accel_count = 0;
  }
  P_MotionStatus = 1; // 电机为运动状态
  P_tim_count=__HAL_TIM_GET_COUNTER(&htim2);
  __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_3,P_tim_count+X_srd.step_delay); // 设置定时器比较值
	HAL_TIM_OC_Start_IT(&htim2,TIM_CHANNEL_3);
  TIM_CCxChannelCmd(TIM2,TIM_CHANNEL_3, TIM_CCx_ENABLE);// 使能定时器通道 
  P_STEPMOTOR_OUTPUT_ENABLE();
}


/**
  * 函数功能: 定时器中断服务函数
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 实现加减速过程
  */
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)//定时器中断处理
{ 
	/*X电机*/
  __IO uint16_t X_tim_count=0;
  // X保存新(下)一个延时周期
  uint16_t X_new_step_delay=0;
  // X加速过程中最后一次延时(脉冲周期).
  __IO static uint16_t X_last_accel_delay=0;
  // X总移动步数计数器
  __IO static uint32_t X_step_count = 0;
  // X记录new_step_delay中的余数,提高下一步计算的精度
  __IO static int32_t X_rest = 0;
  //X定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲
  __IO static uint8_t X_i=0;
	
	/*Y电机*/
	__IO uint16_t Y_tim_count=0;
  // Y保存新(下)一个延时周期
  uint16_t Y_new_step_delay=0;
  // Y加速过程中最后一次延时(脉冲周期).
  __IO static uint16_t Y_last_accel_delay=0;
  // Y总移动步数计数器
  __IO static uint32_t Y_step_count = 0;
  // X记录new_step_delay中的余数,提高下一步计算的精度
  __IO static int32_t Y_rest = 0;
  //Y定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲
  __IO static uint8_t Y_i=0;
  
	/*P电机*/
	__IO uint16_t P_tim_count=0;
  // Y保存新(下)一个延时周期
  uint16_t P_new_step_delay=0;
  // Y加速过程中最后一次延时(脉冲周期).
  __IO static uint16_t P_last_accel_delay=0;
  // Y总移动步数计数器
  __IO static uint32_t P_step_count = 0;
  // X记录new_step_delay中的余数,提高下一步计算的精度
  __IO static int32_t P_rest = 0;
  //Y定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲
  __IO static uint8_t P_i=0;
	
	if(htim->Instance==TIM3)
	{
	/*X电机的定时器配置*/
  if(__HAL_TIM_GET_IT_SOURCE(&htim3, TIM_IT_CC1) !=RESET)
  {
    // 清楚定时器中断
    __HAL_TIM_CLEAR_IT(&htim3, TIM_IT_CC1);
    
    // 设置比较值
    X_tim_count=__HAL_TIM_GET_COUNTER(&htim3);
    __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,X_tim_count+X_srd.step_delay);

    X_i++;     // 定时器中断次数计数值
    if(X_i==2) // 2次,说明已经输出一个完整脉冲
    {
      X_i=0;   // 清零定时器中断次数计数值
      switch(X_srd.run_state) // 加减速曲线阶段
      {
        case STOP:
          X_step_count = 0;  // 清零步数计数器
          X_rest = 0;        // 清零余值
          // 关闭通道
          TIM_CCxChannelCmd(TIM3, TIM_CHANNEL_1, TIM_CCx_DISABLE);        
          __HAL_TIM_CLEAR_FLAG(&htim3,TIM_FLAG_CC1);
          X_STEPMOTOR_OUTPUT_DISABLE();
          X_MotionStatus = 0;  //  电机为停止状态     
          break;

        case ACCEL:
          X_step_count++;      // 步数加1
          if(X_srd.dir==CW)
          {	  	
            X_step_position++; // 绝对位置加1
          }
          else
          {
            X_step_position--; // 绝对位置减1
          }
          X_srd.accel_count++; // 加速计数值加1
          X_new_step_delay = X_srd.step_delay - (((2 *X_srd.step_delay) + X_rest)/(4 * X_srd.accel_count + 1));//计算新(下)一步脉冲周期(时间间隔)
          X_rest = ((2 * X_srd.step_delay)+X_rest)%(4 * X_srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
          if(X_step_count >= X_srd.decel_start)// 检查是够应该开始减速
          {
            X_srd.accel_count = X_srd.decel_val; // 加速计数值为减速阶段计数值的初始值
            X_srd.run_state = DECEL;           // 下个脉冲进入减速阶段
          }
          else if(X_new_step_delay <= X_srd.min_delay) // 检查是否到达期望的最大速度
          {
            X_last_accel_delay = X_new_step_delay; // 保存加速过程中最后一次延时(脉冲周期)
            X_new_step_delay = X_srd.min_delay;    // 使用min_delay(对应最大速度speed)
            X_rest = 0;                          // 清零余值
            X_srd.run_state = RUN;               // 设置为匀速运行状态
          }
          break;

        case RUN:
          X_step_count++;  // 步数加1
          if(X_srd.dir==CW)
          {	  	
            X_step_position++; // 绝对位置加1
          }
          else
          {
            X_step_position--; // 绝对位置减1
          }
          X_new_step_delay = X_srd.min_delay;     // 使用min_delay(对应最大速度speed)
          if(X_step_count >= X_srd.decel_start)   // 需要开始减速
          {
            X_srd.accel_count = X_srd.decel_val;  // 减速步数做为加速计数值
            X_new_step_delay = X_last_accel_delay;// 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)
            X_srd.run_state = DECEL;            // 状态改变为减速
          }
          break;

        case DECEL:
          X_step_count++;  // 步数加1
          if(X_srd.dir==CW)
          {	  	
            X_step_position++; // 绝对位置加1
          }
          else
          {
            X_step_position--; // 绝对位置减1
          }
          X_srd.accel_count++;
          X_new_step_delay = X_srd.step_delay - (((2 * X_srd.step_delay) + X_rest)/(4 * X_srd.accel_count + 1)); //计算新(下)一步脉冲周期(时间间隔)
          X_rest = ((2 * X_srd.step_delay)+X_rest)%(4 * X_srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
          
          //检查是否为最后一步
          if(X_srd.accel_count >= 0)
          {
            X_srd.run_state = STOP;
          }
          break;
      }      
      X_srd.step_delay = X_new_step_delay; // 为下个(新的)延时(脉冲周期)赋值
    }
  }
	
	/*Y电机的定时器配置*/
 if(__HAL_TIM_GET_IT_SOURCE(&htim3, TIM_IT_CC2) !=RESET)
  { 
    // 清楚定时器中断
    __HAL_TIM_CLEAR_IT(&htim3, TIM_IT_CC2);
    
    // 设置比较值
    Y_tim_count=__HAL_TIM_GET_COUNTER(&htim3);
    __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,Y_tim_count+Y_srd.step_delay);

    Y_i++;     // 定时器中断次数计数值
    if(Y_i==2) // 2次,说明已经输出一个完整脉冲
    {
      Y_i=0;   // 清零定时器中断次数计数值
      switch(Y_srd.run_state) // 加减速曲线阶段
      {
        case STOP:
          Y_step_count = 0;  // 清零步数计数器
          Y_rest = 0;        // 清零余值
          // 关闭通道
          TIM_CCxChannelCmd(TIM3, TIM_CHANNEL_2, TIM_CCx_DISABLE);        
          __HAL_TIM_CLEAR_FLAG(&htim3,TIM_FLAG_CC2);
          Y_STEPMOTOR_OUTPUT_DISABLE();
          Y_MotionStatus = 0;  //  电机为停止状态     
          break;

        case ACCEL:
          Y_step_count++;      // 步数加1
          if(Y_srd.dir==CW)
          {	  	
            Y_step_position++; // 绝对位置加1
          }
          else
          {
            Y_step_position--; // 绝对位置减1
          }
          Y_srd.accel_count++; // 加速计数值加1
          Y_new_step_delay = Y_srd.step_delay - (((2 *Y_srd.step_delay) + Y_rest)/(4 * Y_srd.accel_count + 1));//计算新(下)一步脉冲周期(时间间隔)
          Y_rest = ((2 * Y_srd.step_delay)+Y_rest)%(4 * Y_srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
          if(Y_step_count >= Y_srd.decel_start)// 检查是够应该开始减速
          {
            Y_srd.accel_count = Y_srd.decel_val; // 加速计数值为减速阶段计数值的初始值
            Y_srd.run_state = DECEL;           // 下个脉冲进入减速阶段
          }
          else if(Y_new_step_delay <= Y_srd.min_delay) // 检查是否到达期望的最大速度
          {
            Y_last_accel_delay = Y_new_step_delay; // 保存加速过程中最后一次延时(脉冲周期)
            Y_new_step_delay = Y_srd.min_delay;    // 使用min_delay(对应最大速度speed)
            Y_rest = 0;                          // 清零余值
            Y_srd.run_state = RUN;               // 设置为匀速运行状态
          }
          break;

        case RUN:
          Y_step_count++;  // 步数加1
          if(Y_srd.dir==CW)
          {	  	
            Y_step_position++; // 绝对位置加1
          }
          else
          {
            Y_step_position--; // 绝对位置减1
          }
          Y_new_step_delay = Y_srd.min_delay;     // 使用min_delay(对应最大速度speed)
          if(Y_step_count >= Y_srd.decel_start)   // 需要开始减速
          {
            Y_srd.accel_count = Y_srd.decel_val;  // 减速步数做为加速计数值
            Y_new_step_delay = Y_last_accel_delay;// 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)
            Y_srd.run_state = DECEL;            // 状态改变为减速
          }
          break;

        case DECEL:
          Y_step_count++;  // 步数加1
          if(Y_srd.dir==CW)
          {	  	
            Y_step_position++; // 绝对位置加1
          }
          else
          {
            Y_step_position--; // 绝对位置减1
          }
          Y_srd.accel_count++;
          Y_new_step_delay = Y_srd.step_delay - (((2 * Y_srd.step_delay) + Y_rest)/(4 * Y_srd.accel_count + 1)); //计算新(下)一步脉冲周期(时间间隔)
          Y_rest = ((2 * Y_srd.step_delay)+Y_rest)%(4 * Y_srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
          
          //检查是否为最后一步
          if(Y_srd.accel_count >= 0)
          {
            Y_srd.run_state = STOP;
          }
          break;
      }      
      Y_srd.step_delay = Y_new_step_delay; // 为下个(新的)延时(脉冲周期)赋值
    }
  }
}
	if(htim->Instance==TIM2)
	{
		/*P电机的定时器配置*/
 if(__HAL_TIM_GET_IT_SOURCE(&htim2, TIM_IT_CC3) !=RESET)
  { 
    // 清楚定时器中断
    __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_CC3);
    
    // 设置比较值
    P_tim_count=__HAL_TIM_GET_COUNTER(&htim2);
    __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_3,P_tim_count+P_srd.step_delay);

    P_i++;     // 定时器中断次数计数值
    if(P_i==2) // 2次,说明已经输出一个完整脉冲
    {
      P_i=0;   // 清零定时器中断次数计数值
      switch(P_srd.run_state) // 加减速曲线阶段
      {
        case STOP:
          P_step_count = 0;  // 清零步数计数器
          P_rest = 0;        // 清零余值
          // 关闭通道
          TIM_CCxChannelCmd(TIM2, TIM_CHANNEL_3, TIM_CCx_DISABLE);        
          __HAL_TIM_CLEAR_FLAG(&htim2,TIM_FLAG_CC3);
          P_STEPMOTOR_OUTPUT_DISABLE();
          P_MotionStatus = 0;  //  电机为停止状态     
          break;

        case ACCEL:
          P_step_count++;      // 步数加1
          if(P_srd.dir==CW)
          {	  	
            P_step_position++; // 绝对位置加1
          }
          else
          {
            P_step_position--; // 绝对位置减1
          }
          P_srd.accel_count++; // 加速计数值加1
          P_new_step_delay = P_srd.step_delay - (((2 *P_srd.step_delay) + P_rest)/(4 * P_srd.accel_count + 1));//计算新(下)一步脉冲周期(时间间隔)
          P_rest = ((2 * P_srd.step_delay)+P_rest)%(4 *P_srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
          if(P_step_count >= P_srd.decel_start)// 检查是够应该开始减速
          {
            P_srd.accel_count = P_srd.decel_val; // 加速计数值为减速阶段计数值的初始值
            P_srd.run_state = DECEL;           // 下个脉冲进入减速阶段
          }
          else if(P_new_step_delay <= P_srd.min_delay) // 检查是否到达期望的最大速度
          {
            P_last_accel_delay = P_new_step_delay; // 保存加速过程中最后一次延时(脉冲周期)
            P_new_step_delay = P_srd.min_delay;    // 使用min_delay(对应最大速度speed)
            P_rest = 0;                          // 清零余值
            P_srd.run_state = RUN;               // 设置为匀速运行状态
          }
          break;

        case RUN:
          P_step_count++;  // 步数加1
          if(P_srd.dir==CW)
          {	  	
            P_step_position++; // 绝对位置加1
          }
          else
          {
            P_step_position--; // 绝对位置减1
          }
          P_new_step_delay = P_srd.min_delay;     // 使用min_delay(对应最大速度speed)
          if(P_step_count >= P_srd.decel_start)   // 需要开始减速
          {
            P_srd.accel_count = P_srd.decel_val;  // 减速步数做为加速计数值
            P_new_step_delay = P_last_accel_delay;// 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)
            P_srd.run_state = DECEL;            // 状态改变为减速
          }
          break;

        case DECEL:
          P_step_count++;  // 步数加1
          if(P_srd.dir==CW)
          {	  	
            P_step_position++; // 绝对位置加1
          }
          else
          {
            P_step_position--; // 绝对位置减1
          }
          P_srd.accel_count++;
          P_new_step_delay = P_srd.step_delay - (((2 * P_srd.step_delay) + P_rest)/(4 * P_srd.accel_count + 1)); //计算新(下)一步脉冲周期(时间间隔)
          P_rest = ((2 * P_srd.step_delay)+P_rest)%(4 * P_srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
          
          //检查是否为最后一步
          if(P_srd.accel_count >= 0)
          {
            P_srd.run_state = STOP;
          }
          break;
      }      
      P_srd.step_delay = P_new_step_delay; // 为下个(新的)延时(脉冲周期)赋值
    }
  }
}
}


//==================end【By_Gerhart】======================//

2.motor.h文件 

#ifndef __MOTOR_H__
#define __MOTOR_H__
/*引用库函数*/
#include "main.h"

#define X_Motor_OFF()          HAL_TIM_OC_Stop_IT(&htim3,TIM_CHANNEL_1)
#define Y_Motor_OFF()          HAL_TIM_OC_Stop_IT(&htim3,TIM_CHANNEL_2)
#define P_Motor_OFF()          HAL_TIM_OC_Stop_IT(&htim2,TIM_CHANNEL_3)
//#define Motor_EN()          HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)
//#define Motor_DIS()         HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)

/* 电机结构体宏定义 ------------------------------------------------------------------*/
typedef struct {
  __IO uint8_t  run_state ;  // 电机旋转状态
  __IO uint8_t  dir ;        // 电机旋转方向
  __IO int32_t  step_delay;  // 下个脉冲周期(时间间隔),启动时为加速度
  __IO uint32_t decel_start; // 启动减速位置
  __IO int32_t  decel_val;   // 减速阶段步数
  __IO int32_t  min_delay;   // 最小脉冲周期(最大速度,即匀速段速度)
  __IO int32_t  accel_count; // 加减速阶段计数值
}X_speedRampData;

typedef struct {
  __IO uint8_t  run_state ;  // 电机旋转状态
  __IO uint8_t  dir ;        // 电机旋转方向
  __IO int32_t  step_delay;  // 下个脉冲周期(时间间隔),启动时为加速度
  __IO uint32_t decel_start; // 启动减速位置
  __IO int32_t  decel_val;   // 减速阶段步数
  __IO int32_t  min_delay;   // 最小脉冲周期(最大速度,即匀速段速度)
  __IO int32_t  accel_count; // 加减速阶段计数值
}Y_speedRampData;

typedef struct {
  __IO uint8_t  run_state ;  // 电机旋转状态
  __IO uint8_t  dir ;        // 电机旋转方向
  __IO int32_t  step_delay;  // 下个脉冲周期(时间间隔),启动时为加速度
  __IO uint32_t decel_start; // 启动减速位置
  __IO int32_t  decel_val;   // 减速阶段步数
  __IO int32_t  min_delay;   // 最小脉冲周期(最大速度,即匀速段速度)
  __IO int32_t  accel_count; // 加减速阶段计数值
}P_speedRampData;

extern X_speedRampData X_srd;
extern __IO int32_t  X_step_position;
extern __IO uint8_t  X_MotionStatus; 
extern Y_speedRampData Y_srd;
extern __IO int32_t  Y_step_position;
extern __IO uint8_t  Y_MotionStatus; 
extern P_speedRampData P_srd;
extern __IO int32_t  P_step_position;
extern __IO uint8_t  P_MotionStatus; 

#define STEPMOTOR_TIM_PRESCALER               3  // 步进电机驱动器细分设置为:   32  细分
// 定义定时器周期,输出比较模式周期设置为0xFFFF
#define STEPMOTOR_TIM_PERIOD                  0xFFFF

#define FALSE                                 0
#define TRUE                                  1
#define CW                                    0 // 顺时针
#define CCW                                   1 // 逆时针

#define STOP                                  0 // 加减速曲线状态:停止
#define ACCEL                                 1 // 加减速曲线状态:加速阶段
#define DECEL                                 2 // 加减速曲线状态:减速阶段
#define RUN                                   3 // 加减速曲线状态:匀速阶段
#define T1_FREQ                               (SystemCoreClock/(STEPMOTOR_TIM_PRESCALER+1)) // 频率ft值
#define FSPR                                  200         //步进电机单圈步数
#define MICRO_STEP                            32          // 步进电机驱动器细分数
#define SPR                                   (FSPR*MICRO_STEP)   // 旋转一圈需要的脉冲数

// 数学常数
#define ALPHA                                 ((float)(2*3.14159/SPR))       // α= 2*pi/spr
#define A_T_x10                               ((float)(10*ALPHA*T1_FREQ))
#define T1_FREQ_148                           ((float)((T1_FREQ*0.676)/10)) // 0.676为误差修正值
#define A_SQ                                  ((float)(2*100000*ALPHA)) 
#define A_x200                                ((float)(200*ALPHA))

#define X_STEPMOTOR_DIR_FORWARD()               HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET)
#define X_STEPMOTOR_DIR_REVERSAL()              HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET)
#define Y_STEPMOTOR_DIR_FORWARD()               HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET)
#define Y_STEPMOTOR_DIR_REVERSAL()              HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET)
#define P_STEPMOTOR_DIR_FORWARD()               HAL_GPIO_WritePin(GPIOC,GPIO_PIN_6,GPIO_PIN_SET)
#define P_STEPMOTOR_DIR_REVERSAL()              HAL_GPIO_WritePin(GPIOC,GPIO_PIN_6,GPIO_PIN_RESET)

#define X_STEPMOTOR_OUTPUT_ENABLE();						HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET)
#define X_STEPMOTOR_OUTPUT_DISABLE();						HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET)
#define Y_STEPMOTOR_OUTPUT_ENABLE();						HAL_GPIO_WritePin(GPIOC,GPIO_PIN_7,GPIO_PIN_SET)
#define Y_STEPMOTOR_OUTPUT_DISABLE();						HAL_GPIO_WritePin(GPIOC,GPIO_PIN_7,GPIO_PIN_RESET)
#define P_STEPMOTOR_OUTPUT_ENABLE();						HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET)
#define P_STEPMOTOR_OUTPUT_DISABLE();						HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET)

/*可以被外部使用的函数声明*/
void X_STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed);
void Y_STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed);
void P_STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed);


#endif

3. tim.c中添加使能函数

  /* USER CODE BEGIN TIM2_Init 2 */
	HAL_TIM_OC_Stop_IT(&htim2,TIM_CHANNEL_3);	/*停止定时器2 比较输出通道3*/
	TIM_CCxChannelCmd(TIM2,TIM_CHANNEL_3, TIM_CCx_DISABLE);	/* 使能定时器2比较输出通道3 */	
	__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_CC3);  /* 清除指定的TIM2中断标志3 */
	__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_CC3);  /* 使能定时器2比较输出3 */
	 /* Enable the main output */
	__HAL_TIM_MOE_ENABLE(&htim2); /* 启用TIM2主输出 */
  HAL_TIM_Base_Start(&htim2);  /* 使能定时器2 */
  /* USER CODE END TIM2_Init 2 */





  /* USER CODE BEGIN TIM3_Init 2 */
	HAL_TIM_OC_Stop_IT(&htim3,TIM_CHANNEL_1); /*停止定时器3 比较输出通道1*/
	HAL_TIM_OC_Stop_IT(&htim3,TIM_CHANNEL_2); /*停止定时器3 比较输出通道2*/
	
  TIM_CCxChannelCmd(TIM3,TIM_CHANNEL_1, TIM_CCx_DISABLE);	/* 使能定时器3比较输出通道1 */
	TIM_CCxChannelCmd(TIM3,TIM_CHANNEL_2, TIM_CCx_DISABLE);	/* 使能定时器3比较输出通道2 */
	
  __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_CC1);  /* 清除指定的TIM3中断标志1 */
	__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_CC2);  /* 清除指定的TIM3中断标志2 */
	
  __HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC1);  /* 使能定时器3比较输出1 */
	__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_CC2);  /* 使能定时器3比较输出2 */

  /* Enable the main output */
  __HAL_TIM_MOE_ENABLE(&htim3); /* 启用TIM3主输出 */
  HAL_TIM_Base_Start(&htim3);  /* 使能定时器3 */

  /* USER CODE END TIM3_Init 2 */

4. main函数配置

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *	*串口连接:
	*******************孔板X轴移动の电机**********************************
							 PUL--->PA6
						   DIR--->PB8
						   ENA--->PB9
						   OPTO--->3.3V
	*******************加液针头Y轴移动の电机****************************************
							 PUL--->PA7
						   DIR--->PB7
						   ENA--->PC7
						   OPTO--->3.3V
  *******************蠕动泵の电机****************************************
							 PUL+-->5V
							 PUL--->PA2
						   DIR--->PC6
						   ENA--->PB12
  ******************************************************************************
  * 此行为测试VS2022与Keil能否同步开发
  */
  /* USER CODE END Header */
  /* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "Motor.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t Tx_str1[] = "Wellcome_Gerhart!\r\n";
uint8_t Tx_str2[] = "X1_Forward!\r\n";
uint8_t Tx_str3[] = "X1_Reversal!\r\n";
uint8_t Tx_str4[] = "Stop!\r\n";
uint8_t Tx_str5[] = "Y1_Forward!\r\n";
uint8_t Tx_str6[] = "Y1_Reversal!\r\n";
uint8_t Tx_str7[] = "P1_Forward!\r\n";
uint8_t Tx_str8[] = "P1_Reversal!\r\n";
uint8_t Rx_dat = 0;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart)
{
	if (huart->Instance == USART1)//这部分作用是通过串口控制电机
	{
		if (Rx_dat == 0xa1)//发送A1电机X正转
		{
			X_STEPMOTOR_AxisMoveRel(1 * 16 * 200, 9, 9, 19);//设置X电机参数(转数,加速度,减速度,峰值速度)
			HAL_UART_Transmit(&huart1, Tx_str2, sizeof(Tx_str2), 500);
			HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
		}
		else if (Rx_dat == 0xa2)//发送A2电机X反转
		{
			X_STEPMOTOR_AxisMoveRel(1 * 16 * -200, 9, 9, 19);//设置X电机参数(转数,加速度,减速度,峰值速度)
			HAL_UART_Transmit(&huart1, Tx_str3, sizeof(Tx_str3), 500);
			HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
		}
		else if (Rx_dat == 0xa3)//发送A3电机X停止
		{
			X_Motor_OFF();
			HAL_UART_Transmit(&huart1, Tx_str4, sizeof(Tx_str4), 500);
			HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
		}
		else if (Rx_dat == 0xa4)//发送A4电机Y正转
		{
			Y_STEPMOTOR_AxisMoveRel(1 * 16 * 200, 9, 9, 19);//设置Y电机参数(转数,加速度,减速度,峰值速度)
			HAL_UART_Transmit(&huart1, Tx_str5, sizeof(Tx_str5), 500);
			HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
		}
		else if (Rx_dat == 0xa5)//发送A5电机Y反转
		{
			Y_STEPMOTOR_AxisMoveRel(1 * 16 * -200, 9, 9, 19);//设置Y电机参数(转数,加速度,减速度,峰值速度)
			HAL_UART_Transmit(&huart1, Tx_str6, sizeof(Tx_str6), 500);
			HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
		}
		else if (Rx_dat == 0xa6)//发送A6电机Y停止
		{
			Y_Motor_OFF();
			HAL_UART_Transmit(&huart1, Tx_str4, sizeof(Tx_str4), 500);
			HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
		}
		else if (Rx_dat == 0xa7)//发送A7电机P正转
		{
			P_STEPMOTOR_AxisMoveRel(1 * 16 * 200, 9, 9, 19);//设置P电机参数(转数,加速度,减速度,峰值速度)
			HAL_UART_Transmit(&huart1, Tx_str7, sizeof(Tx_str7), 500);
			HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
		}
		else if (Rx_dat == 0xa8)//发送A8电机P反转
		{
			P_STEPMOTOR_AxisMoveRel(1 * 16 * -200, 9, 9, 19);//设置P电机参数(转数,加速度,减速度,峰值速度)
			HAL_UART_Transmit(&huart1, Tx_str8, sizeof(Tx_str8), 500);
			HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
		}
		else if (Rx_dat == 0xa9)//发送A9电机P停止
		{
			P_Motor_OFF();
			HAL_UART_Transmit(&huart1, Tx_str4, sizeof(Tx_str4), 500);
			HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
		}
	}
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
	/* USER CODE BEGIN 1 */

	/* USER CODE END 1 */

	/* MCU Configuration--------------------------------------------------------*/

	/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
	HAL_Init();

	/* USER CODE BEGIN Init */

	/* USER CODE END Init */

	/* Configure the system clock */
	SystemClock_Config();

	/* USER CODE BEGIN SysInit */

	/* USER CODE END SysInit */

	/* Initialize all configured peripherals */
	MX_GPIO_Init();
	MX_TIM3_Init();
	MX_USART1_UART_Init();
	MX_TIM2_Init();
	/* USER CODE BEGIN 2 */
	HAL_UART_Transmit(&huart1, Tx_str1, sizeof(Tx_str1), 10000);
	HAL_UART_Receive_IT(&huart1, &Rx_dat, 1);
	/* USER CODE END 2 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1)
	{
		//		HAL_Delay(1000);
		//		P_STEPMOTOR_AxisMoveRel(1*16*200, 9, 9 , 19);//设置X电机参数(转数,加速度,减速度,峰值速度)
		//		HAL_Delay(5000);
		//		P_STEPMOTOR_AxisMoveRel(1*16*-200, 9, 9 , 19);//设置X电机参数(转数,加速度,减速度,峰值速度)
		//		HAL_Delay(5000);
	}
	/* USER CODE END WHILE */

	/* USER CODE BEGIN 3 */

  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
	RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
	RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };

	/** Initializes the RCC Oscillators according to the specified parameters
	* in the RCC_OscInitTypeDef structure.
	*/
	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
	RCC_OscInitStruct.HSEState = RCC_HSE_ON;
	RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
	RCC_OscInitStruct.HSIState = RCC_HSI_ON;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
	RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
	RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
	{
		Error_Handler();
	}
	/** Initializes the CPU, AHB and APB buses clocks
	*/
	RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
		| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
	RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
	RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
	RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

	if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
	{
		Error_Handler();
	}
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
	/* USER CODE BEGIN Error_Handler_Debug */
	/* User can add his own implementation to report the HAL error return state */
	__disable_irq();
	while (1)
	{
	}
	/* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
	/* USER CODE BEGIN 6 */
	/* User can add his own implementation to report the file name and line number,
	   ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
	   /* USER CODE END 6 */
	}
#endif /* USE_FULL_ASSERT */

你可能感兴趣的:(stm32,单片机,arm)