[应用相关] STM32编程L298N驱动直流有刷电机实现PID位置、速度双闭环控制实现

[复制链接]
1621|11
 楼主| beijinglaobai 发表于 2022-11-30 11:33 | 显示全部楼层 |阅读模式
本帖最后由 beijinglaobai 于 2022-11-30 11:35 编辑

为实现完成PID控制需要使用STM32定时器的输出通道和互补输出通道共同控制引脚链接驱动器驱动电机和编码器链接STM32 MCU定时器的编码器接口来实现一个完成的驱动、反馈闭环,根据STM32MCU的数据手册可以查询定时器的各引脚的功能。


  1. /**
  2.   ******************************************************************************
  3.   * 文件名程: main.c
  4.   * 功    能:位置速度闭环控制
  5.   ******************************************************************************
  6.   */
  7. /* 包含头文件 ----------------------------------------------------------------*/
  8. #include "stm32f1xx_hal.h"
  9. #include "DCMotor/bsp_BDCMotor.h"
  10. #include "key/bsp_key.h"
  11. #include "usart/bsp_usartx.h"
  12. #include "encoder/bsp_encoder.h"

  13. /* 私有类型定义 --------------------------------------------------------------*/
  14. typedef struct
  15. {
  16.   __IO int32_t  SetPoint;                                 //设定目标 Desired Value
  17.   __IO float    SumError;                                 //误差累计
  18.   __IO float    Proportion;                               //比例常数 Proportional Const
  19.   __IO float    Integral;                                 //积分常数 Integral Const
  20.   __IO float    Derivative;                               //微分常数 Derivative Const
  21.   __IO int      LastError;                                //Error[-1]
  22.   __IO int      PrevError;                                //Error[-2]
  23. }PID_TypeDef;
  24. /* 私有宏定义 ----------------------------------------------------------------*/
  25. #define SPEEDRATIO    270
  26. #define ENCODER_RESOLUTION    11
  27. #define PPR           ((SPEEDRATIO*ENCODER_RESOLUTION)*4) // Pulse/Round 每圈可捕获到的脉冲数

  28. /*************************************/
  29. // 定义PID相关宏
  30. // 这三个参数设定对电机运行影响非常大
  31. // PID参数跟采样时间息息相关
  32. /*************************************/
  33. #define  SPD_P_DATA     15.0f        // P参数
  34. #define  SPD_I_DATA     8.8f         // I参数
  35. #define  SPD_D_DATA     0.0f         // D参数
  36. #define  TARGET_SPEED   10.0f        // 目标速度    10r/m

  37. #define  LOC_P_DATA     0.025f       // P参数
  38. #define  LOC_I_DATA     0.001f       // D参数
  39. #define  LOC_D_DATA     0.08f       // D参数
  40. #define  TARGET_LOC     (3*PPR)    // 目标位置    11880 Pulse = 1r

  41. /* 私有变量 ------------------------------------------------------------------*/
  42. uint32_t Motor_Dir = CW;             // 电机方向
  43. __IO uint8_t  Start_flag = 0;        // PID 开始标志

  44. __IO int32_t tmpPWM_DutySpd = 0;
  45. __IO int32_t tmpPWM_Duty = 0;

  46. __IO int32_t Sample_Pulse;           // 编码器捕获值 Pulse
  47. __IO int32_t LastSample_Pulse;       // 编码器捕获值 Pulse
  48. __IO int32_t Spd_PPS;                // 速度值 Pulse/Sample
  49. __IO float Spd_RPM;                  // 速度值 r/m

  50. static __IO uint32_t uwTick;                 // 滴答定时器计数
  51. /* PID结构体 */
  52. PID_TypeDef  sPID,lPID;
  53. /* 扩展变量 ------------------------------------------------------------------*/
  54. /* 私有函数原形 --------------------------------------------------------------*/
  55. void PID_ParamInit(void) ;
  56. int32_t SpdPIDCalc(float NextPoint);
  57. int32_t LocPIDCalc(int32_t NextPoint);
  58. /* 函数体 --------------------------------------------------------------------*/
  59. /**
  60.   * 函数功能: 系统时钟配置
  61.   * 输入参数: 无
  62.   * 返 回 值: 无
  63.   * 说    明: 无
  64.   */
  65. void SystemClock_Config(void)
  66. {
  67.   RCC_OscInitTypeDef RCC_OscInitStruct;
  68.   RCC_ClkInitTypeDef RCC_ClkInitStruct;

  69.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;  // 外部晶振,8MHz
  70.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  71.   RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  72.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  73.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  74.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;  // 9倍频,得到72MHz主时钟
  75.   HAL_RCC_OscConfig(&RCC_OscInitStruct);

  76.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  77.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  78.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;       // 系统时钟:72MHz
  79.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;              // AHB时钟: 72MHz
  80.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;               // APB1时钟:36MHz
  81.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;               // APB2时钟:72MHz
  82.   HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);

  83.         // HAL_RCC_GetHCLKFreq()/1000    1ms中断一次
  84.         // HAL_RCC_GetHCLKFreq()/100000         10us中断一次
  85.         // HAL_RCC_GetHCLKFreq()/1000000 1us中断一次
  86.   HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);  // 配置并启动系统滴答定时器
  87.   /* 系统滴答定时器时钟源 */
  88.   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
  89.   /* 系统滴答定时器中断优先级配置 */
  90.   HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
  91. }

  92. /**
  93.   * 函数功能: 主函数.
  94.   * 输入参数: 无
  95.   * 返 回 值: 无
  96.   * 说    明: 无
  97.   */
  98. int main(void)
  99. {
  100.   /* 复位所有外设,初始化Flash接口和系统滴答定时器 */
  101.   HAL_Init();
  102.   /* 配置系统时钟 */
  103.   SystemClock_Config();
  104.   /* 按键 初始化 */
  105.   KEY_GPIO_Init();
  106.   /* 串口通信配置初始化 */
  107.   MX_USARTx_Init();
  108.   /* 编码器初始化及使能编码器模式 */
  109.   ENCODER_TIMx_Init();
  110.   /* 配置定时器脉冲输出 */
  111.   BDCMOTOR_TIMx_Init();
  112.   /* 设置电机速度 */
  113.   PWM_Duty = BDCMOTOR_DUTY_ZERO;
  114.   SetMotorSpeed(PWM_Duty);
  115.   /* 设置电机方向 */
  116.   SetMotorDir(1);
  117.   HAL_Delay(1000);
  118.   /* PID 参数初始化 */
  119.   PID_ParamInit();
  120.   /* 无限循环 */
  121.   while (1)
  122.   {
  123.     if(KEY1_StateRead()==KEY_DOWN)
  124.     {
  125.       PID_ParamInit();
  126.       PWM_Duty = 0;
  127.       SetMotorSpeed(PWM_Duty); // 0%
  128.       SetMotorStart();
  129.       HAL_Delay(500);
  130.       Start_flag = 1;
  131.     }
  132.     if(KEY2_StateRead()==KEY_DOWN)
  133.     {
  134.       Start_flag = 0;
  135.       HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
  136.       HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
  137.     }
  138.   }
  139. }
  140. /**
  141.   * 函数功能: 系统滴答定时器中断回调函数
  142.   * 输入参数: 无
  143.   * 返 回 值: 无
  144.   * 说    明: 每一秒读取编码器数值,计算速度,并打印到串口
  145.   */
  146. void HAL_SYSTICK_Callback(void)
  147. {
  148.   uwTick++;
  149.   if((uwTick % 50) == 0)
  150.   {
  151. //    /* 获取当前位置值,编码器4倍频之后的数值 */
  152.     Sample_Pulse = (OverflowCount*CNT_MAX) + (int32_t)__HAL_TIM_GET_COUNTER(&htimx_Encoder);

  153.     /* 计算PID结果 */
  154.     if(Start_flag == 1)
  155.     {
  156.       tmpPWM_DutySpd = LocPIDCalc(Sample_Pulse);

  157.       /* 设定速度环的目标值 */
  158.       if(tmpPWM_DutySpd >= TARGET_SPEED)
  159.         tmpPWM_DutySpd = TARGET_SPEED;
  160.       if(tmpPWM_DutySpd <= -TARGET_SPEED)
  161.         tmpPWM_DutySpd = -TARGET_SPEED;
  162.     }
  163.   }
  164.       /* 速度环周期50ms */
  165.   if(uwTick % 50 == 0)
  166.   {
  167.     /* 获得当前速度 */
  168.     Sample_Pulse = (OverflowCount*CNT_MAX) + (int32_t)__HAL_TIM_GET_COUNTER(&htimx_Encoder);
  169.     Spd_PPS = Sample_Pulse - LastSample_Pulse;
  170.     LastSample_Pulse = Sample_Pulse ;

  171.     /* 11线编码器,270减速比,一圈脉冲信号是11*270*4 PPR */
  172.     Spd_RPM = ((((float)Spd_PPS/(float)PPR)*20.0f)*(float)60);//单位是rpm
  173. //
  174.     /* 计算PID结果 */
  175.     if(Start_flag == 1)
  176.     {
  177.       sPID.SetPoint = tmpPWM_DutySpd;
  178.       PWM_Duty = SpdPIDCalc(Spd_RPM);
  179.       if(PWM_Duty < 0)
  180.       {
  181.         Motor_Dir = CW;
  182.         BDDCMOTOR_DIR_CW();
  183.         PWM_Duty = -PWM_Duty;
  184.       }
  185.       else
  186.       {
  187.         Motor_Dir = CCW;
  188.         BDDCMOTOR_DIR_CCW();
  189.       }
  190.       __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,PWM_Duty );
  191.     }
  192.     printf("LOC:%d Sped: %2.2f r/m \n",Sample_Pulse,Spd_RPM );
  193.   }
  194. }


  195. /********************* PID参数初始化 ********************************/
  196. /**
  197.   * 函数功能: PID参数初始化
  198.   * 输入参数: 无
  199.   * 返 回 值: 无
  200.   * 说    明: 无
  201.   */
  202. void PID_ParamInit()
  203. {
  204.     Sample_Pulse = (OverflowCount*CNT_MAX) + (int32_t)__HAL_TIM_GET_COUNTER(&htimx_Encoder);
  205.     if(Sample_Pulse != 0)
  206.     {
  207.       Sample_Pulse = 0;
  208.       __HAL_TIM_SET_COUNTER(&htimx_Encoder,0);
  209.     }
  210.     sPID.LastError = 0;               // Error[-1]
  211.     sPID.PrevError = 0;               // Error[-2]
  212.     sPID.SumError = 0;
  213.     sPID.Proportion = SPD_P_DATA; // 比例常数 Proportional Const
  214.     sPID.Integral = SPD_I_DATA;   // 积分常数  Integral Const
  215.     sPID.Derivative = SPD_D_DATA; // 微分常数 Derivative Const
  216.     sPID.SetPoint = TARGET_SPEED;     // 设定目标Desired Value

  217.     lPID.LastError = 0;               // Error[-1]
  218.     lPID.PrevError = 0;               // Error[-2]
  219.     lPID.SumError = 0;
  220.     lPID.Proportion = LOC_P_DATA; // 比例常数 Proportional Const
  221.     lPID.Integral = LOC_I_DATA;   // 积分常数  Integral Const
  222.     lPID.Derivative = LOC_D_DATA; // 微分常数 Derivative Const
  223.     lPID.SetPoint = TARGET_LOC;     // 设定目标Desired Value
  224. }

  225. /******************** 电流闭环 PID 控制设计 ************************************/

  226. /**
  227.   * 函数名称:速度闭环PID控制设计
  228.   * 输入参数:当前控制量
  229.   * 返 回 值:目标控制量
  230.   * 说    明:无
  231.   */
  232. int32_t SpdPIDCalc(float NextPoint)
  233. {
  234.   float iError,dError;
  235.   iError = sPID.SetPoint - NextPoint; //偏差

  236.   if((iError<0.2f )&& (iError>-0.2f))
  237.     iError = 0.0f;

  238.   sPID.SumError += iError; //积分
  239.     /* 设定积分上限 */
  240.   if(lPID.SumError >= TARGET_SPEED*10)
  241.     lPID.SumError = TARGET_SPEED*10;
  242.   if(lPID.SumError <= -TARGET_SPEED*10)
  243.     lPID.SumError = -TARGET_SPEED*10;

  244.   dError = iError - sPID.LastError; //微分
  245.   sPID.LastError = iError;
  246.   return (int32_t)(sPID.Proportion * (float)iError //比例项
  247.   + sPID.Integral * (float)sPID.SumError //积分项
  248.   + sPID.Derivative * (float)dError); //微分项
  249. }
  250. /**
  251.   * 函数名称:位置闭环PID控制设计
  252.   * 输入参数:当前控制量
  253.   * 返 回 值:目标控制量
  254.   * 说    明:无
  255.   */
  256. int32_t LocPIDCalc(int32_t NextPoint)
  257. {
  258.   int32_t iError,dError;
  259.   iError = lPID.SetPoint - NextPoint; //偏差
  260.   /* 设定闭环死区 */
  261.   if((iError >= -50) && (iError <= 50))
  262.   {
  263.     iError = 0;
  264.     lPID.SumError /= 2.0;
  265.   }
  266.   /* 积分分离 */
  267.   if((iError >= -1000) && (iError <= 1000))
  268.   {
  269.     lPID.SumError += iError; //积分

  270.     /* 设定积分上限 */
  271.     if(lPID.SumError >= 1000)
  272.       lPID.SumError = 1000;
  273.     if(lPID.SumError <= -1000)
  274.       lPID.SumError = -1000;
  275.   }
  276.   dError = iError - lPID.LastError; //微分
  277.   lPID.LastError = iError;

  278.   return (int32_t)(lPID.Proportion * (float)iError //比例项
  279.   + lPID.Integral * (float)lPID.SumError //积分项
  280.   + lPID.Derivative * (float)dError);    //微分项
  281. }



STM32F103ZET6MCU定时器引脚功能定义

STM32F103ZET6MCU定时器引脚功能定义

使用功能引脚在芯片上的位置对照

使用功能引脚在芯片上的位置对照

MCU编码器反馈引脚定义代码

MCU编码器反馈引脚定义代码

电机驱动控制引脚定义代码

电机驱动控制引脚定义代码
 楼主| beijinglaobai 发表于 2022-11-30 11:45 | 显示全部楼层
本帖最后由 beijinglaobai 于 2022-11-30 11:47 编辑
  1. [media=x,500,375]【STM32编程控制L298N驱动直流有刷电机实现PID速度、位置双闭环控制】 https://www.bilibili.com/video/BV1yN411d7EJ/?share_source=copy_web&vd_source=75b07358fcefa824ffaf5ab2d1c4b8ca[/media]
Clyde011 发表于 2024-11-8 07:08 | 显示全部楼层

电源电压处于1.6V到5.5V之间
公羊子丹 发表于 2024-11-8 08:01 | 显示全部楼层

电压范围称为工作电源电压
万图 发表于 2024-11-8 09:04 | 显示全部楼层

内部电路工作电圧是通过内部电压调节器调节电源电压得到的
Uriah 发表于 2024-11-8 10:07 | 显示全部楼层

单片机的外部都连接有象电池等电源部分
帛灿灿 发表于 2024-11-8 12:03 | 显示全部楼层

通常选择0.01μF~0.1μF的陶瓷电容作为旁路电容。
Bblythe 发表于 2024-11-8 13:06 | 显示全部楼层

主时钟振荡器主要用作CPU的工作时钟
周半梅 发表于 2024-11-8 15:02 | 显示全部楼层

防止因瞬间大电流引起的电源电压下降
Pulitzer 发表于 2024-11-8 16:05 | 显示全部楼层

要在外部连接一个振荡电路提供时钟信号
童雨竹 发表于 2024-11-8 18:01 | 显示全部楼层

与15号引脚连接的C1称为旁路电容
Wordsworth 发表于 2024-11-8 19:04 | 显示全部楼层

时序电路是按时钟信号(CK)的上升沿(信号从L→H的变化)或下降沿(信号从H→L的变化)同步工作的
您需要登录后才可以回帖 登录 | 注册

本版积分规则

58

主题

113

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部