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

STM32F103ZET6MCU定时器引脚功能定义

STM32F103ZET6MCU定时器引脚功能定义

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

在相应的.h资源文件利用宏定义实现功能引脚和指定功能的一一对应

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

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

为了调试我们还定义了串口输出引脚和按键控制引脚的定义

串口调试引脚定义

按键控制引脚定义

根据定义的引脚初始化以后就可以控制运行状态的进程里编辑PID控制代码了

/********************************************************************************* 文件名程: main.c* 功    能:位置速度双闭环控制*******************************************************************************/
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
#include "DCMotor/bsp_BDCMotor.h"
#include "key/bsp_key.h"
#include "usart/bsp_usartx.h"
#include "encoder/bsp_encoder.h"/* 私有类型定义 --------------------------------------------------------------*/
typedef struct
{__IO int32_t  SetPoint;                                 //设定目标 Desired Value__IO float    SumError;                                 //误差累计__IO float    Proportion;                               //比例常数 Proportional Const__IO float    Integral;                                 //积分常数 Integral Const__IO float    Derivative;                               //微分常数 Derivative Const__IO int      LastError;                                //Error[-1]__IO int      PrevError;                                //Error[-2]
}PID_TypeDef;
/* 私有宏定义 ----------------------------------------------------------------*/
#define SPEEDRATIO    270
#define ENCODER_RESOLUTION    11
#define PPR           ((SPEEDRATIO*ENCODER_RESOLUTION)*4) // Pulse/Round 每圈可捕获到的脉冲数/*************************************/
// 定义PID相关宏
// 这三个参数设定对电机运行影响非常大
// PID参数跟采样时间息息相关
/*************************************/
#define  SPD_P_DATA     15.0f        // P参数
#define  SPD_I_DATA     8.8f         // I参数
#define  SPD_D_DATA     0.0f         // D参数
#define  TARGET_SPEED   10.0f        // 目标速度    10r/m#define  LOC_P_DATA     0.025f       // P参数
#define  LOC_I_DATA     0.001f       // D参数
#define  LOC_D_DATA     0.08f       // D参数
#define  TARGET_LOC     (3*PPR)    // 目标位置    11880 Pulse = 1r/* 私有变量 ------------------------------------------------------------------*/
uint32_t Motor_Dir = CW;             // 电机方向
__IO uint8_t  Start_flag = 0;        // PID 开始标志__IO int32_t tmpPWM_DutySpd = 0;
__IO int32_t tmpPWM_Duty = 0;__IO int32_t Sample_Pulse;           // 编码器捕获值 Pulse
__IO int32_t LastSample_Pulse;       // 编码器捕获值 Pulse
__IO int32_t Spd_PPS;                // 速度值 Pulse/Sample
__IO float Spd_RPM;                  // 速度值 r/mstatic __IO uint32_t uwTick;                 // 滴答定时器计数
/* PID结构体 */
PID_TypeDef  sPID,lPID;
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
void PID_ParamInit(void) ;
int32_t SpdPIDCalc(float NextPoint);
int32_t LocPIDCalc(int32_t NextPoint);
/* 函数体 --------------------------------------------------------------------*/
/*** 函数功能: 系统时钟配置* 输入参数: 无* 返 回 值: 无* 说    明: 无*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct;RCC_ClkInitTypeDef RCC_ClkInitStruct;RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;  // 外部晶振,8MHzRCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;  // 9倍频,得到72MHz主时钟HAL_RCC_OscConfig(&RCC_OscInitStruct);RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;       // 系统时钟:72MHzRCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;              // AHB时钟: 72MHzRCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;               // APB1时钟:36MHzRCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;               // APB2时钟:72MHzHAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);// HAL_RCC_GetHCLKFreq()/1000    1ms中断一次// HAL_RCC_GetHCLKFreq()/100000    10us中断一次// HAL_RCC_GetHCLKFreq()/1000000 1us中断一次HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);  // 配置并启动系统滴答定时器/* 系统滴答定时器时钟源 */HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);/* 系统滴答定时器中断优先级配置 */HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}/*** 函数功能: 主函数.* 输入参数: 无* 返 回 值: 无* 说    明: 无*/
int main(void)
{/* 复位所有外设,初始化Flash接口和系统滴答定时器 */HAL_Init();/* 配置系统时钟 */SystemClock_Config();/* 按键 初始化 */KEY_GPIO_Init();/* 串口通信配置初始化 */MX_USARTx_Init();/* 编码器初始化及使能编码器模式 */ENCODER_TIMx_Init();/* 配置定时器脉冲输出 */BDCMOTOR_TIMx_Init();/* 设置电机速度 */PWM_Duty = BDCMOTOR_DUTY_ZERO;SetMotorSpeed(PWM_Duty);/* 设置电机方向 */SetMotorDir(1);HAL_Delay(1000);/* PID 参数初始化 */PID_ParamInit();/* 无限循环 */while (1){if(KEY1_StateRead()==KEY_DOWN){PID_ParamInit();PWM_Duty = 0;SetMotorSpeed(PWM_Duty); // 0%SetMotorStart();HAL_Delay(500);Start_flag = 1;}if(KEY2_StateRead()==KEY_DOWN){Start_flag = 0;HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);}}
}
/*** 函数功能: 系统滴答定时器中断回调函数* 输入参数: 无* 返 回 值: 无* 说    明: 每一秒读取编码器数值,计算速度,并打印到串口*/
void HAL_SYSTICK_Callback(void)
{uwTick++;if((uwTick % 50) == 0){
//    /* 获取当前位置值,编码器4倍频之后的数值 */Sample_Pulse = (OverflowCount*CNT_MAX) + (int32_t)__HAL_TIM_GET_COUNTER(&htimx_Encoder);/* 计算PID结果 */if(Start_flag == 1){tmpPWM_DutySpd = LocPIDCalc(Sample_Pulse);/* 设定速度环的目标值 */if(tmpPWM_DutySpd >= TARGET_SPEED)tmpPWM_DutySpd = TARGET_SPEED;if(tmpPWM_DutySpd <= -TARGET_SPEED)tmpPWM_DutySpd = -TARGET_SPEED;}}/* 速度环周期50ms */if(uwTick % 50 == 0){/* 获得当前速度 */Sample_Pulse = (OverflowCount*CNT_MAX) + (int32_t)__HAL_TIM_GET_COUNTER(&htimx_Encoder);Spd_PPS = Sample_Pulse - LastSample_Pulse;LastSample_Pulse = Sample_Pulse ;/* 11线编码器,270减速比,一圈脉冲信号是11*270*4 PPR */Spd_RPM = ((((float)Spd_PPS/(float)PPR)*20.0f)*(float)60);//单位是rpm
///* 计算PID结果 */if(Start_flag == 1){sPID.SetPoint = tmpPWM_DutySpd;PWM_Duty = SpdPIDCalc(Spd_RPM);if(PWM_Duty < 0){Motor_Dir = CW;BDDCMOTOR_DIR_CW();PWM_Duty = -PWM_Duty;}else{Motor_Dir = CCW;BDDCMOTOR_DIR_CCW();}__HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,PWM_Duty );}printf("LOC:%d Sped: %2.2f r/m \n",Sample_Pulse,Spd_RPM );}
}/********************* PID参数初始化 ********************************/
/*** 函数功能: PID参数初始化* 输入参数: 无* 返 回 值: 无* 说    明: 无*/
void PID_ParamInit()
{Sample_Pulse = (OverflowCount*CNT_MAX) + (int32_t)__HAL_TIM_GET_COUNTER(&htimx_Encoder);if(Sample_Pulse != 0){Sample_Pulse = 0;__HAL_TIM_SET_COUNTER(&htimx_Encoder,0);}sPID.LastError = 0;               // Error[-1]sPID.PrevError = 0;               // Error[-2]sPID.SumError = 0;sPID.Proportion = SPD_P_DATA; // 比例常数 Proportional ConstsPID.Integral = SPD_I_DATA;   // 积分常数  Integral ConstsPID.Derivative = SPD_D_DATA; // 微分常数 Derivative ConstsPID.SetPoint = TARGET_SPEED;     // 设定目标Desired ValuelPID.LastError = 0;               // Error[-1]lPID.PrevError = 0;               // Error[-2]lPID.SumError = 0;lPID.Proportion = LOC_P_DATA; // 比例常数 Proportional ConstlPID.Integral = LOC_I_DATA;   // 积分常数  Integral ConstlPID.Derivative = LOC_D_DATA; // 微分常数 Derivative ConstlPID.SetPoint = TARGET_LOC;     // 设定目标Desired Value
}/******************** 电流闭环 PID 控制设计 ************************************//*** 函数名称:速度闭环PID控制设计* 输入参数:当前控制量* 返 回 值:目标控制量* 说    明:无*/
int32_t SpdPIDCalc(float NextPoint)
{float iError,dError;iError = sPID.SetPoint - NextPoint; //偏差if((iError<0.2f )&& (iError>-0.2f))iError = 0.0f;sPID.SumError += iError; //积分/* 设定积分上限 */if(lPID.SumError >= TARGET_SPEED*10)lPID.SumError = TARGET_SPEED*10;if(lPID.SumError <= -TARGET_SPEED*10)lPID.SumError = -TARGET_SPEED*10;dError = iError - sPID.LastError; //微分sPID.LastError = iError;return (int32_t)(sPID.Proportion * (float)iError //比例项+ sPID.Integral * (float)sPID.SumError //积分项+ sPID.Derivative * (float)dError); //微分项
}
/*** 函数名称:位置闭环PID控制设计* 输入参数:当前控制量* 返 回 值:目标控制量* 说    明:无*/
int32_t LocPIDCalc(int32_t NextPoint)
{int32_t iError,dError;iError = lPID.SetPoint - NextPoint; //偏差/* 设定闭环死区 */if((iError >= -50) && (iError <= 50)){iError = 0;lPID.SumError /= 2.0;}/* 积分分离 */if((iError >= -1000) && (iError <= 1000)){lPID.SumError += iError; //积分/* 设定积分上限 */if(lPID.SumError >= 1000)lPID.SumError = 1000;if(lPID.SumError <= -1000)lPID.SumError = -1000;}dError = iError - lPID.LastError; //微分lPID.LastError = iError;return (int32_t)(lPID.Proportion * (float)iError //比例项+ lPID.Integral * (float)lPID.SumError //积分项+ lPID.Derivative * (float)dError);    //微分项
}

<iframe src="//player.bilibili.com/player.html?aid=501300008&bvid=BV1yN411d7EJ&cid=285872452&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>

完成源代码链接

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

  1. STM32 电机教程 2 - 直流有刷电机转动控制

    前言 有刷电机是大家最早接触的一类电机,中学时物理课堂上介绍电动机也是以它为模型来展示的.有刷电机的主要结构就是定子+转子+电刷,通过旋转磁场获得转动力矩,从而输出动能.电刷与换向器不断接触摩擦,在转 ...

  2. 【电机应用控制】——直流有刷电机驱动板/编码器介绍PID算法实操代码思路

    目录 前言 一.电机简介 二.直流有刷电机 1.基本知识 2.直流有刷驱动板 3.编码器介绍 三.PID算法 四.实操思路 1.单环控制 2.双环控制 3.三环控制 拓:闭环死区 总结 前言 声明:学 ...

  3. proteus如何添加stm32_【Proteus】单片机H桥驱动24V直流有刷电机

    前言 一般有关直流有刷电机的仿真都是直接高低电平驱动,或者ULN2003,这种电路是只能驱动小电压小功率的电机的,如果碰到电压稍高一些,电流大一些的电机,2003驱动是驱动不起来的,这时候对于大电流的 ...

  4. 直流有刷电机及Matlab/Simulink驱动仿真

    文章目录 前言 一.直流有刷电机简介 二.直流有刷电机的工作原理 三.直流有刷电机的驱动及仿真 3.1.Matlab/Simulink仿真 3.2.仿真结果分析 3.3.直流有刷电机的换向控制 3.4 ...

  5. 基于stm32F4的项目总结:控制层设计(四)直流有刷电机驱动基础知识

    直流有刷电机介绍 ​ 直流有刷电机(Brushed DC motor)具有结构简单.易于控制.成本低等特点,在一些功能简单的应用场合,或者说在能够满足必要的性能.低成本和足够的可靠性的前提下,直流有刷 ...

  6. stm32f103vet6通过L298N驱动12V直流无刷电机过程含代码

    一.传统开头介绍一下L298N电机驱动模块 L298N是ST公司生产的一种高电压.大电流电机驱动芯片. 该芯片采用15脚封装.主要特点是:工作电压高,最高工作电压可达46V:输出电流大,瞬间峰值电流可 ...

  7. stm32和电机开发(直流有刷电机和步进电机)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 很多的课程都喜欢把电机原理完完整整讲一遍.但是对于控制的同学,这部分内容是否真的需要值得商榷.做 ...

  8. 直流有刷电机驱动项目需求分析

    文章来源:直流有刷电机驱动项目需求分析,超实用! 一. 项目名称:<直流电机驱动器设计> 二. 项目需求分析: 我们想设计一款直流有刷电机驱动器,那么在设计驱动器之前,我们需要明确驱动器的 ...

  9. 有刷电机驱动专题-直流有刷电机调速电路分析

    最近有一个项目要求驱动一个60V的直流有刷电机调速,之前做的都是低压电的驱动,48V的已经是较高的,现在需要做一个60V的驱动,不好搞. 网上买了一个别人的调压模块来研究: 可以看到接口非常简单,只有 ...

最新文章

  1. html5新年网页做给父母的,春节回家,要陪父母做这十件小事
  2. Exchange server 2003迁移到2010之升级默认地址簿及地址策略
  3. Java 大数类BigInteger与BigDecimal详细介绍(配蓝桥杯例题讲解)
  4. 【图像处理】图像内插“最近邻插值 最近邻内插法(Nearest Neighbour Interpolate)”代码演示(调整图像大小、放大、缩小)
  5. python os 文件锁_python 中给文件加锁——fcntl模块
  6. Error:java: 无效的目标发行版: 11解决方案
  7. 【快讯】阿里云张建锋:数据成为经济发展的新生产要素
  8. mcq 队列_MCQ | 8086微处理器中的字符串操作指令
  9. 信号生成及DFT的python实现
  10. 《深入浅出DPDK》读书笔记(八):网卡性能优化(异步中断模式、轮询模式、混和中断轮询模式)
  11. 佳能hdr_佳能发布Cinema EOS系统首款RF卡口 4K数字电影摄影机EOS C70
  12. 干净卸载sqlserver2019 亲测有效!
  13. 计算机数字模拟仿真软件,实时数字仿真系统
  14. 高级Java程序员必备:《IDEA问题库》常见问题及解决方案,提升开发效率(JAVA 小虚竹)
  15. 阿里云云服务器 ECS基础知识
  16. file对象转换为Muti文件对象工具类
  17. ffmpeg将mp4转为m4a,m4a转mp3,mp3转ogg
  18. 计算机专业的文献翻译,计算机专业外文文献翻译
  19. face_recognition人脸检测
  20. python做马尔科夫模型预测法_python实现隐马尔科夫模型HMM

热门文章

  1. 《文明之光 第二册》一一16.1 寻找冯·布劳恩(1)
  2. 把gphoto2弄到Android手机上,来控制单反相机
  3. 电磁流量计在复杂工业管道中的选型与安装
  4. IF:6+ 益气化瘀汤诱导胃癌铁死亡的网络药理学研究及实验验证
  5. 【HTML】绝地求生
  6. 数智融合 | 美格智能助力AIGC产业迈向新未来
  7. python随机模块 无范围_Python模块:生成随机数模块random
  8. 【PyQt5】解决:ibpng warning: iCCP: known incorrect sRGB profile / iCCP: cHRM chunk does not
  9. 跟老韩学安全之信息收集
  10. 卫生统计学第2版_卫生统计学第二版