找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2520|回复: 0
打印 上一主题 下一主题
收起左侧

57&42步进电机_速度闭环_位置式PID STM32源程序

[复制链接]
跳转到指定楼层
楼主
ID:301684 发表于 2019-9-25 09:50 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
黑石H7开发板步进电机驱动  亲测可用
  1. #include "stm32h7xx_hal.h"
  2. #include "bsp_led.h"
  3. #include "bsp_key.h"
  4. #include "bsp_debug_usart.h"
  5. #include "bsp_stepmotor.h"
  6. #include "bsp_encoder.h"
  7. #include <string.h>
  8. #include <math.h>
  9. #include <stdbool.h>
  10. #include <stdlib.h>

  11. /* 私有类型定义 --------------------------------------------------------------*/
  12. typedef struct
  13. {
  14.   __IO float    SetPoint;    // 目标值  单位:r/m
  15.   __IO int32_t  LastError;   // 前一次误差   
  16.   __IO int32_t  PrevError;   // 前两次误差
  17.   __IO int32_t  SumError;    // 累计误差
  18.   float    Proportion;       // Kp系数
  19.   float    Integral;         // Ki系数
  20.   float    Derivative;       // Kd系数
  21. }PID_Typedef;

  22. /* 私有宏定义 ----------------------------------------------------------------*/
  23. #define    PID_DEFAULT_P          0.4f  //
  24. #define    PID_DEFAULT_I          0.2f  //
  25. #define    PID_DEFAULT_D          0.0f  //
  26. #define    SPEED                  5.3f  // 移动速度 mm/s

  27. #define    MM_ROUND               5   // 丝杆导程 mm/r
  28. #define    INTERVAL               20  //  采样和PID计算时间间隔 ms

  29. /* 私有变量 ------------------------------------------------------------------*/

  30. __IO static PID_Typedef vPID; // 速度环PID结构体

  31. __IO uint16_t time_count = 0; // 时间计数,每1ms增加一(与滴答定时器频率有关)
  32. __IO uint8_t Time_Flag   = 0; // 任务时间标记
  33. bool Start_flag = false;      // 启动/停止

  34. /* 扩展变量 ------------------------------------------------------------------*/

  35. /* 私有函数原形 --------------------------------------------------------------*/

  36. static void SystemClock_Config( void );
  37. static void CPU_CACHE_Enable( void );

  38. /* 函数体 --------------------------------------------------------------------*/
  39.   
  40. /**
  41.   * 函数功能:位置式PID速度环计算
  42.   * 输入参数: @NextPoint    由编码器得到的计数值
  43.   *           @TargetVal    目标值
  44.   * 返 回 值:经过PID运算得到的增量值
  45.   * 说    明:位置式 PID 速度环控制设计,计算得到的结果仍然是速度值
  46.   */
  47. float IncPIDCalc(int NextPoint,float TargetVal)       //临时变量,期望值
  48. {
  49.   float iError = 0,iIncpid = 0;                       //当前误差
  50.   
  51.   iError = TargetVal - NextPoint;                     // 增量计算
  52.   if((iError < 0.5f)&&(iError>-0.5f))
  53.     iError = 0;                                       // |e| < 0.5,不做调整
  54.   
  55.   vPID.PrevError = iError - vPID.LastError;
  56.   vPID.SumError += iError;
  57.   iIncpid=(vPID.Proportion * iError)                  // E[k]项
  58.               +(vPID.Integral * vPID.SumError)        // E[k-1]项
  59.               +(vPID.Derivative * vPID.PrevError);    // E[k-2]项
  60.   
  61.   vPID.PrevError = vPID.LastError;                    // 存储误差,用于下次计算
  62.   vPID.LastError = iError;
  63.   return(iIncpid);                                    // 返回增量值
  64. }

  65. /**
  66.   * 函数功能: PID结构体初始化
  67.   * 输入参数: 无
  68.   * 返 回 值: 无
  69.   * 说    明: 初始化PID参数
  70.   */
  71. void Init_PIDStruct()
  72. {
  73.   vPID.SetPoint   = SPEED;          // 目标值  单位:mm/s
  74.   vPID.Proportion = PID_DEFAULT_P;  // Kp系数
  75.   vPID.Integral   = PID_DEFAULT_I;  // Ki系数
  76.   vPID.Derivative = PID_DEFAULT_D;  // Kd系数
  77.   vPID.LastError  = 0;
  78.   vPID.PrevError  = 0;
  79.   vPID.SumError   = 0;
  80. }
  81. /**
  82.   * 函数功能: 主函数.
  83.   * 输入参数: 无
  84.   * 返 回 值: 无
  85.   * 说    明: 无
  86.   */
  87. int main(void)
  88. {
  89.   float     Exp_Val     = 0;  // PID计算出来的期望值
  90.   float     Vel_Target  = 0;; // 速度目标值
  91.   int16_t   MSF = 0;          // 电机反馈速度
  92.   int32_t   Enc_Cap     = 0;  // 编码器输入捕获数
  93.   int32_t   lastEnc_Cap = 0;  // 编码器上一次捕获值
  94.   
  95.   CPU_CACHE_Enable();
  96.   /* 复位所有外设,初始化Flash接口和系统滴答定时器 */
  97.   HAL_Init();
  98.   /* 配置系统时钟 */
  99.   SystemClock_Config();
  100.   /* 板载LED,按键初始化 */
  101.   LED_GPIO_Init();
  102.   KEY_GPIO_Init();
  103.   /* 初始化PID参数结构体 */
  104.   Init_PIDStruct();
  105.   /* 调试串口初始化 */
  106.   MX_DEBUG_UART_Init();

  107.   /* 编码器定时器初始化并配置输入捕获功能 */
  108.   ENCODER_TIMx_Init();
  109.   /* 启动编码器接口 */
  110.   HAL_TIM_Encoder_Start(&htimx_Encoder, TIM_CHANNEL_ALL);
  111.   
  112.   HAL_Delay(10);
  113.   
  114.   /* 步进电机定时器初始化*/
  115.   SMOTOR1_TIMx_Init();
  116.   
  117.   /* 首先禁止步进电机动作*/
  118.   SMotor_Set_State(MOTOR_DISABLE);
  119.   /* 比较输出和中断 */
  120.   HAL_TIM_OC_Stop_IT(&htimx_SMotor1,TIM_CHANNEL_1);
  121.   
  122.   /* 移动速度转换为PID的目标值
  123.    * 目标值是每个采样周期的编码器计数值,SetPoint是移动速度(mm/s),INTERVAL是采样周期间隔
  124.    * 1mm/s对应的编码器计数值是 ENCODER_RESOLUTION/MM_ROUND -> 2400/5 = 480 Hz
  125.    * 每个采样周期之间的编码器计数值就是 480/(1000/INTERVAL) = 9.6f
  126.    * SetPoint*9.6 就是 SetPoint mm/s 对应的采样周期间隔编码器计数值
  127.    */
  128.   Vel_Target = round(fabs(vPID.SetPoint) * (ENCODER_RESOLUTION/MM_ROUND)/(1000/INTERVAL)  );//每单位采样周期内的脉冲数(频率)
  129.   
  130.   __HAL_DBGMCU_FREEZE_TIM8();// debug 的时候停止定时器时钟
  131.   __HAL_DBGMCU_FREEZE_TIM2();// debug 的时候停止定时器时钟
  132.   while (1)
  133.   {
  134.    
  135.     /* KEY1启动电机,并且使能PID计算 */
  136.     if(KEY1_StateRead() == KEY_DOWN)
  137.     {
  138.       SMotor_Set_State(MOTOR_ENABLE);
  139.       Exp_Val = 0;
  140.       Init_PIDStruct();
  141.       if(vPID.SetPoint < 0 )
  142.         SMotor_Set_Dir( MOTOR_DIR_CCW );
  143.       else
  144.         SMotor_Set_Dir( MOTOR_DIR_CW );
  145.       SMotor_Set_Speed(0.01); // 低速启动
  146.       HAL_TIM_OC_Start_IT(&htimx_SMotor1,TIM_CHANNEL_1);
  147.       Start_flag = true;           // 启动/停止
  148.       printf("启动PID计算\n");
  149.       printf("设置的目标值是 %f mm/s \n",vPID.SetPoint);//
  150.       printf("对应的编码器计数值是 %d pulse/s \n",(int32_t)Vel_Target*(1000/INTERVAL));//
  151.     }
  152.     /* KEY2停止电机,并且停止PID计算 */
  153.     if(KEY2_StateRead() == KEY_DOWN)
  154.     {
  155.       SMotor_Set_State(MOTOR_DISABLE);
  156.       HAL_TIM_OC_Stop_IT(&htimx_SMotor1,TIM_CHANNEL_1);
  157.       Start_flag = false;           // 启动/停止
  158.     }
  159.     /* KEY3/KEY4用于调控电机位置 */
  160.     if(KEY3_StateRead() == KEY_DOWN)
  161.     {
  162.       Start_flag = false;          // 启动/停止
  163.       SMotor_Set_State( MOTOR_ENABLE );
  164.       SMotor_Set_Dir( MOTOR_DIR_CW );
  165.       SMotor_Set_Speed( 160 );     //设置低速启动
  166.       HAL_TIM_OC_Start_IT(&htimx_SMotor1, SMOTOR1_PUL_TIMx_CHANNELx);
  167.     }
  168.     if(KEY4_StateRead() == KEY_DOWN)
  169.     {
  170.       Start_flag = false;          // 启动/停止
  171.       SMotor_Set_State( MOTOR_ENABLE );
  172.       SMotor_Set_Dir( MOTOR_DIR_CCW );
  173.       SMotor_Set_Speed( 160 );     // 设置低速启动
  174.       HAL_TIM_OC_Start_IT(&htimx_SMotor1, SMOTOR1_PUL_TIMx_CHANNELx);
  175.     }
  176.     // 采样和控制周期为20ms
  177.     if(Time_Flag & 0x01)
  178.     {
  179.       //获得编码器的脉冲值
  180.       Enc_Cap  = YS_Encoder_GetCounting();

  181.       //M法 测速度
  182.       MSF = Enc_Cap - lastEnc_Cap;
  183.       lastEnc_Cap = Enc_Cap;
  184.       MSF = abs(MSF);
  185.       
  186.       Exp_Val    = IncPIDCalc(MSF, Vel_Target);
  187.       Exp_Val    = fabs(Exp_Val);

  188.       SMotor_Set_Speed(Exp_Val);
  189.       Time_Flag &= ~0x01;
  190.     }
  191.    
  192.     // 数据发送周期为1s, 显示转速
  193.     if(Time_Flag & 0x02)
  194.     {
  195.       int32_t pulse = YS_Encoder_GetCounting();
  196.       static int32_t tmp = 0;
  197.       float speed = (float)( pulse - tmp ) / ENCODER_RESOLUTION ;
  198.       
  199.       printf("1s内的编码器计数值:  %d\n", pulse - tmp);
  200.       printf("电机转速:  %.2f r/s  \n", speed );
  201.       printf("移动速度:  %.1f mm/s \n", speed*5.0f);// 1r就是5mm
  202.       tmp = pulse ;
  203.       Time_Flag &= ~0x02;
  204.     }
  205.   }
  206. }
  207. /**
  208.   * 函数功能: 系统滴答定时器中断回调函数
  209.   * 输入参数: 无
  210.   * 返 回 值: 无
  211.   * 说    明: 每发生一次滴答定时器中断进入该回调函数一次
  212.   */
  213. void HAL_SYSTICK_Callback(void)
  214. {
  215.   // 每1ms自动增一
  216.   time_count++;         
  217.   if( (time_count % INTERVAL) == 0)// 20ms读取一次编码器数值
  218.   {
  219.     if(Start_flag )
  220.       Time_Flag |= 0x01;
  221.   }
  222.   else if(time_count >= 1000)      // 1s发送一次数据
  223.   {
  224.     Time_Flag |= 0x02;
  225.     time_count = 0;
  226.   }
  227. }


  228. /**
  229.   * 函数功能: 定时器比较输出中断回调函数
  230.   * 输入参数: htim:定时器句柄指针
  231.   * 返 回 值: 无
  232.   * 说    明: 控制脉冲输出
  233.   */
  234. void HAL_TIM_OC_DelayElapsedCallback( TIM_HandleTypeDef * htim)
  235. {
  236.   uint32_t Toggle = 0;
  237.   
  238.   if(htim == &htimx_SMotor1)
  239.   {
  240.     /* 设置下一次中断的间隔 */
  241.     Toggle = __HAL_TIM_GET_COMPARE(&htimx_SMotor1, SMOTOR1_PUL_TIMx_CHANNELx);
  242.     Toggle += SMotor1.PulseWidth; // 计算实际的比较值
  243.     __HAL_TIM_SET_COMPARE(&htimx_SMotor1, SMOTOR1_PUL_TIMx_CHANNELx, (uint16_t)Toggle);
  244.   }
  245. }
  246. /**
  247.   * 函数功能: 系统时钟配置
  248.   * 输入参数: 无
  249.   * 返 回 值: 无
  250.   * 说    明: 无
  251.   */
  252. static void SystemClock_Config(void)
  253. {
  254.   RCC_OscInitTypeDef RCC_OscInitStruct={0};
  255.   RCC_ClkInitTypeDef RCC_ClkInitStruct={0};

  256.   /* 使能PWR配置更新 */
  257.   MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);

  258.   /* 设置调压器输出电压级别1 */
  259.   __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  260.   /* 等待D3区域VOS 输出准备就绪 */
  261.   while ((PWR->D3CR & (PWR_D3CR_VOSRDY)) != PWR_D3CR_VOSRDY) // 等待内核电源就绪
  262.   {
  263.    
  264.   }
  265.   /* RCC 内部/外部晶振配置 ,用于CPU,AHB,APH总线时钟 */
  266.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;// 外部晶振
  267.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;//选择外部时钟晶振 HSE 25MHz
  268.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;//使能PLL
  269.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;//PLL时钟源选择HSE
  270.   RCC_OscInitStruct.PLL.PLLM = 5;  // 外部时钟2分频,得到5MHz 作为PLL时钟源
  271.   RCC_OscInitStruct.PLL.PLLN = 160;// PLL 160倍频,得到800MHz PLLCLK
  272.   RCC_OscInitStruct.PLL.PLLP = 2;  // PLLCLK 2分频 得到400MHz SYSCLK
  273.   RCC_OscInitStruct.PLL.PLLQ = 2;  // PLLCLK 2分频 得到400MHz用于部分外设的时钟
  274.   RCC_OscInitStruct.PLL.PLLR = 2;  // PLLCLK 2分频 得到400MHz用于部分外设的时钟
  275.   RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;// PLL时钟输入范围4-8MHz
  276.   RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;// PLL时钟输出范围192-836MHz
  277.   RCC_OscInitStruct.PLL.PLLFRACN = 0;  // 此参数用于微调 PLL1 VCO 范围0~2^13-1
  278.   HAL_RCC_OscConfig(&RCC_OscInitStruct);
  279.   
  280.   /* RCC CPU,AHB,APH总线时钟配置*/
  281.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  282.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
  283.                               |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  284.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;//SYSCLK 时钟源400MHz
  285.   RCC_ClkInitStruct.SYSCLKDivider  = RCC_SYSCLK_DIV1;// SYSCLK 1分频 HCLK=400MHz
  286.   RCC_ClkInitStruct.AHBCLKDivider  = RCC_HCLK_DIV2;  // HCLK 2分频,AHB = 200Mhz
  287.   RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; // APB3 2分频,APB3 = 100MHz
  288.   RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; // APB1 2分频,APB1 = 100MHz
  289.   RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; // APB2 2分频,APB2 = 100MHz
  290.   RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; // APB4 2分频,APB4 = 100MHz
  291.   HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
  292.   

  293.   /* 使能时钟安全机制 */
  294.   HAL_RCC_EnableCSS();

  295.   /* 滴答定时器配置1ms */
  296.   // SystemCoreClock/1000    1ms中断一次
  297.         // SystemCoreClock/100000         10us中断一次
  298.         // SystemCoreClock/1000000 1us中断一次
  299.   HAL_SYSTICK_Config(SystemCoreClock/(1000));

  300.   /* 系统滴答定时器时钟源 */
  301.   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  302.   /* 系统滴答定时器中断优先级配置 */
  303.   HAL_NVIC_SetPriority(SysTick_IRQn, 1, 1);
  304. }

  305. /**
  306.   * 函数功能: 使能CPU L1-Cache
  307.   * 输入参数: 无
  308.   * 返 回 值: 无
  309.   * 说    明: 无
  310.   */
  311. static void CPU_CACHE_Enable(void)
  312. {
  313.   /* 使能 I-Cache */
  314.   SCB_EnableICache();

  315.   /* 使能 D-Cache */
  316.   SCB_EnableDCache();
  317. }

  318. /********** (C) COPYRIGHT 2019-2030 硬石嵌入式开发团队 *******END OF FILE******/
复制代码


YS-H7Multi_HAL_MOTOR-236. 57&amp;42步进电机_速度闭环_位置式PID.7z

7.37 MB, 下载次数: 78, 下载积分: 黑币 -5

黑石H7步进电机驱动

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏3 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表