标题:
STM32步进电机PID速度环控速源码
[打印本页]
作者:
Leo.Lee
时间:
2018-11-9 15:21
标题:
STM32步进电机PID速度环控速源码
【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脉冲给步进电机驱动器,电机以目标速度持续转动,同时在
串口助手上每秒显示一次当前位置,速度,捕获值等信息.
单片机源程序如下:
/**
******************************************************************************
* 文件名程: main.c
* 作 者: 硬石嵌入式开发团队
* 版 本: V1.1
* 功 能: 基于PID速度环的步进电机速度调节
******************************************************************************
* 说明:
* 本例程配套硬石stm32开发板YS-F1Pro使用。
*
******************************************************************************
*/
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
#include "StepMotor/bsp_STEPMOTOR.h"
#include "usart/bsp_debug_usart.h"
#include "EncoderTIM/bsp_EncoderTIM.h"
#include <stdlib.h>
#include <string.h>
/* 私有类型定义 --------------------------------------------------------------*/
typedef struct
{
__IO float SetPoint; // 目标值 单位:mm/s
__IO int LastError; // 前一次误差
__IO int PrevError; // 前两次误差
__IO long SumError; // 累计误差
__IO double Proportion; // Kp系数
__IO double Integral; // Ki系数
__IO double Derivative; // Kd系数
}PID;
/* 私有宏定义 ----------------------------------------------------------------*/
#define TXDCYCLE 1000 // 数据发送周期;单位:ms
#define SAMPLING 0x01 // 采样标记
#define TXD 0x02 // 发送数据标记
#define abs(x) ((x)<0?(-x):(x))
#define SENDBUFF_SIZE 100 // 串口DMA发送缓冲区大小
/* 私有变量 ------------------------------------------------------------------*/
__IO static PID vPID;
__IO uint16_t time_count = 0; // 时间计数,每1ms增加一(与滴答定时器频率有关)
__IO uint8_t Time_Flag = 0; // 任务时间标记
/* 扩展变量 ------------------------------------------------------------------*/
extern int16_t OverflowCount; //编码器计数溢出 计数器
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
* 函数功能:增量式PID速度环计算
* 输入参数:NextPoint 由编码器得到的速度值
* TargetVal 目标值
* 返 回 值:经过PID运算得到的增量值
* 说 明:增量式 PID 速度环控制设计,计算得到的结果仍然是速度值
*/
float IncPIDCalc(int NextPoint,float TargetVal) //临时变量,期望值
{
float iError = 0,iIncpid = 0; //当前误差
iError = TargetVal - NextPoint; // 增量计算
if((iError<0.5)&&(iError>-0.5))
iError = 0; // |e| < 0.5,不做调整
iIncpid=(vPID.Proportion * iError) // E[k]项
-(vPID.Integral * vPID.LastError) // E[k-1]项
+(vPID.Derivative * vPID.PrevError); // E[k-2]项
vPID.PrevError=vPID.LastError; // 存储误差,用于下次计算
vPID.LastError = iError;
return(iIncpid); // 返回增量值
}
/**
* 函数功能: 系统时钟配置
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 外部晶振,8MHz
RCC_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; // 系统时钟:72MHz
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟:72MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // APB1时钟:36MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟:72MHz
HAL_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, 1, 0);
}
/**
* 函数功能: PID结构体初始化
* 输入参数: 无
* 返 回 值: 无
* 说 明: 初始化PID参数
*/
void Init_PID()
{
vPID.SetPoint = 5; // 目标值 单位:mm/s
vPID.Proportion = 0.11; // Kp系数
vPID.Derivative = 0.03; // Ki系数
vPID.Integral = 0.12; // Kd系数
vPID.LastError = 0;
vPID.PrevError = 0;
vPID.SumError = 0;
}
/**
* 函数功能: 主函数.
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
int main(void)
{
static float Exp_Val = 0; // PID计算出来的期望值
float Vel_Target; // 速度目标值
uint16_t SUM_Pulse = 0; // 1秒内的总脉冲
int16_t MSF = 0; // 电机反馈速度
__IO int32_t CaptureNumber=0; // 输入捕获数
__IO int32_t Last_CaptureNumber=0; // 上一次捕获值
uint8_t aTxBuffer[SENDBUFF_SIZE]; // 串口DMA发送缓冲区
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
Init_PID();
/* 调试串口初始化 */
MX_DEBUG_USART_Init();
/* 编码器定时器初始化并配置输入捕获功能 */
ENCODER_TIMx_Init();
/* 启动编码器接口 */
HAL_TIM_Encoder_Start(&htimx_Encoder, TIM_CHANNEL_ALL);
HAL_Delay(10);
/* 步进电机定时器初始化*/
STEPMOTOR_TIMx_Init();
/* 首先禁止步进电机动作*/
STEPMOTOR_OUTPUT_DISABLE();
/* 启动定时器 */
HAL_TIM_Base_Start(&htimx_STEPMOTOR);
/* 启动比较输出并使能中断 */
HAL_TIM_OC_Start_IT(&htimx_STEPMOTOR,TIM_CHANNEL_1);
/* 目标值单位为:mm/s,这里需要转换为频率
f = v*PPM;
PPM是每mm的编码器脉冲数,得到的F就是每秒的脉冲数 (Pulse/s,也就是频率:Hz)
Vel_Target = f/(1000/20);得到每个采样周期(20ms)的脉冲数(Pulse/ms)
*/
Vel_Target = (vPID.SetPoint*P_PERIOD);//每单位采样周期内的脉冲数(频率)
/* 无限循环 */
while (1)
{
//采样和控制周期为20ms
if(Time_Flag & SAMPLING)
{
//获得编码器的脉冲值
CaptureNumber = OverflowCount*65535 + __HAL_TIM_GET_COUNTER(&htimx_Encoder);
//M法 测速度
MSF = CaptureNumber - Last_CaptureNumber;
Last_CaptureNumber = CaptureNumber;
MSF = abs(MSF);
//对速度进行累计,得到1s内的脉冲数
SUM_Pulse += MSF;
Exp_Val += IncPIDCalc(MSF,Vel_Target);
Exp_Val = abs(Exp_Val);
/* 经过PID计算得到的结果是编码器的输出期望值的增量,
需要转换为步进电机的控制量(频率值),这里乘上一个系数6400/2400
*/
STEPMOTOR_Motion_Ctrl(CW,Exp_Val*FEEDBACK_CONST);//乘上一个系数,6400/2400,将PID计算结果转换为步进电机的频率(速度)
Time_Flag &= ~SAMPLING;
}
//数据发送周期为1s
if(Time_Flag & TXD)
{
sprintf(aTxBuffer,"捕获值:%d--速度:%.1f mm/s\n",CaptureNumber,(float)SUM_Pulse*MPP);
sprintf(aTxBuffer+strlen((const char*)aTxBuffer),"1s内编码器计数值:%d\n",SUM_Pulse);
HAL_UART_Transmit_DMA(&husart_debug, aTxBuffer, strlen((const char*)aTxBuffer));
SUM_Pulse = 0;
Time_Flag &= ~TXD;
}
}
}
/**
* 函数功能: 系统滴答定时器中断回调函数
* 输入参数: 无
* 返 回 值: 无
* 说 明: 每发生一次滴答定时器中断进入该回调函数一次
*/
void HAL_SYSTICK_Callback(void)
{
// 每1ms自动增一
time_count++;
if(time_count%(SAMPLING_PERIOD) == 0) // 20ms
{
Time_Flag |= SAMPLING;
}
if(time_count >= TXDCYCLE) // 1s
{
Time_Flag |= TXD;
time_count = 0;
}
}
/******************* (C) COPYRIGHT 2015-2020 硬石嵌入式开发团队 *****END OF FILE****/
复制代码
所有资料51hei提供下载:
YSF1_HAL_MOTOR-018. 步进电机速度环控速.rar
(3.23 MB, 下载次数: 304)
2018-11-9 15:21 上传
点击文件名下载附件
下载积分: 黑币 -5
作者:
admin
时间:
2018-11-11 01:47
好资料,51黑有你更精彩!!!
作者:
hzd530
时间:
2018-11-11 10:14
资料不错,程序注释也比较详细,看起来就不那么累了
作者:
zhb7723
时间:
2019-4-25 22:36
感谢楼主分享
作者:
lyx831
时间:
2020-6-18 10:16
好资料,感谢分享
作者:
echowjk
时间:
2020-6-23 12:00
感谢楼主分享
作者:
15532112031
时间:
2020-7-3 10:01
有原理图吗
作者:
huangjinchuan12
时间:
2020-12-7 16:05
有集成加减速的吗,加上加减速的PID闭环怎么做
作者:
杨重阳
时间:
2020-12-7 16:20
好用。。。。。。
作者:
嘉哥呀
时间:
2021-1-20 13:43
这编码器和步进电机是怎样连的?
作者:
rundstedt
时间:
2021-1-22 20:22
嘉哥呀 发表于 2021-1-20 13:43
这编码器和步进电机是怎样连的?
安装到同一根主轴即可。但是楼主思路有问题,步进电机用于廉价低生产率场合,配编码器太贵而且无法明显提高性能,唯一优点是丢步会马上发现。真的不差钱的高效率生产场合,是要用伺服电机+编码器的。
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1