找回密码
 立即注册

QQ登录

只需一步,快速开始

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

STM32步进电机PID速度环控速源码

  [复制链接]
跳转到指定楼层
楼主
ID:364498 发表于 2018-11-9 15:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
【1】例程简介
  使用定时器功能输出PWM信号到步进电机驱动器,使其驱动步进电机转动。
  编码器用于电机测速。在电机转动一圈时编码器可以输出固定的脉冲数,通过读取编码器
脉冲可以获取当前电机转动状态。
一般处理编码器脉冲有两种方法:
1. T法:计算一定量的脉冲数所用的时间
2. M法:计算一段固定时间内所捕获的脉冲数。
  根据当前速度和目标速度之间的误差,使用PID计算定时器输出的脉冲频率,使得滑台能够以目标速度
运动

【2】跳线帽情况
编码器              A相  --> PC6
                B相  --> PC7
               
步进电机驱动器    DIR- --> PB13
                ENA- --> PB14
                PUL- --> PA8
                DIR+ --> +5V
                ENA+ --> +5V
                PUL+ --> +5V
  
【3】操作及现象
  根据引脚定义方法连接开发板和步进电机驱动器和编码器,另外步进电机
连接自行根据电机和驱动器标识连接,驱动器需要一个24V的直流电源供电。
接线时注意开发板与驱动器“共地”连接。编码器的AB相输出都是开漏输出,
所以需要使用加上拉电阻才能连接,使用开发板配套的MINI USB线连接到开发
板标示“调试串口”字样的MIMI USB接口为开发板提供电源。下载完程序之后
,开发板持续PWM脉冲给步进电机驱动器,电机以目标速度持续转动,同时在
串口助手上每秒显示一次当前位置,速度,捕获值等信息.

单片机源程序如下:
  1. /**
  2.   ******************************************************************************
  3.   * 文件名程: main.c
  4.   * 作    者: 硬石嵌入式开发团队
  5.   * 版    本: V1.1
  6.   * 功    能: 基于PID速度环的步进电机速度调节
  7.   ******************************************************************************
  8.   * 说明:
  9.   * 本例程配套硬石stm32开发板YS-F1Pro使用。
  10.   *

  11.   ******************************************************************************
  12.   */
  13. /* 包含头文件 ----------------------------------------------------------------*/
  14. #include "stm32f1xx_hal.h"
  15. #include "StepMotor/bsp_STEPMOTOR.h"
  16. #include "usart/bsp_debug_usart.h"
  17. #include "EncoderTIM/bsp_EncoderTIM.h"
  18. #include  <stdlib.h>
  19. #include  <string.h>
  20. /* 私有类型定义 --------------------------------------------------------------*/
  21. typedef struct
  22. {
  23.   __IO float SetPoint;    // 目标值  单位:mm/s
  24.   __IO int LastError;     // 前一次误差   
  25.   __IO int PrevError;     // 前两次误差
  26.   __IO long SumError;     // 累计误差
  27.   __IO double Proportion; // Kp系数
  28.   __IO double Integral;   // Ki系数
  29.   __IO double Derivative; // Kd系数
  30. }PID;
  31. /* 私有宏定义 ----------------------------------------------------------------*/
  32. #define TXDCYCLE                  1000    // 数据发送周期;单位:ms
  33. #define SAMPLING                  0x01    // 采样标记
  34. #define TXD                       0x02    // 发送数据标记

  35. #define abs(x)    ((x)<0?(-x):(x))
  36. #define SENDBUFF_SIZE              100    // 串口DMA发送缓冲区大小

  37. /* 私有变量 ------------------------------------------------------------------*/
  38. __IO static PID vPID;

  39. __IO uint16_t time_count = 0;              // 时间计数,每1ms增加一(与滴答定时器频率有关)
  40. __IO uint8_t Time_Flag   = 0;              // 任务时间标记

  41. /* 扩展变量 ------------------------------------------------------------------*/
  42. extern int16_t OverflowCount;              //编码器计数溢出 计数器
  43. /* 私有函数原形 --------------------------------------------------------------*/

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

  66. /**
  67.   * 函数功能: 系统时钟配置
  68.   * 输入参数: 无
  69.   * 返 回 值: 无
  70.   * 说    明: 无
  71.   */
  72. void SystemClock_Config(void)
  73. {
  74.   RCC_OscInitTypeDef RCC_OscInitStruct;
  75.   RCC_ClkInitTypeDef RCC_ClkInitStruct;

  76.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;  // 外部晶振,8MHz
  77.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  78.   RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  79.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  80.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  81.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;  // 9倍频,得到72MHz主时钟
  82.   HAL_RCC_OscConfig(&RCC_OscInitStruct);

  83.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  84.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  85.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;       // 系统时钟:72MHz
  86.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;              // AHB时钟:72MHz
  87.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;               // APB1时钟:36MHz
  88.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;               // APB2时钟:72MHz
  89.   HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);

  90.          // HAL_RCC_GetHCLKFreq()/1000    1ms中断一次
  91.         // HAL_RCC_GetHCLKFreq()/100000         10us中断一次
  92.         // HAL_RCC_GetHCLKFreq()/1000000 1us中断一次
  93.   HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);  // 配置并启动系统滴答定时器
  94.   /* 系统滴答定时器时钟源 */
  95.   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
  96.   /* 系统滴答定时器中断优先级配置 */
  97.   HAL_NVIC_SetPriority(SysTick_IRQn, 1, 0);
  98. }

  99. /**
  100.   * 函数功能: PID结构体初始化
  101.   * 输入参数: 无
  102.   * 返 回 值: 无
  103.   * 说    明: 初始化PID参数
  104.   */
  105. void Init_PID()
  106. {
  107.   vPID.SetPoint   = 5;       // 目标值  单位:mm/s
  108.   vPID.Proportion = 0.11;    // Kp系数
  109.   vPID.Derivative = 0.03;    // Ki系数
  110.   vPID.Integral   = 0.12;    // Kd系数
  111.   vPID.LastError  = 0;
  112.   vPID.PrevError  = 0;
  113.   vPID.SumError   = 0;
  114. }
  115. /**
  116.   * 函数功能: 主函数.
  117.   * 输入参数: 无
  118.   * 返 回 值: 无
  119.   * 说    明: 无
  120.   */
  121. int main(void)
  122. {
  123.   static float Exp_Val = 0;           // PID计算出来的期望值
  124.   float Vel_Target;                   // 速度目标值
  125.   uint16_t SUM_Pulse = 0;             // 1秒内的总脉冲
  126.   int16_t MSF = 0;                    // 电机反馈速度
  127.   __IO int32_t CaptureNumber=0;       // 输入捕获数
  128.   __IO int32_t Last_CaptureNumber=0;  // 上一次捕获值
  129.   uint8_t aTxBuffer[SENDBUFF_SIZE];   // 串口DMA发送缓冲区
  130.   /* 复位所有外设,初始化Flash接口和系统滴答定时器 */
  131.   HAL_Init();
  132.   /* 配置系统时钟 */
  133.   SystemClock_Config();
  134.   Init_PID();
  135.   /* 调试串口初始化 */
  136.   MX_DEBUG_USART_Init();

  137.   /* 编码器定时器初始化并配置输入捕获功能 */
  138.   ENCODER_TIMx_Init();
  139.   /* 启动编码器接口 */
  140.   HAL_TIM_Encoder_Start(&htimx_Encoder, TIM_CHANNEL_ALL);
  141.   
  142.   HAL_Delay(10);
  143.   /* 步进电机定时器初始化*/
  144.   STEPMOTOR_TIMx_Init();
  145.   /* 首先禁止步进电机动作*/
  146.   STEPMOTOR_OUTPUT_DISABLE();
  147.   /* 启动定时器 */
  148.   HAL_TIM_Base_Start(&htimx_STEPMOTOR);
  149.   /* 启动比较输出并使能中断 */
  150.   HAL_TIM_OC_Start_IT(&htimx_STEPMOTOR,TIM_CHANNEL_1);
  151.   
  152.   /* 目标值单位为:mm/s,这里需要转换为频率
  153.      f = v*PPM;
  154.      PPM是每mm的编码器脉冲数,得到的F就是每秒的脉冲数 (Pulse/s,也就是频率:Hz)
  155.      Vel_Target = f/(1000/20);得到每个采样周期(20ms)的脉冲数(Pulse/ms)
  156.    */
  157.   Vel_Target = (vPID.SetPoint*P_PERIOD);//每单位采样周期内的脉冲数(频率)
  158.   
  159.   /* 无限循环 */
  160.   while (1)
  161.   {
  162.    
  163.     //采样和控制周期为20ms
  164.     if(Time_Flag & SAMPLING)
  165.     {
  166.       //获得编码器的脉冲值
  167.       CaptureNumber = OverflowCount*65535 + __HAL_TIM_GET_COUNTER(&htimx_Encoder);

  168.       //M法 测速度
  169.       MSF = CaptureNumber  - Last_CaptureNumber;
  170.       Last_CaptureNumber = CaptureNumber;
  171.       MSF = abs(MSF);
  172.       //对速度进行累计,得到1s内的脉冲数
  173.       SUM_Pulse += MSF;
  174.       Exp_Val += IncPIDCalc(MSF,Vel_Target);
  175.       Exp_Val = abs(Exp_Val);
  176.       /* 经过PID计算得到的结果是编码器的输出期望值的增量,
  177.          需要转换为步进电机的控制量(频率值),这里乘上一个系数6400/2400
  178.       */
  179.       STEPMOTOR_Motion_Ctrl(CW,Exp_Val*FEEDBACK_CONST);//乘上一个系数,6400/2400,将PID计算结果转换为步进电机的频率(速度)
  180.       Time_Flag &= ~SAMPLING;
  181.     }
  182.     //数据发送周期为1s
  183.     if(Time_Flag & TXD)
  184.     {
  185.       sprintf(aTxBuffer,"捕获值:%d--速度:%.1f mm/s\n",CaptureNumber,(float)SUM_Pulse*MPP);
  186.       sprintf(aTxBuffer+strlen((const char*)aTxBuffer),"1s内编码器计数值:%d\n",SUM_Pulse);
  187.       HAL_UART_Transmit_DMA(&husart_debug, aTxBuffer, strlen((const char*)aTxBuffer));
  188.       SUM_Pulse = 0;
  189.       Time_Flag &= ~TXD;
  190.     }
  191.   }
  192. }

  193. /**
  194.   * 函数功能: 系统滴答定时器中断回调函数
  195.   * 输入参数: 无
  196.   * 返 回 值: 无
  197.   * 说    明: 每发生一次滴答定时器中断进入该回调函数一次
  198.   */
  199. void HAL_SYSTICK_Callback(void)
  200. {
  201.   // 每1ms自动增一
  202.   time_count++;         
  203.   if(time_count%(SAMPLING_PERIOD) == 0) // 20ms
  204.   {
  205.     Time_Flag |= SAMPLING;
  206.   }
  207.   if(time_count >= TXDCYCLE)            // 1s
  208.   {
  209.     Time_Flag |= TXD;
  210.     time_count = 0;
  211.   }
  212. }


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

所有资料51hei提供下载:
YSF1_HAL_MOTOR-018. 步进电机速度环控速.rar (3.23 MB, 下载次数: 302)


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

使用道具 举报

沙发
ID:1 发表于 2018-11-11 01:47 | 只看该作者
好资料,51黑有你更精彩!!!
回复

使用道具 举报

板凳
ID:307544 发表于 2018-11-11 10:14 | 只看该作者
资料不错,程序注释也比较详细,看起来就不那么累了
回复

使用道具 举报

地板
ID:521835 发表于 2019-4-25 22:36 | 只看该作者
感谢楼主分享
回复

使用道具 举报

5#
ID:782879 发表于 2020-6-18 10:16 | 只看该作者
好资料,感谢分享
回复

使用道具 举报

6#
ID:469171 发表于 2020-6-23 12:00 | 只看该作者
感谢楼主分享
回复

使用道具 举报

7#
ID:774108 发表于 2020-7-3 10:01 来自手机 | 只看该作者
有原理图吗
回复

使用道具 举报

8#
ID:856091 发表于 2020-12-7 16:05 | 只看该作者
有集成加减速的吗,加上加减速的PID闭环怎么做
回复

使用道具 举报

9#
ID:793041 发表于 2020-12-7 16:20 | 只看该作者
好用。。。。。。
回复

使用道具 举报

10#
ID:620239 发表于 2021-1-20 13:43 | 只看该作者
这编码器和步进电机是怎样连的?
回复

使用道具 举报

11#
ID:879809 发表于 2021-1-22 20:22 | 只看该作者
嘉哥呀 发表于 2021-1-20 13:43
这编码器和步进电机是怎样连的?

安装到同一根主轴即可。但是楼主思路有问题,步进电机用于廉价低生产率场合,配编码器太贵而且无法明显提高性能,唯一优点是丢步会马上发现。真的不差钱的高效率生产场合,是要用伺服电机+编码器的。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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