一、编码电机驱动
编码电机的详情可以查看此篇文章:
stm32平衡小车--(1)JGB-520减速电机+tb6612(附测试代码)_jgb520-CSDN博客
简单来说,编码电机的驱动主要是给一个 PWM 和一个正负级就能驱动。PWM 的大小决定转速快慢,电机的两个电源正负极不同决定了旋转的方向不同。
1、PWM设置:
PWM 的值可以在图形化配置界面进行配置,同时也可以通过如下函数进行设置:第一个参数是定时器,第二个是捕获比较值,第三个是通道。
            
            
              cs
              
              
            
          
          void DL_Timer_setCaptureCompareValue(
    GPTIMER_Regs *gptimer, uint32_t value, DL_TIMER_CC_INDEX ccIndex);
        如下是设置占空比的一个函数。第一个参数是捕获比较值,第二个参数是设置通道,我是用一个定时器输出两路 PWM,所以需要两个通道。因为设置的是下降沿触发,系统主频是80MHz,进行了10000分频,所以计数值就是8000。duty = 7999 - 7999 * (duty/3200.0);就可以将捕获比较值从8000映射到3200。然后下面 if 来判断设置PWM输出的通道。
            
            
              cs
              
              
            
          
          // 设置电机PWM占空比,取值范围为3200~0,对应占空比为100%~0%
void Motor_SetPwm(float duty,uint8_t channel) {
    duty = 7999 - 7999 * (duty/3200.0);
    if(channel==0){
        DL_TimerA_setCaptureCompareValue(PWM_MOTOR_INST , duty, DL_TIMER_CC_0_INDEX );  // 设置定时器捕获比较值
    }else {
        DL_TimerA_setCaptureCompareValue(PWM_MOTOR_INST , duty, DL_TIMER_CC_1_INDEX );  // 设置定时器捕获比较值
    }
}
        PWM 图形化配置:



2、电机方向设置
此函数与上面的参数一致。这两个函数分别用来设置引脚低电平和高电平。
            
            
              cs
              
              
            
          
          DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL1_PORT, GPIO_MOTOR_PIN_FL1_PIN);
DL_GPIO_setPins(GPIO_MOTOR_PIN_FL2_PORT, GPIO_MOTOR_PIN_FL2_PIN);
        下面函数时根据驱动电机。duty 值为正,电机正转,duty 值为负,电机反转;channel 值为0,驱动右轮,channel 值为1,驱动左轮。
            
            
              cs
              
              
            
          
          // 电机前进控制函数
void Set_Speed(float duty, uint8_t channel) {
    if(channel==0){     //判断左右轮
        if(duty>0){     //判断正反转
            Motor_SetPwm(duty,channel);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL1_PORT, GPIO_MOTOR_PIN_FL1_PIN);
            DL_GPIO_setPins(GPIO_MOTOR_PIN_FL2_PORT, GPIO_MOTOR_PIN_FL2_PIN);
        }else if(duty<0){
            Motor_SetPwm(-duty,channel);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL2_PORT, GPIO_MOTOR_PIN_FL2_PIN);
            DL_GPIO_setPins(GPIO_MOTOR_PIN_FL1_PORT, GPIO_MOTOR_PIN_FL1_PIN);
        }else {
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL1_PORT, GPIO_MOTOR_PIN_FL1_PIN);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FL2_PORT, GPIO_MOTOR_PIN_FL2_PIN);
        }
    }else {
        if(duty>0){
            Motor_SetPwm(duty,channel);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FR1_PORT, GPIO_MOTOR_PIN_FR1_PIN);
            DL_GPIO_setPins(GPIO_MOTOR_PIN_FR2_PORT, GPIO_MOTOR_PIN_FR2_PIN);
        }else if(duty<0){
            Motor_SetPwm(-duty,channel);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FR2_PORT, GPIO_MOTOR_PIN_FR2_PIN);
            DL_GPIO_setPins(GPIO_MOTOR_PIN_FR1_PORT, GPIO_MOTOR_PIN_FR1_PIN);
        }else {
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FR1_PORT, GPIO_MOTOR_PIN_FR1_PIN);
            DL_GPIO_clearPins(GPIO_MOTOR_PIN_FR2_PORT, GPIO_MOTOR_PIN_FR2_PIN);
        }
    }
}
        二、通用PID
其中增量式 PID 不需要初始阈值,其输出主要是与上次误差与上上次误差有关,适合调节稳定速度;
位置式 PID 需要初始阈值,其输出的结果与历史的状态有关,适合将速度维持在某个定值。
pid.c
            
            
              cs
              
              
            
          
          #include "pid.h"
#include <math.h>
// 初始化PID结构体
void PID_Init(PID *s_PID, PID_VAR_TYPE set_point, PID_VAR_TYPE Proportion,
              PID_VAR_TYPE Integral, PID_VAR_TYPE Derivative)
{
    // 初始化PID参数
    s_PID->SetPoint = set_point;            // 设定目标值
    s_PID->Proportion = Proportion;         // 比例系数
    s_PID->Integral = Integral;             // 积分系数
    s_PID->Derivative = Derivative;         // 微分系数
    s_PID->Error = 0;                       // 当前误差
    s_PID->LastError = 0;                   // 上一次误差
    s_PID->PrevError = 0;                   // 上上次误差
    s_PID->SumError = 0;                    // 总误差
    s_PID->LastResult = 0;                  // 上一次计算结果
    s_PID->Result = 0;                      // 当前计算结果
    s_PID->OutMax = DEFAULT_PID_OUT_MAX;    // 输出上限
    s_PID->OutMin = DEFAULT_PID_OUT_MIN;    // 输出下限
    s_PID->IntegralMax = DEFAULT_PID_INTEGRAL_OUT_MAX;  // 积分输出上限
    s_PID->IntegralMin = DEFAULT_PID_INTEGRAL_OUT_MIN;  // 积分输出下限
}
// 设置PID的目标值
void PID_SetPoint(PID *s_PID, PID_VAR_TYPE set_point)
{
    s_PID->SetPoint = set_point;
}
// 设置PID的输出范围
void PID_SetOutRange(PID *s_PID, PID_VAR_TYPE outMax, PID_VAR_TYPE outMin)
{
    s_PID->OutMax = outMax;
    s_PID->OutMin = outMin;
}
// 设置PID的积分输出范围
void PID_SetIntegralOutRange(PID *s_PID, PID_VAR_TYPE outMax, PID_VAR_TYPE outMin)
{
    s_PID->IntegralMax = outMax;
    s_PID->IntegralMin = outMin;
}
// 增量型PID计算
PID_VAR_TYPE Increment_PID_Cal(PID *s_PID, PID_VAR_TYPE now_point)
{
    s_PID->LastResult = s_PID->Result;   // 保存上一次的计算结果
    // 计算误差
    s_PID->Error = s_PID->SetPoint - now_point;
    // PID计算
    s_PID->Result =
        s_PID->LastResult +
        s_PID->Proportion * (s_PID->Error - s_PID->LastError) +  // 比例项
        s_PID->Integral * s_PID->Error +                          // 积分项
        s_PID->Derivative * (s_PID->Error - 2 * s_PID->LastError + s_PID->PrevError);  // 微分项
    s_PID->PrevError = s_PID->LastError;   // 更新上上次误差
    s_PID->LastError = s_PID->Error;       // 更新上一次误差
    // 限制输出范围
    if (s_PID->Result > s_PID->OutMax)
        s_PID->Result = s_PID->OutMax;
    else if (s_PID->Result < s_PID->OutMin)
        s_PID->Result = s_PID->OutMin;
    return s_PID->Result;
}
// 位置型PID计算
PID_VAR_TYPE Position_PID_Cal(PID *s_PID, PID_VAR_TYPE now_point)
{
    s_PID->LastResult = s_PID->Result;   // 保存上一次的计算结果
    // 计算误差
    s_PID->Error = s_PID->SetPoint - now_point;
    s_PID->SumError += s_PID->Error;    // 更新总误差
    // 限制积分输出范围
    PID_VAR_TYPE IOutValue = s_PID->SumError * s_PID->Integral;
    if (IOutValue > s_PID->IntegralMax)
        IOutValue = s_PID->IntegralMax;
    else if (IOutValue < s_PID->IntegralMin)
        IOutValue = s_PID->IntegralMin;
    // PID计算
    s_PID->Result =
        s_PID->Proportion * s_PID->Error +      // 比例项
        IOutValue +                            // 积分项
        s_PID->Derivative * (s_PID->Error - s_PID->LastError);  // 微分项
    s_PID->PrevError = s_PID->LastError;   // 更新上上次误差
    s_PID->LastError = s_PID->Error;       // 更新上一次误差
    // 限制输出范围
    if (s_PID->Result > s_PID->OutMax)
        s_PID->Result = s_PID->OutMax;
    else if (s_PID->Result < s_PID->OutMin)
        s_PID->Result = s_PID->OutMin;
    return s_PID->Result;
}
// 综合型PID计算
PID_VAR_TYPE PID_Cal(PID *s_PID, PID_VAR_TYPE now_point)
{
    s_PID->LastResult = s_PID->Result;   // 保存上一次的计算结果
    // 计算误差
    s_PID->Error = s_PID->SetPoint - now_point;
    s_PID->SumError += s_PID->Error;    // 更新总误差
    // 限制积分输出范围
    PID_VAR_TYPE IOutValue = s_PID->SumError * s_PID->Integral;
    if (IOutValue > s_PID->IntegralMax)
        IOutValue = s_PID->IntegralMax;
    else if (IOutValue < s_PID->IntegralMin)
        IOutValue = s_PID->IntegralMin;
    // PID计算
    s_PID->Result =
        s_PID->Proportion * (s_PID->Error + IOutValue) +   // 比例项
        s_PID->Derivative * (s_PID->Error - s_PID->LastError);  // 微分项
    s_PID->PrevError = s_PID->LastError;   // 更新上上次误差
    s_PID->LastError = s_PID->Error;       // 更新上一次误差
    // 限制输出范围
    if (s_PID->Result > s_PID->OutMax)
        s_PID->Result = s_PID->OutMax;
    else if (s_PID->Result < s_PID->OutMin)
        s_PID->Result = s_PID->OutMin;
    return s_PID->Result;
}
        pid.h
            
            
              cs
              
              
            
          
          #ifndef __PID_H
#define __PID_H	  
//PID输出范围宏定义
#define DEFAULT_PID_OUT_MAX               ( 3200)
#define DEFAULT_PID_OUT_MIN               (-3200)
//PID积分范围宏定义
#define DEFAULT_PID_INTEGRAL_OUT_MAX      ( 400)
#define DEFAULT_PID_INTEGRAL_OUT_MIN      (-400)
//PID变量类型宏定义
#define PID_VAR_TYPE                            float     //int
//定义结构体
typedef struct
{
    PID_VAR_TYPE SetPoint;      // 设定目标 
    PID_VAR_TYPE Proportion;    // 比例常数
    PID_VAR_TYPE Integral;      // 积分常数
    PID_VAR_TYPE Derivative;    // 微分常数 
    PID_VAR_TYPE SumError;      // 积分和
    PID_VAR_TYPE Error;         // 误差
    PID_VAR_TYPE LastError;     // 上次误差 
    PID_VAR_TYPE PrevError;     // 前一次误差   
    PID_VAR_TYPE LastResult;    // 上次计算结果
    PID_VAR_TYPE Result;        // 当前计算结果
    PID_VAR_TYPE OutMax;        // 输出限幅最大值
    PID_VAR_TYPE OutMin;        // 输出限幅最小值
    PID_VAR_TYPE IntegralMax;   // 积分限幅最大值
    PID_VAR_TYPE IntegralMin;   // 积分限幅最小值
} PID;
extern void PID_Init(PID *s_PID, PID_VAR_TYPE set_point,
                        PID_VAR_TYPE Proportion,
                        PID_VAR_TYPE Integral,
                        PID_VAR_TYPE Derivative);  //PID初始化
extern void PID_SetOutRange(PID *s_PID, PID_VAR_TYPE outMax, PID_VAR_TYPE outMin);          //设置PID输出范围
extern void PID_SetIntegralOutRange(PID *s_PID, PID_VAR_TYPE outMax, PID_VAR_TYPE outMin);  //设置PID积分范围
extern void PID_SetPoint(PID *s_PID, PID_VAR_TYPE set_point);                               //设置目标值
extern PID_VAR_TYPE Increment_PID_Cal(PID *s_PID, PID_VAR_TYPE now_point);  //增量式PID计算
extern PID_VAR_TYPE Position_PID_Cal(PID *s_PID, PID_VAR_TYPE now_point);   //位置式PID计算
extern PID_VAR_TYPE PID_Cal(PID *s_PID, PID_VAR_TYPE now_point);            //比例外置式PID
#endif