单片机直流有刷电机速度环PID控制实验

单片机 :STM32F407

开发板:DMF407电机开发板

平台:keil V5.31

HSE 为8MHZ

HSI为16MHZ

PID算法:

一、速度环PID实验:

主函数

复制代码
int main(void)
{
    uint8_t key;
    uint16_t t;
    uint8_t debug_cmd = 0;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口1初始化,用于上位机调试 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    key_init();                             /* 初始化按键 */
    pid_init();                             /* 初始化PID参数 */
    atim_timx_cplm_pwm_init(8400 - 1 , 0);  /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
    dcmotor_init();                         /* 初始化电机 */
    gtim_timx_encoder_chy_init(0XFFFF, 0);  /* 编码器定时器初始化,不分频直接84M的计数频率 */
    btim_timx_int_init(1000 - 1 , 84 - 1);  /* 基本定时器初始化,1ms计数周期 */

#if DEBUG_ENABLE                            /* 开启调试 */
    
    debug_init();                           /* 初始化调试 */
    debug_send_motorcode(DC_MOTOR);         /* 上传电机类型(直流有刷电机) */
    debug_send_motorstate(IDLE_STATE);      /* 上传电机状态(空闲) */
    
    /* 同步数据(选择第1组PID,目标速度地址,P,I,D参数)到上位机 */
    debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD);
    
#endif

    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
    lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
    lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
    lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);

    while (1)
    {
        key = key_scan(0);                                  /* 按键扫描 */
        if(key == KEY0_PRES)                                /* 当key0按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            g_speed_pid.SetPoint += 30;
            if (g_speed_pid.SetPoint == 0)
            {
                dcmotor_stop();                             /* 停止则立刻响应 */
                g_motor_data.motor_pwm = 0;
                motor_pwm_set(g_motor_data.motor_pwm);      /* 设置电机方向、转速 */
            }
            else
            {
                dcmotor_start();                            /* 开启电机 */
                if (g_speed_pid.SetPoint >= 300)            /* 限速 */
                {
                    g_speed_pid.SetPoint = 300;
                }
#if DEBUG_ENABLE
                debug_send_motorstate(RUN_STATE);           /* 上传电机状态(运行) */
#endif
            }
        }

        else if(key == KEY1_PRES)                           /* 当key1按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            g_speed_pid.SetPoint -= 30;
            if (g_speed_pid.SetPoint == 0)
            {
                dcmotor_stop();                             /* 停止则立刻响应 */
                g_motor_data.motor_pwm = 0;
                motor_pwm_set(g_motor_data.motor_pwm);      /* 设置电机方向、转速 */
            }
            else
            {
                dcmotor_start();                            /* 开启电机 */
                if (g_speed_pid.SetPoint <= -300)           /* 限速 */
                {
                    g_speed_pid.SetPoint = -300;
                }
#if DEBUG_ENABLE
                debug_send_motorstate(RUN_STATE);           /* 上传电机状态(运行) */
#endif
            }
        }

        else if(key == KEY2_PRES)                           /* 当key2按下 */
        {
            dcmotor_stop();                                 /* 停止电机 */
            pid_init();                                     /* 重置pid参数 */
            g_run_flag = 0;                                 /* 标记电机停止 */
            g_motor_data.motor_pwm = 0;
            motor_pwm_set(g_motor_data.motor_pwm);          /* 设置电机转向、速度 */
            
#if DEBUG_ENABLE
            debug_send_motorstate(BREAKED_STATE);           /* 上传电机状态(刹车) */
            debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD);           /* 同步数据到上位机 */
#endif
        }

#if DEBUG_ENABLE
         /* 查询接收PID助手的PID参数 */
        debug_receive_pid(TYPE_PID1, (float *)&g_speed_pid.Proportion,(float *)&g_speed_pid.Integral, (float *)&g_speed_pid.Derivative);
        
        debug_set_point_range(300, -300, 300);              /* 设置目标速度范围 */
        debug_cmd = debug_receive_ctrl_code();              /* 读取上位机指令 */

        if (debug_cmd == BREAKED)                           /* 电机刹车 */
        {
            dcmotor_stop();                                 /* 停止电机 */
            pid_init();                                     /* 重置pid参数 */
            g_run_flag = 0;                                 /* 标记电机停止 */
            g_motor_data.motor_pwm = 0;
            motor_pwm_set(g_motor_data.motor_pwm);          /* 设置电机转向、速度 */
            debug_send_motorstate(BREAKED_STATE);           /* 上传电机状态(刹车) */
            debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD);
        } 
        else if (debug_cmd == RUN_CODE)                     /* 电机运行 */
        {  
            dcmotor_start();                                /* 开启电机 */
            g_speed_pid.SetPoint = 30;                      /* 设置目标速度:30 RPM */
            g_run_flag = 1;                                 /* 标记电机启动 */
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
        }
#endif
        t++;
        if(t % 20 == 0)
        {
            lcd_dis();                                      /* 显示数据 */
            LED0_TOGGLE();                                  /*LED0(红灯) 翻转*/
#if DEBUG_ENABLE
            debug_send_speed(g_motor_data.speed);           /* 发送速度 */
#endif
        }
        delay_ms(10);
    }
}

pid:

复制代码
/* 位置式PID参数相关宏 */
#define  KP      10.0f               /* P参数*/
#define  KI      6.00f               /* I参数*/
#define  KD      0.5f                /* D参数*/
#define  SMAPLSE_PID_SPEED  50       /* 采样周期 单位ms*/

/* PID参数结构体 */
typedef struct
{
    __IO float  SetPoint;            /* 设定目标 */
    __IO float  ActualValue;         /* 期望输出值 */
    __IO float  SumError;            /* 误差累计 */
    __IO float  Proportion;          /* 比例常数 P */
    __IO float  Integral;            /* 积分常数 I */
    __IO float  Derivative;          /* 微分常数 D */
    __IO float  Error;               /* Error[1] */
    __IO float  LastError;           /* Error[-1] */
    __IO float  PrevError;           /* Error[-2] */
} PID_TypeDef;

void pid_init(void)
{
    g_speed_pid.SetPoint = 0;       /* 设定目标值 */
    g_speed_pid.ActualValue = 0.0;  /* 期望输出值 */
    g_speed_pid.SumError = 0.0;     /* 积分值 */
    g_speed_pid.Error = 0.0;        /* Error[1] */
    g_speed_pid.LastError = 0.0;    /* Error[-1] */
    g_speed_pid.PrevError = 0.0;    /* Error[-2] */
    g_speed_pid.Proportion = KP;    /* 比例常数 Proportional Const */
    g_speed_pid.Integral = KI;      /* 积分常数 Integral Const */
    g_speed_pid.Derivative = KD;    /* 微分常数 Derivative Const */ 
}

int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value)
{
    PID->Error = (float)(PID->SetPoint - Feedback_value);                   /* 计算偏差 */
    
#if  INCR_LOCT_SELECT                                                       /* 增量式PID */
    
    PID->ActualValue += (PID->Proportion * (PID->Error - PID->LastError))                          /* 比例环节 */
                        + (PID->Integral * PID->Error)                                             /* 积分环节 */
                        + (PID->Derivative * (PID->Error - 2 * PID->LastError + PID->PrevError));  /* 微分环节 */
    
    PID->PrevError = PID->LastError;                                        /* 存储偏差,用于下次计算 */
    PID->LastError = PID->Error;
    
#else                                                                       /* 位置式PID */
    
    PID->SumError += PID->Error;
    PID->ActualValue = (PID->Proportion * PID->Error)                       /* 比例环节 */
                       + (PID->Integral * PID->SumError)                    /* 积分环节 */
                       + (PID->Derivative * (PID->Error - PID->LastError)); /* 微分环节 */
    PID->LastError = PID->Error;
    
#endif
    return ((int32_t)(PID->ActualValue));                                   /* 返回计算后输出的数值 */
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     static uint8_t val = 0;
     if (htim->Instance == TIM3)
     {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
     }
     else if (htim->Instance == TIM6)
     {
        int Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 5);                                  /* 中位平均值滤除编码器抖动数据,5ms计算一次速度*/
         
        if (val % SMAPLSE_PID_SPEED == 0)                               /* 50ms进行一次pid计算 */
        {
            if (g_run_flag)                                             /* 判断电机是否启动了*/
            { 
                /* PID计算,输出比较值(占空比) */
                g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid, g_motor_data.speed);

                if (g_motor_data.motor_pwm >= 8200)                     /* 限速 */
                {
                    g_motor_data.motor_pwm = 8200;
                }
                else if (g_motor_data.motor_pwm <= -8200)
                {
                    g_motor_data.motor_pwm = -8200;
                }

#if DEBUG_ENABLE  /* 发送基本参数*/

                debug_send_wave_data( 1 ,g_motor_data.speed);                           /* 选择通道1,发送实际速度(波形显示)*/
                debug_send_wave_data( 2 ,g_speed_pid.SetPoint);                         /* 选择通道2,发送目标速度(波形显示)*/
                debug_send_wave_data( 3 ,g_motor_data.motor_pwm * 100 / 8400);          /* 选择通道3,发送占空比(波形显示)*/
#endif
                motor_pwm_set(g_motor_data.motor_pwm);                                  /* 设置电机转速 */
            }
            val = 0;
        }
        val ++;
     }
}

debug:

复制代码
/*********************************** 开发板 ------> 上位机 *******************************************/

/* 数据类别 */
typedef enum
{
    TYPE_ERROR          = 0x0F,      /* 故障类型 */
    TYPE_STATUS         = 0x10,      /* 设备状态 */
    TYPE_SPEED          = 0x11,      /* 速度 */
    TYPE_HAL_ENC        = 0x12,      /* 霍尔、编码器位置值 */
    TYPE_VBUS           = 0x13,      /* 电压 */
    TYPE_AMP            = 0x14,      /* 电流 */
    TYPE_TEMP           = 0x15,      /* 驱动板温度 */
    TYPE_SUM_LEN        = 0x16,      /* 总里程 */
    TYPE_BEM            = 0x17,      /* 反电动势 */
    TYPE_MOTOR_CODE     = 0x18,      /* 电机类型 */
    TYPE_TORQUE         = 0x19,      /* 扭矩 */
    TYPE_POWER          = 0x1A,      /* 功率 */
    TYPE_PID1           = 0x20,      /* 用户PID1参数上报 */
    TYPE_PID2           = 0x21,      /* PID2 */
    TYPE_PID3           = 0x22,      /* PID3 */
    TYPE_PID4           = 0x23,      /* PID4 */
    TYPE_PID5           = 0x24,      /* PID5 */
    TYPE_PID6           = 0x25,      /* PID6 */
    TYPE_PID7           = 0x26,      /* PID7 */
    TYPE_PID8           = 0x27,      /* PID8 */
    TYPE_PID9           = 0x28,      /* PID9 */
    TYPE_PID10          = 0x29,      /* PID10 */
    TYPE_USER_DATA      = 0x30,      /* 波形数据上报 */
}upload_type;

/* 故障类型 */
typedef enum
{
    HALL_ENC_ERROR      = 0x01,      /* 编码器、霍尔错误 */
    OVERSPEED_ERROR     = 0x02,      /* 电机过速 */
    DB_OVERTEMP_ERROR   = 0x04,      /* 驱动板过温 */
    M_OVERTEMP_ERROR    = 0x08,      /* 电机过温 */
    DB_OVERVOL_ERROR    = 0x10,      /* 驱动板过压 */
    DB_UNDERVOL_ERROR   = 0x10,      /* 驱动板欠压 */
    DB_OVERCUR_ERROR    = 0x10,      /* 驱动板过流 */
    UNKNOWN_ERROR       = 0x10,      /* 未知错误 */
}motor_error;

/* 电机状态 */
typedef enum
{
    IDLE_STATE          = 0x00,      /* 空闲状态 */
    RUN_STATE           = 0x01,      /* 运行状态 */
    ERROR_STATE         = 0x02,      /* 错误状态 */
    LRTOUT_STATE        = 0x03,      /* 堵转超时 */
    BREAKED_STATE       = 0x04,      /* 刹车 */
}motor_state;

/* 电机类型 */
typedef enum
{
    DC_MOTOR            = 0x10,      /* 直流有刷电机 */
    BLDC_MOTOR          = 0x11,      /* 直流无刷电机 */
    PMSM_MOTOR          = 0x12,      /* 永磁同步电机 */
    STEP_MOTOR          = 0x13,      /* 步进电机 */
    SERVO_MOTOR         = 0x14,      /* 伺服电机 */
    EXCHANG_MOTOR       = 0x15,      /* 变频器(三相交流异步电机) */
    HELM_MOTOR          = 0x16,      /* 舵机 */
}motor_code;

/* PID参数存放结构体 */
typedef struct
{
    union
    {
        float pidf[3];               /* PID参数发送存放 */
        int8_t pidi8[12];            /* PID参数接收存放 */
    }pid;
}pid_struct;

/* 参数存放结构体(开发板------>上位机) */
typedef struct
{
    uint8_t status;                  /* 电机状态 */
    int16_t speed;                   /* 电机速度 */
    uint8_t hall_p;                  /* 霍尔位置值 */
    uint16_t encode_p;               /* 编码器位置值 */
    float bus_vol;                   /* 电压 */
    float amp[3];                    /* 电流 */
    float temp[2];                   /* 温度 */
    uint64_t sum_len;                /* 总里程 */
    float bem[3];                    /* 反电动势 */
    uint8_t motor_code;              /* 电机类型 */
    float torque;                    /* 扭矩 */
    float power;                     /* 功率 */
    pid_struct pid[10];              /* 10组PID参数(收发共用) */
    int16_t user_data[16];           /* 波形数据 */
}debug_data;

extern debug_data g_debug;           /* 发送变量 */


/*********************************** 上位机  ------>  开发板 *******************************************/

/* 数据类别 */
typedef enum
{
    CMD_GET_ALL_DATA    = 0x19,      /* 获取全部参数 */
    CMD_SET_CTR_CODE    = 0x21,      /* 下发控制指令 */
    CMD_SET_CTR_MODE    = 0x22,      /* 下发控制模式 */
    CMD_SET_SPEED       = 0x23,      /* 设定速度 */
    CMD_SET_TORQUE      = 0x24,      /* 设定转矩 */
    CMD_SET_VF_VOL      = 0x25,      /* 设定VF电压 */
    CMD_SET_VF_IF_FRE   = 0x26,      /* 设定V/F、IF频率 */
    CMD_SET_IF_CUR      = 0x27,      /* 设定IF电流 */
    CMD_SET_DQ_CUR_D    = 0x28,      /* 设定DQ电流D */
    CMD_SET_DQ_CUR_Q    = 0x29,      /* 设定DQ电流Q */
    CMD_SET_PID1        = 0x31,      /* 设定PID1参数 */
    CMD_SET_PID2        = 0x32,      /* PID2 */
    CMD_SET_PID3        = 0x33,      /* PID3 */
    CMD_SET_PID4        = 0x34,      /* PID4 */
    CMD_SET_PID5        = 0x35,      /* PID5 */
    CMD_SET_PID6        = 0x36,      /* PID6 */
    CMD_SET_PID7        = 0x37,      /* PID7 */
    CMD_SET_PID8        = 0x38,      /* PID8 */
    CMD_SET_PID9        = 0x39,      /* PID9 */
    CMD_SET_PID10       = 0x3A,      /* PID10 */
}cmd_type;

/* 下发控制指令 */
typedef enum
{
    HALT_CODE           = 0x01,      /* 停机 */
    RUN_CODE            = 0x02,      /* 运行 */
    BREAKED             = 0x03,      /* 刹车 */
}cmd_code;

/* 下发控制模式 */
typedef enum
{
    SPEED_MODE          = 0x01,      /* 转速模式 */
    TORQUE_MODE         = 0x02,      /* 转矩模式 */
    IF_MODE             = 0x03,      /* IF模式 */
    VF_MODE             = 0x04,      /* VF模式 */
    DQ_MODE             = 0x05,      /* DQ模式 */
}cmd_mode;

/* 参数存放结构体(上位机------>开发板) */
typedef struct
{
    uint8_t Ctrl_code;
    uint8_t Ctrl_mode;
    float *speed;
    float *torque;
    float pid[3];
}debug_data_rev;

void debug_obj_init(debug_data *data)
{
    size_t obj_size = sizeof(debug_data);
    memset(data, 0, (size_t)obj_size);             /* 把指定范围内存清零 */
}

void debug_handle(uint8_t *data)
{
    uint8_t temp[DEBUG_REV_MAX_LEN];
    uint8_t i;

    if (debug_rev_p >= DEBUG_REV_MAX_LEN)          /* 超过缓冲区(数组)最大长度 */
    {
        debug_rev_p = 0;                           /* 地址偏移量清零 */
    }

    debug_rev_data[debug_rev_p] = *(data);         /* 取出数据,存进数组 */

    if (*data == DEBUG_DATA_END)                   /* 判断是否收到帧尾 */
    {
        if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                        /* 数据包长度为5个字节,判断第一个字节是否为帧头 */
        {
            for (i = 0; i < 2; i++)
            {
                temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + i) % DEBUG_REV_MAX_LEN];                         /* 取出帧头、数据类别,5个字节的数据包没有数据域 */
            }

#if EN_CRC                                         /* 进行CRC校验 */
            if (crc16_calc(temp, 2) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 2) % DEBUG_REV_MAX_LEN] << 8) | \
                                         debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 3) % DEBUG_REV_MAX_LEN]))
#endif
            {
                if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 1) % DEBUG_REV_MAX_LEN] == CMD_GET_ALL_DATA)           /* 判断数据类别是否为:获取全部参数 */
                {
                    debug_upload_data(&g_debug, TYPE_STATUS);                                                                    /* 发送电机状态 */
                    debug_upload_data(&g_debug, TYPE_SPEED);                                                                     /* 发送速度值 */
                    debug_upload_data(&g_debug, TYPE_HAL_ENC);                                                                   /* 发送霍尔、编码器位置 */
                    debug_upload_data(&g_debug, TYPE_VBUS);                                                                      /* 发送电压 */
                    debug_upload_data(&g_debug, TYPE_AMP);                                                                       /* 发送电流 */
                    debug_upload_data(&g_debug, TYPE_TEMP);                                                                      /* 发送温度 */
                    debug_upload_data(&g_debug, TYPE_SUM_LEN);                                                                   /* 发送总里程 */
                    debug_upload_data(&g_debug, TYPE_BEM);                                                                       /* 发送反电动势 */
                    debug_upload_data(&g_debug, TYPE_MOTOR_CODE);                                                                /* 发送电机类型 */
                    for (i = TYPE_PID1; i < TYPE_PID10; i++)
                    {
                        debug_upload_data(&g_debug, i);                                                                          /* 发送PID参数 */
                    }
                }
            }
        }

        if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                        /* 数据包长度为6个字节,判断第一个字节是否为帧头 */
        {
            for (i = 0; i < 3; i++)
            {
                temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + i) % DEBUG_REV_MAX_LEN];                         /* 取出帧头、数据类别、数据域 */
            }
#if EN_CRC                                         /* 进行CRC校验 */
            if (crc16_calc(temp, 3) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 3) % DEBUG_REV_MAX_LEN] << 8) | \
                                         debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 4) % DEBUG_REV_MAX_LEN]))
#endif
            {
                switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 1) % DEBUG_REV_MAX_LEN])                           /* 判断数据类别 */
                {
                    case CMD_SET_CTR_CODE:                                                                                       /* 下发控制指令 */
                        debug_rev.Ctrl_code = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 2) % DEBUG_REV_MAX_LEN];
                        break;

                    case CMD_SET_CTR_MODE:                                                                                       /* 下发控制模式 */
                        debug_rev.Ctrl_mode = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 2) % DEBUG_REV_MAX_LEN];
                        break;
                }
            }
        }

        if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                        /* 数据包长度为7个字节,判断第一个字节是否为帧头 */
        {
            for (i = 0; i < 4; i++)
            {
                temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + i) % DEBUG_REV_MAX_LEN];                         /* 取出帧头、数据类别、数据域 */
            }
#if EN_CRC                                         /* 进行CRC校验 */
            if (crc16_calc(temp, 4) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 4) % DEBUG_REV_MAX_LEN] << 8) | \
                                         debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 5) % DEBUG_REV_MAX_LEN]))
#endif
            {
                switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 1) % DEBUG_REV_MAX_LEN])                           /* 判断数据类别 */
                {
                    case CMD_SET_SPEED:                                                                                          /* 设定电机速度 */
                        *(debug_rev.speed) = (int16_t)((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 2) % DEBUG_REV_MAX_LEN] << 8) | \
                                                        debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 3) % DEBUG_REV_MAX_LEN]);
                        break;

                    case CMD_SET_TORQUE:                                                                                         /* 设定转矩 */
                        *(debug_rev.torque) = (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 2) % DEBUG_REV_MAX_LEN] << 8) | \
                                               debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 3) % DEBUG_REV_MAX_LEN];
                        break;
                }
            }
        }

        if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                       /* 数据包长度为17个字节,判断第一个字节是否为帧头 */
        {
            for (i = 0; i < 14; i++)
            {
                temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + i) % DEBUG_REV_MAX_LEN];                        /* 取出帧头、数据类别、数据域 */
            }
#if EN_CRC                                          /* 进行CRC校验 */
            if (crc16_calc(temp, 14) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 14) % DEBUG_REV_MAX_LEN] << 8) | \
                                          debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 15) % DEBUG_REV_MAX_LEN]))
#endif
            {
                switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 1) % DEBUG_REV_MAX_LEN])                          /* 判断数据类别 */
                {
                    case CMD_SET_PID1:
                    case CMD_SET_PID2:
                    case CMD_SET_PID3:
                    case CMD_SET_PID4:
                    case CMD_SET_PID5:
                    case CMD_SET_PID6:
                    case CMD_SET_PID7:
                    case CMD_SET_PID8:
                    case CMD_SET_PID9:
                    case CMD_SET_PID10:
                    for (i = 0; i < 12; i++)                                                                                     /* 接收设定的PID参数 */
                    {
                        g_debug.pid[debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 1) % DEBUG_REV_MAX_LEN] - CMD_SET_PID1].pid.pidi8[i] = \
                                    debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 2 + i) % DEBUG_REV_MAX_LEN];
                    }
                    break;
                }
            }
        }
    }

    debug_rev_p ++;
}

void debug_upload_data(debug_data *data, uint8_t upload_type)
{
    uint8_t cur_data, i;
    uint8_t upload_data[37];                                            /* 数据上传数组 */
    upload_data[0] = DEBUG_DATA_HEAD;                                   /* 数据包第1个字节(数组第0个元素),固定为帧头 */
    cur_data = 2;                                                       /* 数据域从第3个字节(数组第2个元素)开始 */

    switch (upload_type)                                                /* 判断数据类别 */
    {
        case TYPE_STATUS:                                               /* 设备状态 */
            upload_data[1] = upload_type;                               /* 数据包第2个字节(数组第1个元素),固定为数据类别 */
            upload_data[cur_data++] = data->status;                     /* 存入要发送的数据域 */
            break;

        case TYPE_SPEED:                                                /* 电机速度 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (data->speed >> 8) & 0xFF;        /* 先存入速度值高8位(小端模式中u16赋给u8类型,只取低8位) */
            upload_data[cur_data++] = data->speed & 0xFF;               /* 再存入速度值低8位 */
            break;

        case TYPE_HAL_ENC:                                              /* 霍尔、编码器位置值 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (data->hall_p) & 0x08;            /* 存入霍尔位置值 */
            upload_data[cur_data++] = (data->encode_p >> 8) & 0xFF;     /* 存入编码器位置值高8位 */
            upload_data[cur_data++] = (data->encode_p) & 0xFF;          /* 存入编码器位置值低8位 */
            break;

        case TYPE_VBUS:                                                                 /* 电压,范围 0~100.99 V */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = ((uint8_t)data->bus_vol) % 101;                   /* 存入电压值整数部分,整数部分不允许超过100 */
            upload_data[cur_data++] = ((uint16_t)(data->bus_vol * 100)) % 100;          /* 存入电压值小数部分,小数部分不允许超过99 */
            break;

        case TYPE_AMP:                                                                  /* 电流 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (((int16_t)(data->amp[0] * 1000)) >> 8) & 0xFF;   /* 存入U相电流高8位 */
            upload_data[cur_data++] = ((int16_t)(data->amp[0] * 1000)) & 0xFF;          /* 存入U相电流低8位 */
            upload_data[cur_data++] = (((int16_t)(data->amp[1] * 1000)) >> 8) & 0xFF;   /* 存入V相电流高8位 */
            upload_data[cur_data++] = ((int16_t)(data->amp[1] * 1000)) & 0xFF;          /* 存入V相电流低8位 */
            upload_data[cur_data++] = (((int16_t)(data->amp[2] * 1000)) >> 8) & 0xFF;   /* 存入W相电流高8位 */
            upload_data[cur_data++] = ((int16_t)(data->amp[2] * 1000)) & 0xFF;          /* 存入W相电流低8位 */
            break;

        case TYPE_TEMP:                                                                 /* 温度 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (uint8_t)(data->temp[0] + 50);                    /* 存入驱动板温度 */
            upload_data[cur_data++] = (uint8_t)(data->temp[1] + 50);                    /* 存入电机温度 */
            break;

        case TYPE_SUM_LEN:                                                              /* 总里程 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (data->sum_len >> 56) & 0xFF;                     /* 存入总里程 56~63 位 */
            upload_data[cur_data++] = (data->sum_len >> 48) & 0xFF;                     /* 存入总里程 48~55 位 */
            upload_data[cur_data++] = (data->sum_len >> 40) & 0xFF;                     /* 存入总里程 40~47 位 */
            upload_data[cur_data++] = (data->sum_len >> 32) & 0xFF;                     /* 存入总里程 32~39 位 */
            upload_data[cur_data++] = (data->sum_len >> 24) & 0xFF;                     /* 存入总里程 24~31 位 */
            upload_data[cur_data++] = (data->sum_len >> 16) & 0xFF;                     /* 存入总里程 16~23 位 */
            upload_data[cur_data++] = (data->sum_len >> 8) & 0xFF;                      /* 存入总里程 8~15 位 */
            upload_data[cur_data++] = (data->sum_len >> 0) & 0xFF;                      /* 存入总里程 0~7 位 */
            break;

        case TYPE_BEM:                                                                  /* 反电动势 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (int8_t)data->bem[0];                             /* 存入U相反电动势电压整数部分 */
            upload_data[cur_data++] = ((int16_t)(data->bem[0] * 100)) % 100;            /* 存入U相反电动势电压小数部分 */
            upload_data[cur_data++] = (int8_t)data->bem[1];                             /* 存入V相反电动势电压整数部分 */
            upload_data[cur_data++] = ((int16_t)(data->bem[1] * 100)) % 100;            /* 存入V相反电动势电压小数部分 */
            upload_data[cur_data++] = (int8_t)data->bem[2];                             /* 存入W相反电动势电压整数部分 */
            upload_data[cur_data++] = ((int16_t)(data->bem[2] * 100)) % 100;            /* 存入W相反电动势电压小数部分 */
            break;

        case TYPE_MOTOR_CODE:                                                           /* 电机类型 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = data->motor_code;                                 /* 存入电机类型 */
            break;

        case TYPE_TORQUE:                                                               /* 扭矩 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (((int16_t)(data->torque * 1000)) >> 8) & 0xFF;   /* 存入扭矩值整数部分 */
            upload_data[cur_data++] = ((int16_t)(data->torque * 1000)) & 0xFF;          /* 存入扭矩值小数部分 */
            break;

        case TYPE_POWER:                                                                /* 功率 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (((int16_t)(data->power * 100)) >> 8) & 0xFF;     /* 存入功率值高8位 */
            upload_data[cur_data++] = ((int16_t)(data->power * 100)) & 0xFF;            /* 存入功率值低8位 */
            break;

        case TYPE_PID1:                                                                 /* PID参数组别 */
        case TYPE_PID2:
        case TYPE_PID3:
        case TYPE_PID4:
        case TYPE_PID5:
        case TYPE_PID6:
        case TYPE_PID7:
        case TYPE_PID8:
        case TYPE_PID9:
        case TYPE_PID10:
            upload_data[1] = upload_type;

            for (i = 0; i < 3; i++)                                            /* 循环存入P、I、D系数值,每个系数占4个字节 */
            {
                upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 0];
                upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 1];
                upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 2];
                upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 3];
            }
            break;

        case TYPE_USER_DATA:                                                   /* 波形数据 */
            upload_data[1] = upload_type;

            for (i = 0; i < 16; i++)                                           /* 循环存入1~16个通道波形数据 */
            {
                upload_data[cur_data++] = (data->user_data[i] >> 8) & 0xFF;    /* 存入波形数据高8位 */
                upload_data[cur_data++] =  data->user_data[i] & 0xFF;          /* 存入波形数据低8位 */
            }
            break;

        default :
            upload_data[1] = 0xFE;                                             /* 数据类别错误,存入错误码0xFE */
            break;
    }

    if (upload_data[1] == 0xFE)                                                /* 数据类别错误,直接跳出 */
    {
        return;
    }
    else                                                                       /* 数据类别正确 */
    {
        uint16_t crc_res = crc16_calc(&(upload_data[0]), cur_data);            /* 进行CRC校验 */
        upload_data[cur_data++] = (crc_res >> 8) & 0xFF;                       /* 存入校验结果高8位 */
        upload_data[cur_data++] = (crc_res) & 0xFF;                            /* 存入校验结果低8位 */
        upload_data[cur_data++] = DEBUG_DATA_END;                              /* 存入帧尾 */
        HAL_UART_Transmit(&g_uart1_handle, upload_data, cur_data, 0xFFFF);     /* 发送数据到上位机 */
    }
}

void debug_init(void)
{
    debug_obj_init(&g_debug);            /* 初始化所需内存 */
}

void debug_set_point_range(float max_limit, float min_limit, float step_max)
{
    static float step_temp = 0.0;

    if (abs((int)(*debug_rev.speed - step_temp)) > step_max)     /* 判断速度突变是否超过允许范围 */
    {
        *debug_rev.speed = step_temp;                            /* 超过最大突变值,保持原来速度 */
    }

    step_temp = *debug_rev.speed;                                /* 保存本次速度 */

    if (*debug_rev.speed >= max_limit)                           /* 超过限速 */
    {
        *debug_rev.speed = max_limit;                            /* 配置为最大允许速度 */
    }

    if (*debug_rev.speed <= min_limit)                           /* 超过限速 */
    {
        *debug_rev.speed = min_limit;                            /* 配置为最大允许速度 */
    }
}

void debug_send_initdata(upload_type PIDx, float *SetPoint, float P, float I, float D)
{
    debug_rev.speed = (float *)(SetPoint);          /* 开发板和上位机共用一个PID目标值的内存地址,数据同步更方便 */

    g_debug.pid[PIDx - TYPE_PID1].pid.pidf[0] = P;  /* 传入P值 */
    g_debug.pid[PIDx - TYPE_PID1].pid.pidf[1] = I;  /* 传入I值 */
    g_debug.pid[PIDx - TYPE_PID1].pid.pidf[2] = D;  /* 传入D值 */
    debug_upload_data(&g_debug, PIDx);              /* 发送PID参数 */
}

void debug_send_current(float U_I, float V_I, float W_I)
{
    g_debug.amp[0] = U_I;                           /* 传入U相电流值 */
    g_debug.amp[1] = V_I;                           /* 传入V相电流值 */
    g_debug.amp[2] = W_I;                           /* 传入W相电流值 */
    debug_upload_data(&g_debug, TYPE_AMP);          /* 发送电流数据 */
}

void debug_send_valtage(float valtage)
{
    g_debug.bus_vol = valtage;                      /* 传入电压值 */
    debug_upload_data(&g_debug, TYPE_VBUS);         /* 发送电压数据 */
}

void debug_send_power(float power)
{
    g_debug.power = power;                          /* 传入功率值 */
    debug_upload_data(&g_debug, TYPE_POWER);        /* 发送功率数据 */
}

void debug_send_speed(float speed)
{
    g_debug.speed = (int16_t)(speed);               /* 传入速度值 */
    debug_upload_data(&g_debug, TYPE_SPEED);        /* 发送速度数据 */
}

void debug_send_distance(uint64_t len)
{
    g_debug.sum_len = len;                          /* 传入总里程值 */
    debug_upload_data(&g_debug, TYPE_SUM_LEN);      /* 发送总里程数据 */
}

void debug_send_temp(float motor_temp, float board_temp)
{
    g_debug.temp[0] = board_temp;                   /* 传入驱动板温度值 */
    g_debug.temp[1] = motor_temp;                   /* 传入电机温度值 */
    debug_upload_data(&g_debug, TYPE_TEMP);         /* 发送温度数据 */
}

void debug_send_motorstate(motor_state motor_codestae)
{
    g_debug.status = motor_codestae;                /* 传入电机状态 */
    debug_upload_data(&g_debug, TYPE_STATUS);       /* 发送电机状态 */
}

void debug_send_motorcode(motor_code motorcode)
{
    g_debug.motor_code = motorcode;                 /* 传入电机类型 */
    debug_upload_data(&g_debug, TYPE_MOTOR_CODE);   /* 发送电机类型 */
}

void debug_send_wave_data(uint8_t chx, int16_t wave)
{
    g_debug.user_data[chx - 1] = wave;              /* 选择通道,传入数据 */
    debug_upload_data(&g_debug, TYPE_USER_DATA);    /* 发送波形数据 */
}

void debug_receive_pid(upload_type PIDx, float *P, float *I, float *D)
{
    *P = g_debug.pid[PIDx - TYPE_PID1].pid.pidf[0]; /* 接收P参数 */
    *I = g_debug.pid[PIDx - TYPE_PID1].pid.pidf[1]; /* 接收I参数 */
    *D = g_debug.pid[PIDx - TYPE_PID1].pid.pidf[2]; /* 接收D参数 */
}

uint8_t debug_receive_ctrl_code(void)
{
    static uint8_t rec_r = 0;
    if (debug_rev.Ctrl_code >= 0x01 && debug_rev.Ctrl_code <= 0x03) /* 判断命令范围是否正确 */
    {
        rec_r++;
        if (rec_r >= 2)
        {
            rec_r = 0;
            debug_rev.Ctrl_code = 0;
        }
        return debug_rev.Ctrl_code;                 /* 返回命令 */
    }
    return 0;
}

测试结果:

二、电流环PID控制实验

主函数

复制代码
int main(void)
{
    uint8_t key;
    uint16_t t;
    uint8_t debug_cmd = 0;

    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);         /* 设置时钟,168Mhz */
    delay_init(168);                            /* 延时初始化 */
    usart_init(115200);                         /* 串口1初始化,用于上位机调试 */
    led_init();                                 /* 初始化LED */
    lcd_init();                                 /* 初始化LCD */
    key_init();                                 /* 初始化按键 */
    pid_init();                                 /* 初始化PID参数 */
    atim_timx_cplm_pwm_init(8400 - 1 , 0);      /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
    dcmotor_init();                             /* 初始化电机 */
    gtim_timx_encoder_chy_init(0XFFFF, 0);      /* 编码器定时器初始化,不分频直接84M的计数频率 */
    btim_timx_int_init(1000 - 1 , 84 - 1);      /* 基本定时器初始化,1ms计数周期 */
    adc_nch_dma_init();                         /* 初始化ADC、DMA */
    
#if DEBUG_ENABLE                                /* 开启调试 */
    
    debug_init();                               /* 初始化调试 */
    debug_send_motorcode(DC_MOTOR);             /* 上传电机类型(直流有刷电机) */
    debug_send_motorstate(IDLE_STATE);          /* 上传电机状态(空闲) */
    
    /* 同步数据(选择第1组PID,目标电流地址,P,I,D参数)到上位机 */
    debug_send_initdata(TYPE_PID1, (float *)(&g_current_pid.SetPoint), KP, KI, KD);
    
#endif

    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
    lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
    lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
    lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
    
    delay_ms(500);
    
    while (1)
    {
        key = key_scan(0);                                          /* 按键扫描 */
        
        if(key == KEY0_PRES)                                        /* 当key0按下 */
        {
            g_run_flag = 1;                                         /* 标记电机启动 */
            dcmotor_start();                                        /* 启动电机 */
            g_current_pid.SetPoint += 20;

            if (g_current_pid.SetPoint >= 120)                      /* 限制电流:120mA */
            {
                g_current_pid.SetPoint = 120;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);                       /* 上传电机状态(运行) */
#endif
        }
        
        else if(key == KEY1_PRES)                                   /* 当key1按下 */
        {
            g_current_pid.SetPoint -= 20;

            if (g_current_pid.SetPoint < 60)                        /* 目标电流小于60mA */
            {
                dcmotor_stop();                                     /* 停止电机 */
                pid_init();                                         /* 重置pid参数 */
                g_run_flag = 0;                                     /* 标记电机停止 */
                g_motor_data.motor_pwm = 0;
                motor_pwm_set(g_motor_data.motor_pwm);              /* 设置电机转向、速度 */
#if DEBUG_ENABLE
                debug_send_motorstate(BREAKED_STATE);               /* 上传电机状态(刹车) */
                debug_send_initdata(TYPE_PID1, (float *)(&g_current_pid.SetPoint), KP, KI, KD);
#endif
            }
        }
        
        else if(key == KEY2_PRES)                                   /* 当key2按下 */
        {
            dcmotor_stop();                                         /* 停止电机 */
            pid_init();                                             /* 重置pid参数 */
            g_run_flag = 0;                                         /* 标记电机停止 */
            g_motor_data.motor_pwm = 0;
            motor_pwm_set(g_motor_data.motor_pwm);                  /* 设置电机转向、速度 */
#if DEBUG_ENABLE
            debug_send_motorstate(BREAKED_STATE);                   /* 上传电机状态(刹车)*/
            debug_send_initdata(TYPE_PID1, (float *)(&g_current_pid.SetPoint), KP, KI, KD);
#endif
        }
        
#if DEBUG_ENABLE
        
        /* 查询接收PID助手的PID参数 */
        debug_receive_pid(TYPE_PID1, (float *)&g_current_pid.Proportion,(float *)&g_current_pid.Integral, (float *)&g_current_pid.Derivative);

        debug_set_point_range(120, 0, 120);                         /* 设置目标调节范围 */
        
        debug_cmd = debug_receive_ctrl_code();                      /* 读取上位机指令 */

        if (debug_cmd == BREAKED)                                   /* 电机刹车 */
        { 
            dcmotor_stop();                                         /* 停止电机 */
            pid_init();                                             /* 重置pid参数 */
            g_run_flag = 0;                                         /* 标记电机停止 */
            g_motor_data.motor_pwm = 0;
            motor_pwm_set(g_motor_data.motor_pwm);                  /* 设置电机转向、速度 */
            debug_send_motorstate(BREAKED_STATE);                   /* 上传电机状态(刹车) */
            debug_send_initdata(TYPE_PID1, (float *)(&g_current_pid.SetPoint), KP, KI, KD);
        } 
        else if (debug_cmd == RUN_CODE)                             /* 电机运行 */
        { 
            dcmotor_start();                                        /* 启动电机 */
            g_current_pid.SetPoint = 60;                            /* 目标电流:60mA */
            g_run_flag = 1;                                         /* 标记电机启动 */
            debug_send_motorstate(RUN_STATE);                       /* 上传电机状态(运行) */
        }
#endif
        t++;
        if(t % 20 == 0)
        {
            lcd_dis();                                              /* 显示数据 */
            LED0_TOGGLE();                                          /* LED0(红灯) 翻转 */
            
#if DEBUG_ENABLE
            debug_send_current(g_motor_data.current * 0.001f, 0, 0);/* 发送电流 */
            debug_send_speed(g_motor_data.speed);                   /* 发送速度 */
#endif
        }
        delay_ms(10);
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     int32_t motor_pwm_temp = 0;
     static uint8_t val = 0;
    
     if (htim->Instance == TIM3)
     {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
     }
     else if (htim->Instance == TIM6)
     {
        int Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 5);                                  /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
         
        if (val % SMAPLSE_PID_SPEED == 0)                               /* 进行一次pid计算 */
        {
            if (g_run_flag)                                             /* 判断电机是否启动了 */
            { 
                /* PID计算,输出比较值(占空比),再进行一阶低通滤波 */
                motor_pwm_temp = increment_pid_ctrl(&g_current_pid, g_motor_data.current);
                g_motor_data.motor_pwm = (int32_t)((g_motor_data.motor_pwm * 0.5) + (motor_pwm_temp * 0.5));

                if (g_motor_data.motor_pwm >= 8200)                     /* 限制占空比 */
                {
                    g_motor_data.motor_pwm = 8200;
                }
                else if (g_motor_data.motor_pwm <= 0)
                {
                    g_motor_data.motor_pwm = 0;
                }

#if DEBUG_ENABLE  /* 发送基本参数*/

                debug_send_wave_data( 1 ,g_motor_data.current);                                         /* 选择通道1,发送实际电流(波形显示)*/
                debug_send_wave_data( 2 ,g_current_pid.SetPoint);                                       /* 选择通道2,发送目标电流(波形显示)*/
                debug_send_wave_data( 3 ,g_motor_data.motor_pwm * 100 / 8400 );                         /* 选择通道3,发送占空比(波形显示)*/
#endif
                motor_pwm_set(g_motor_data.motor_pwm);                                                  /* 设置占空比(电机转速) */
            }
            val = 0;
        }
        val ++;
     }
}

测试结果:

三、位置环PID控制


用编码器计数总值代表电机的位置。
主函数:

复制代码
int main(void)
{
    uint8_t key;
    uint16_t t;
    uint8_t debug_cmd = 0;
    
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);         /* 设置时钟,168Mhz */
    delay_init(168);                            /* 延时初始化 */
    usart_init(115200);                         /* 串口1初始化,用于上位机调试 */
    led_init();                                 /* 初始化LED */
    lcd_init();                                 /* 初始化LCD */
    key_init();                                 /* 初始化按键 */
    pid_init();                                 /* 初始化PID参数 */
    atim_timx_cplm_pwm_init(8400 - 1 , 0);      /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
    dcmotor_init();                             /* 初始化电机 */
    gtim_timx_encoder_chy_init(0XFFFF, 0);      /* 编码器定时器初始化,不分频直接84M的计数频率 */
    btim_timx_int_init(1000 - 1 , 84 - 1);      /* 基本定时器初始化,1ms计数周期 */
    adc_nch_dma_init();                         /* 初始化ADC、DMA */
    
#if DEBUG_ENABLE                                /* 开启调试 */
    
    debug_init();                               /* 初始化调试 */
    debug_send_motorcode(DC_MOTOR);             /* 上传电机类型(直流有刷电机) */
    debug_send_motorstate(IDLE_STATE);          /* 上传电机状态(空闲) */
    
    /* 同步数据(选择第1组PID,目标位置地址,P,I,D参数)到上位机 */
    debug_send_initdata(TYPE_PID1, (float *)(&g_location_pid.SetPoint), KP, KI, KD);
    
#endif

    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
    lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
    lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
    lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
    
    while (1)
    {

        key = key_scan(0);                                              /* 按键扫描 */
        if(key == KEY0_PRES)                                            /* 当key0按下 */
        {
            g_run_flag = 1;                                             /* 标记电机启动 */
            dcmotor_start();                                            /* 开启电机 */
            g_location_pid.SetPoint  += 1320;                           /* 正转一圈,电机旋转圈数 = 计数值变化量 / 44 / 30 */

            if (g_location_pid.SetPoint >= 6600)                        /* 限制电机位置(正转最大5圈) */
            {
                g_location_pid.SetPoint = 6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);                           /* 上传电机状态(运行) */
#endif
        }

        else if(key == KEY1_PRES)                                       /* 当key1按下 */
        {
            g_run_flag = 1;                                             /* 标记电机启动 */
            dcmotor_start();                                            /* 开启电机 */
            g_location_pid.SetPoint  -= 1320;                           /* 反转一圈 */

            if (g_location_pid.SetPoint <= -6600)                       /* 限制电机位置(反转最大5圈) */
            {
                g_location_pid.SetPoint = -6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);                           /* 上传电机状态(运行) */
#endif
        }

        else if(key == KEY2_PRES)                                       /* 当key2按下 */
        {
            g_location_pid.SetPoint = 0;                                /* 恢复初始位置 */
        }
        
#if DEBUG_ENABLE
        /* 查询接收PID助手的PID参数 */
        debug_receive_pid(TYPE_PID1, (float *)&g_location_pid.Proportion,(float *)&g_location_pid.Integral, (float *)&g_location_pid.Derivative);

        debug_set_point_range(6600, -6600, 6600);                       /* 设置目标调节范围 */
        
        debug_cmd = debug_receive_ctrl_code();                          /* 读取上位机指令 */

        if (debug_cmd == HALT_CODE)                                     /* 电机停机 */
        { 
            g_location_pid.SetPoint = 0;                                /* 恢复初始位置 */
        }
        else if (debug_cmd == RUN_CODE)                                 /* 电机运行 */
        {
            g_run_flag = 1;                                             /* 标记电机启动 */
            dcmotor_start();                                            /* 开启电机 */
            g_location_pid.SetPoint = 1320;                             /* 设置目标位置 */
            debug_send_motorstate(RUN_STATE);                           /* 上传电机状态(运行) */
        }
#endif
        t++;
        if(t % 20 == 0)
        {
            lcd_dis();                                                  /* 显示数据 */
            LED0_TOGGLE();                                              /* LED0(红灯)翻转 */
            g_debug.encode_p = g_motor_data.location;                   /* 传入编码器当前总计数值 */
            debug_upload_data(&g_debug, TYPE_HAL_ENC);                  /* 发送编码器当前总计数值 */
        }
        delay_ms(10);
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     int32_t motor_pwm_temp = 0;
     static uint8_t val = 0;
    
     if (htim->Instance == TIM3)
     {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
     }
     else if (htim->Instance == TIM6)
     {
        int Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 5);                                  /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
         
        g_motor_data.location = Encode_now;                             /* 获取当前计数总值,用于位置闭环控制 */
         
        if (val % SMAPLSE_PID_SPEED == 0)                               /* 进行一次pid计算 */
        {
            if (g_run_flag)                                             /* 判断电机是否启动了 */
            { 
                /* PID计算,输出比较值(占空比),再进行一阶低通滤波 */
                motor_pwm_temp = increment_pid_ctrl(&g_location_pid, g_motor_data.location);
                g_motor_data.motor_pwm = (int32_t)((g_motor_data.motor_pwm * 0.5) + (motor_pwm_temp * 0.5));

                if (g_motor_data.motor_pwm >= 4200)                     /* 限制占空比 */
                {
                    g_motor_data.motor_pwm = 4200;
                }
                else if (g_motor_data.motor_pwm <= -4200)
                {
                    g_motor_data.motor_pwm = -4200;
                }

#if DEBUG_ENABLE  /* 发送基本参数*/

                debug_send_wave_data( 1 ,g_motor_data.location);                                        /* 选择通道1,发送实际位置(波形显示)*/
                debug_send_wave_data( 2 ,g_location_pid.SetPoint);                                      /* 选择通道2,发送目标位置(波形显示)*/
#endif
                motor_pwm_set(g_motor_data.motor_pwm);                                                  /* 设置占空比(电机转速)*/
            }
            val = 0;
        }
        val ++;
     }
}

测试结果:

四、速度环+位置环控制

主函数

复制代码
int main(void)
{
    uint8_t key;
    uint16_t t;
    uint8_t debug_cmd = 0;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口1初始化,用于上位机调试 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    key_init();                             /* 初始化按键 */
    pid_init();                             /* 初始化PID参数 */
    atim_timx_cplm_pwm_init(8400 - 1 , 0);  /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
    dcmotor_init();                         /* 初始化电机 */
    gtim_timx_encoder_chy_init(0XFFFF, 0);  /* 编码器定时器初始化,不分频直接84M的计数频率 */
    btim_timx_int_init(1000 - 1 , 84 - 1);  /* 基本定时器初始化,1ms计数周期 */

#if DEBUG_ENABLE                            /* 开启调试 */
    
    debug_init();                           /* 初始化调试 */
    debug_send_motorcode(DC_MOTOR);         /* 上传电机类型(直流有刷电机) */
    debug_send_motorstate(IDLE_STATE);      /* 上传电机状态(空闲) */
    
    /* 同步数据PID参数到上位机 ,无论同步哪一组数据,目标值地址只能是外环PID的 */
    debug_send_initdata(TYPE_PID1, (float *)(&g_location_pid.SetPoint), L_KP, L_KI, L_KD);  /* 位置环PID参数(PID1)*/
    debug_send_initdata(TYPE_PID2, (float *)(&g_location_pid.SetPoint), S_KP, S_KI, S_KD);  /* 速度环PID参数(PID2)*/

#endif

    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
    lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
    lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
    lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);

    while (1)
    {
        key = key_scan(0);                                  /* 按键扫描 */
        if(key == KEY0_PRES)                                /* 当key0按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            dcmotor_start();                                /* 开启电机 */
            g_location_pid.SetPoint += 1320;                /* 正转一圈,电机旋转圈数 = 编码器总计数值 / 44 / 30 */
            
            if (g_location_pid.SetPoint >= 6600)            /* 限制电机位置(正转最大5圈) */
            {
                g_location_pid.SetPoint = 6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
#endif
        }
        
        else if(key == KEY1_PRES)                           /* 当key1按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            dcmotor_start();                                /* 开启电机 */
            g_location_pid.SetPoint -= 1320;                /* 反转一圈 */
            
            if (g_location_pid.SetPoint <= -6600)           /* 限制电机位置(反转最大5圈) */
            {
                g_location_pid.SetPoint = -6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
#endif
        }
        
        else if(key == KEY2_PRES)                           /* 当key2按下 */
        {
            g_location_pid.SetPoint = 0;                    /* 恢复初始位置 */
        }

#if DEBUG_ENABLE

        /* 接收PID助手设置的位置环PID参数 */
        debug_receive_pid(TYPE_PID1, (float *)&g_location_pid.Proportion,(float *)&g_location_pid.Integral, (float *)&g_location_pid.Derivative);

        /* 接收PID助手设置的速度环PID参数 */
        debug_receive_pid(TYPE_PID2, (float *)&g_speed_pid.Proportion, (float *)&g_speed_pid.Integral, (float *)&g_speed_pid.Derivative);
        
        debug_set_point_range(6600, -6600, 6600);                       /* 设置目标调节范围 */
        
        debug_cmd = debug_receive_ctrl_code();                          /* 读取上位机指令 */

        if (debug_cmd == HALT_CODE)                                     /* 电机停机 */
        {
            g_location_pid.SetPoint = 0;                                /* 恢复初始位置 */
        } 
        else if (debug_cmd == RUN_CODE)                                 /* 电机运行 */
        { 
            g_run_flag = 1;                                             /* 标记电机启动 */
            dcmotor_start();                                            /* 开启电机 */
            g_location_pid.SetPoint = 1320;                             /* 设置目标位置 */
            debug_send_motorstate(RUN_STATE);                           /* 上传电机状态(运行) */
        }
#endif
        t++;
        if(t % 20 == 0)
        {
            lcd_dis();
            LED0_TOGGLE();                                              /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
            debug_send_speed(g_motor_data.speed);                       /* 发送速度 */
            g_debug.encode_p = g_motor_data.location;                   /* 传入编码器当前总计数值 */
            debug_upload_data(&g_debug, TYPE_HAL_ENC);                  /* 发送编码器当前总计数值 */
#endif
        }
        delay_ms(10);
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     static uint8_t val = 0;
    
     if (htim->Instance == TIM3)
     {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
     }
     else if (htim->Instance == TIM6)
     {
        int Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 5);                                  /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
         
        if (val % SMAPLSE_PID_SPEED == 0)                               /* 进行一次pid计算 */
        {
            if (g_run_flag)                                             /* 判断电机是否启动了 */
            { 
                g_motor_data.location = (float)Encode_now;              /* 获取当前编码器总计数值,用于位置闭环控制 */

                g_motor_data.motor_pwm = increment_pid_ctrl(&g_location_pid, g_motor_data.location);    /* 位置环PID控制(外环) */
                
                if (g_motor_data.motor_pwm >= 150)                      /* 限制外环输出(目标速度) */
                {
                    g_motor_data.motor_pwm = 150;
                }
                else if (g_motor_data.motor_pwm <= -150)
                {
                    g_motor_data.motor_pwm = -150;
                }
                
                g_speed_pid.SetPoint = g_motor_data.motor_pwm;          /* 设置目标速度,外环输出作为内环输入 */
                
                g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid, g_motor_data.speed);          /* 速度环PID控制(内环) */
                
                if (g_motor_data.motor_pwm >= 8200)                     /* 限制占空比 */
                {
                    g_motor_data.motor_pwm = 8200;
                }
                else if (g_motor_data.motor_pwm <= -8200)
                {
                    g_motor_data.motor_pwm = -8200;
                }

#if DEBUG_ENABLE  /* 发送基本参数*/

                debug_send_wave_data( 1 ,g_motor_data.location);                                        /* 选择通道1,发送实际位置(波形显示)*/
                debug_send_wave_data( 2 ,g_location_pid.SetPoint);                                      /* 选择通道2,发送目标位置(波形显示)*/
#endif
                motor_pwm_set(g_motor_data.motor_pwm);                                                  /* 设置占空比(电机转速) */
            }
            val = 0;
        }
        val ++;
     }
}

测试结果:

五、电流环+位置环控制

主函数

复制代码
int main(void)
{
    uint8_t key;
    uint16_t t;
    uint8_t debug_cmd = 0;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口1初始化,用于上位机调试 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    key_init();                             /* 初始化按键 */
    pid_init();                             /* 初始化PID参数 */
    atim_timx_cplm_pwm_init(8400 - 1 , 0);  /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
    dcmotor_init();                         /* 初始化电机 */
    gtim_timx_encoder_chy_init(0XFFFF, 0);  /* 编码器定时器初始化,不分频直接84M的计数频率 */
    btim_timx_int_init(1000 - 1 , 84 - 1);  /* 基本定时器初始化,1ms计数周期 */
    adc_nch_dma_init();                     /* 初始化ADC、DMA */
    
#if DEBUG_ENABLE                            /* 开启调试 */
    
    debug_init();                           /* 初始化调试 */
    debug_send_motorcode(DC_MOTOR);         /* 上传电机类型(直流有刷电机) */
    debug_send_motorstate(IDLE_STATE);      /* 上传电机状态(空闲) */
    
    /* 同步数据PID参数到上位机 ,无论同步哪一组数据,目标值地址只能是外环PID的 */
    debug_send_initdata(TYPE_PID1, (float *)(&g_location_pid.SetPoint), L_KP, L_KI, L_KD);  /* 位置环PID参数(PID1)*/
    debug_send_initdata(TYPE_PID2, (float *)(&g_location_pid.SetPoint), C_KP, C_KI, C_KD);  /* 电流环PID参数(PID2)*/

#endif

    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
    lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
    lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
    lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);

    while (1)
    {
        key = key_scan(0);                                  /* 按键扫描 */
        if(key == KEY0_PRES)                                /* 当key0按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            dcmotor_start();                                /* 开启电机 */
            g_location_pid.SetPoint += 1320;                /* 正转一圈,电机旋转圈数 = 编码器总计数值 / 44 / 30 */
            
            if (g_location_pid.SetPoint >= 6600)            /* 限制电机位置(正转最大5圈) */
            {
                g_location_pid.SetPoint = 6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
#endif
        }
        
        else if(key == KEY1_PRES)                           /* 当key1按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            dcmotor_start();                                /* 开启电机 */
            g_location_pid.SetPoint -= 1320;                /* 反转一圈 */
            
            if (g_location_pid.SetPoint <= -6600)           /* 限制电机位置(反转最大5圈) */
            {
                g_location_pid.SetPoint = -6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
#endif
        }
        
        else if(key == KEY2_PRES)                           /* 当key2按下 */
        {
            g_location_pid.SetPoint = 0;                    /* 恢复初始位置 */
        }

#if DEBUG_ENABLE

        /* 接收PID助手设置的位置环PID参数 */
        debug_receive_pid(TYPE_PID1, (float *)&g_location_pid.Proportion,(float *)&g_location_pid.Integral, (float *)&g_location_pid.Derivative);

        /* 接收PID助手设置的速度环PID参数 */
        debug_receive_pid(TYPE_PID2, (float *)&g_current_pid.Proportion, (float *)&g_current_pid.Integral, (float *)&g_current_pid.Derivative);
        
        debug_set_point_range(6600, -6600, 6600);                       /* 设置目标调节范围 */
        
        debug_cmd = debug_receive_ctrl_code();                          /* 读取上位机指令 */

        if (debug_cmd == HALT_CODE)                                     /* 电机停机 */
        {
            g_location_pid.SetPoint = 0;                                /* 恢复初始位置 */
        } 
        else if (debug_cmd == RUN_CODE)                                 /* 电机运行 */
        { 
            g_run_flag = 1;                                             /* 标记电机启动 */
            dcmotor_start();                                            /* 开启电机 */
            g_location_pid.SetPoint = 1320;                             /* 设置目标位置 */
            debug_send_motorstate(RUN_STATE);                           /* 上传电机状态(运行) */
        }
#endif
        t++;
        if(t % 20 == 0)
        {
            lcd_dis();
            LED0_TOGGLE();                                              /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
            debug_send_current(g_motor_data.current,0,0);               /* 发送电流到U相 */
            g_debug.encode_p = g_motor_data.location;                   /* 传入编码器当前总计数值 */
            debug_upload_data(&g_debug, TYPE_HAL_ENC);                  /* 发送编码器当前总计数值 */
#endif
        }
        delay_ms(10);
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     static uint8_t val = 0;

     if (htim->Instance == TIM3)
     {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
     }
     else if (htim->Instance == TIM6)
     {
        int Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 5);                                  /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
         
        if (val % SMAPLSE_PID_SPEED == 0)                               /* 进行一次pid计算 */
        {
            if (g_run_flag)                                             /* 判断电机是否启动了 */
            { 
                g_motor_data.location = (float)Encode_now;              /* 获取当前编码器总计数值,用于位置闭环控制 */

                g_motor_data.motor_pwm = increment_pid_ctrl(&g_location_pid, g_motor_data.location);    /* 位置环PID控制(外环) */
                
                if ( g_motor_data.motor_pwm > 0)                        /* 判断位置环输出值是否为正数 */
                {
                    dcmotor_dir(0);                                     /* 输出为正数,设置电机正转 */
                }
                else
                {
                    g_motor_data.motor_pwm = -g_motor_data.motor_pwm;   /* 输出为负数,取反 */
                    dcmotor_dir(1);                                     /* 设置电机反转 */
                }
                    
                if (g_motor_data.motor_pwm >= 120)                      /* 限制外环输出(目标电流) */
                {
                    g_motor_data.motor_pwm = 120;
                }
                g_current_pid.SetPoint = g_motor_data.motor_pwm;        /* 设置目标电流,外环输出作为内环输入 */

                g_motor_data.motor_pwm = increment_pid_ctrl(&g_current_pid, g_motor_data.current);      /* 电流环PID控制(内环) */
                
                if (g_motor_data.motor_pwm >= 8200)                     /* 限制占空比 */
                {
                    g_motor_data.motor_pwm = 8200;
                }
                else if (g_motor_data.motor_pwm <= 0)
                {
                    g_motor_data.motor_pwm = 0;
                }

#if DEBUG_ENABLE  /* 发送基本参数*/

                debug_send_wave_data( 1 ,g_motor_data.location);                                        /* 选择通道1,发送实际位置(波形显示)*/
                debug_send_wave_data( 2 ,g_location_pid.SetPoint);                                      /* 选择通道2,发送目标位置(波形显示)*/
#endif
                dcmotor_speed(g_motor_data.motor_pwm);                                                  /* 设置占空比 */
            }
            val = 0;
        }
        val ++;
     }
}

测试结果:

六、电流环+速度环控制

主函数

复制代码
int main(void)
{
    uint8_t key;
    uint16_t t;
    uint8_t debug_cmd = 0;

    
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口1初始化,用于上位机调试 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    key_init();                             /* 初始化按键 */
    pid_init();                             /* 初始化PID参数 */
    atim_timx_cplm_pwm_init(8400 - 1 , 0);  /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
    dcmotor_init();                         /* 初始化电机 */
    gtim_timx_encoder_chy_init(0XFFFF, 0);  /* 初始化编码器定时器,不分频直接84M的计数频率 */
    btim_timx_int_init(1000 - 1 , 84 - 1);  /* 初始化基本定时器,1ms计数周期 */
    adc_nch_dma_init();                     /* 初始化ADC、DMA */

#if DEBUG_ENABLE                            /* 开启调试 */
    
    debug_init();                           /* 初始化调试 */
    debug_send_motorcode(DC_MOTOR);         /* 上传电机类型(直流有刷电机)*/
    debug_send_motorstate(IDLE_STATE);      /* 上传电机状态(空闲)*/
    
    /* 同步数据PID参数到上位机 ,无论同步哪一组PID,目标值地址只能是外环PID的 */
    debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), S_KP, S_KI, S_KD); /* 速度环PID参数(PID1)*/
    debug_send_initdata(TYPE_PID2, (float *)(&g_speed_pid.SetPoint), C_KP, C_KI, C_KD); /* 电流环PID参数(PID2)*/

#endif

    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
    lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
    lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
    lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);

    while (1)
    {
        key = key_scan(0);                                  /* 按键扫描 */
        if(key == KEY0_PRES)                                /* 当key0按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            g_speed_pid.SetPoint += 50;
            dcmotor_start();                                /* 开启电机 */
            
            if (g_speed_pid.SetPoint >= 300)                /* 限速 */
            {
                g_speed_pid.SetPoint = 300;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
#endif
        }

        else if(key == KEY1_PRES)                           /* 当key1按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            g_speed_pid.SetPoint -= 50;
            dcmotor_start();                                /* 开启电机 */
            
            if (g_speed_pid.SetPoint <= -300)               /* 限速 */
            {
                g_speed_pid.SetPoint = -300;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
#endif
        }

        else if(key == KEY2_PRES)                           /* 当key2按下 */
        {
            dcmotor_stop();                                 /* 停止电机 */
            pid_init();                                     /* 重置pid参数 */
            g_run_flag = 0;                                 /* 标记电机停止 */
            g_motor_data.motor_pwm = 0;
            motor_pwm_set(g_motor_data.motor_pwm);          /* 设置电机转向、速度 */
            
#if DEBUG_ENABLE
            debug_send_motorstate(BREAKED_STATE);           /* 上传电机状态(刹车) */
            debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), S_KP, S_KI, S_KD);           /* 同步PID参数到上位机 */
            debug_send_initdata(TYPE_PID2, (float *)(&g_speed_pid.SetPoint), C_KP, C_KI, C_KD);
#endif
        }
        
#if DEBUG_ENABLE
        
        /* 接收PID助手设置的速度环PID参数 */
        debug_receive_pid(TYPE_PID1, (float *)&g_speed_pid.Proportion,(float *)&g_speed_pid.Integral,(float *)&g_speed_pid.Derivative);
        
        /* 接收PID助手设置的电流环PID参数 */
        debug_receive_pid(TYPE_PID2, (float *)&g_current_pid.Proportion,(float *)&g_current_pid.Integral, (float *)&g_current_pid.Derivative);
        
        debug_set_point_range(300, -300, 300);              /* 设置目标调节范围,有电流环的系统目标值突变不要太大 */
        debug_cmd = debug_receive_ctrl_code();              /* 读取上位机指令 */

        if (debug_cmd == BREAKED)                           /* 电机刹车 */
        {
            dcmotor_stop();                                 /* 停止电机 */
            pid_init();                                     /* 重置pid参数 */
            g_run_flag = 0;                                 /* 标记电机停止 */
            g_motor_data.motor_pwm = 0;
            motor_pwm_set(g_motor_data.motor_pwm);          /* 设置电机转向、速度 */
            debug_send_motorstate(BREAKED_STATE);           /* 上传电机状态(刹车) */
            debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), S_KP, S_KI, S_KD);           /* 同步PID参数到上位机 */
            debug_send_initdata(TYPE_PID2, (float *)(&g_speed_pid.SetPoint), C_KP, C_KI, C_KD);
        } 
        else if (debug_cmd == RUN_CODE)                     /* 电机运行 */
        {  
            dcmotor_start();                                /* 开启电机 */
            g_speed_pid.SetPoint = 50;                      /* 设置目标速度 */
            g_run_flag = 1;                                 /* 标记电机启动 */
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
        }
#endif
        t++;
        if(t % 20 == 0)
        {
            c_sum_error = (int)g_current_pid.SumError;      /* 此变量用于调试时查看电流环累计积分 */
            s_sum_error = (int)g_speed_pid.SumError;        /* 此变量用于调试时查看速度环累计积分 */
            
            lcd_dis();                                      /* 显示数据 */
            LED0_TOGGLE();                                  /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
            debug_send_speed(g_motor_data.speed);           /* 发送速度 */
#endif
        }
        delay_ms(10);
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     static uint8_t val = 0;
    
     if (htim->Instance == TIM3)
     {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
     }
     else if (htim->Instance == TIM6)
     {
        int Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 5);                                  /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
         
        if (val % SMAPLSE_PID_SPEED == 0)                               /* 进行一次pid计算 */
        {
            if (g_run_flag)                                             /* 判断电机是否启动了 */
            { 
                
                integral_limit( &g_speed_pid , 7000 , -7000 );          /* 速度环积分限幅 */
                integral_limit( &g_current_pid , 2500 , -2500 );        /* 电流环积分限幅 */
                
                g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid, g_motor_data.speed);         /* 速度环PID控制(外环) */
                
                if ( g_motor_data.motor_pwm > 0)                        /* 判断速度环输出值是否为正数 */
                {
                    dcmotor_dir(0);                                     /* 输出为正数,设置电机正转 */
                }
                else
                {
                    g_motor_data.motor_pwm = -g_motor_data.motor_pwm;   /* 目标电流不能为负数,速度环输出要取反 */
                    dcmotor_dir(1);                                     /* 设置电机反转 */
                }
                    
                if (g_motor_data.motor_pwm >= 200)                      /* 限制外环输出(目标电流) */
                {
                    g_motor_data.motor_pwm = 200;
                }
                g_current_pid.SetPoint = g_motor_data.motor_pwm;        /* 设置目标电流,外环输出作为内环输入 */

                g_motor_data.motor_pwm = increment_pid_ctrl(&g_current_pid, g_motor_data.current);      /* 电流环PID控制(内环) */
                
                if (g_motor_data.motor_pwm >= 8200)                     /* 限制占空比 */
                {
                    g_motor_data.motor_pwm = 8200;
                }
                else if (g_motor_data.motor_pwm <= 0)                   /* 滤掉无效输出 */
                {
                    g_motor_data.motor_pwm = 0;
                }

#if DEBUG_ENABLE  /* 发送基本参数*/

                debug_send_wave_data( 1 ,g_motor_data.speed);                                           /* 选择通道1,发送实际速度(波形显示)*/
                debug_send_wave_data( 2 ,g_speed_pid.SetPoint);                                         /* 选择通道2,发送目标速度(波形显示)*/
#endif
                dcmotor_speed(g_motor_data.motor_pwm);                                                  /* 设置占空比 */
            }
            val = 0;
        }
        val ++;
     }
}

测试结果:

七、电流环+速度环+位置环控制

主函数

复制代码
int main(void)
{
    uint8_t key;
    uint16_t t;
    uint8_t debug_cmd = 0;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口1初始化,用于上位机调试 */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    key_init();                             /* 初始化按键 */
    pid_init();                             /* 初始化PID参数 */
    atim_timx_cplm_pwm_init(8400 - 1 , 0);  /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
    dcmotor_init();                         /* 初始化电机 */
    gtim_timx_encoder_chy_init(0XFFFF, 0);  /* 编码器定时器初始化,不分频直接84M的计数频率 */
    btim_timx_int_init(1000 - 1 , 84 - 1);  /* 基本定时器初始化,1ms计数周期 */
    adc_nch_dma_init();
    
#if DEBUG_ENABLE                            /* 开启调试 */
    
    debug_init();                           /* 初始化调试 */
    debug_send_motorcode(DC_MOTOR);         /* 上传电机类型(直流有刷电机) */
    debug_send_motorstate(IDLE_STATE);      /* 上传电机状态(空闲) */
    
    /* 同步数据PID参数到上位机 ,无论同步哪一组数据,目标值地址只能是外环PID的 */
    debug_send_initdata(TYPE_PID1, (float *)(&g_location_pid.SetPoint), L_KP, L_KI, L_KD);  /* 位置环PID参数(PID1)*/
    debug_send_initdata(TYPE_PID2, (float *)(&g_location_pid.SetPoint), S_KP, S_KI, S_KD);  /* 速度环PID参数(PID2)*/
    debug_send_initdata(TYPE_PID3, (float *)(&g_location_pid.SetPoint), C_KP, C_KI, C_KD);  /* 电流环PID参数(PID3)*/

#endif

    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
    lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
    lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
    lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);

    while (1)
    {
        key = key_scan(0);                                  /* 按键扫描 */
        if(key == KEY0_PRES)                                /* 当key0按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            dcmotor_start();                                /* 开启电机 */
            g_location_pid.SetPoint += 1320;                /* 正转一圈,电机旋转圈数 = 编码器总计数值 / 44 / 30 */
            
            if (g_location_pid.SetPoint >= 6600)            /* 限制电机位置(正转最大5圈) */
            {
                g_location_pid.SetPoint = 6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
#endif
        }
        
        else if(key == KEY1_PRES)                           /* 当key1按下 */
        {
            g_run_flag = 1;                                 /* 标记电机启动 */
            dcmotor_start();                                /* 开启电机 */
            g_location_pid.SetPoint -= 1320;                /* 反转一圈 */
            
            if (g_location_pid.SetPoint <= -6600)           /* 限制电机位置(反转最大5圈) */
            {
                g_location_pid.SetPoint = -6600;
            }
#if DEBUG_ENABLE
            debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */
#endif
        }
        
        else if(key == KEY2_PRES)                           /* 当key2按下 */
        {
            g_location_pid.SetPoint = 0;                    /* 恢复初始位置 */
        }
        
#if DEBUG_ENABLE
        
        /* 接收PID助手设置的PID参数 */
        debug_receive_pid(TYPE_PID1,(float *)&g_location_pid.Proportion,(float *)&g_location_pid.Integral,(float *)&g_location_pid.Derivative);
        debug_receive_pid(TYPE_PID2,(float *)&g_speed_pid.Proportion,(float *)&g_speed_pid.Integral,(float *)&g_speed_pid.Derivative);
        debug_receive_pid(TYPE_PID3,(float *)&g_current_pid.Proportion,(float *)&g_current_pid.Integral,(float *)&g_current_pid.Derivative);

        debug_set_point_range(6600, -6600, 6600);                       /* 设置目标调节范围 */
        
        debug_cmd = debug_receive_ctrl_code();                          /* 读取上位机指令 */

        if (debug_cmd == HALT_CODE)                                     /* 电机停机 */
        {
            g_location_pid.SetPoint = 0;                                /* 恢复初始位置 */
        } 
        else if (debug_cmd == RUN_CODE)                                 /* 电机运行 */
        { 
            g_run_flag = 1;                                             /* 标记电机启动 */
            dcmotor_start();                                            /* 开启电机 */
            g_location_pid.SetPoint = 1320;                             /* 设置目标位置 */
            debug_send_motorstate(RUN_STATE);                           /* 上传电机状态(运行) */
        }
#endif
        t++;
        if(t % 20 == 0)
        {
            lcd_dis();
            LED0_TOGGLE();                                              /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
            debug_send_speed(g_motor_data.speed);                       /* 发送速度 */
            g_debug.encode_p = g_motor_data.location;                   /* 传入编码器当前总计数值 */
            debug_upload_data(&g_debug, TYPE_HAL_ENC);                  /* 发送编码器当前总计数值 */
#endif
        }
        delay_ms(10);
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     static uint8_t val = 0;
    
     if (htim->Instance == TIM3)
     {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
     }
     else if (htim->Instance == TIM6)
     {
        int Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 5);                                  /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
         
        if (val % SMAPLSE_PID_SPEED == 0)                               /* 进行一次pid计算 */
        {
            if (g_run_flag)                                             /* 判断电机是否启动了 */
            { 
                g_motor_data.location = (float)Encode_now;              /* 获取当前编码器总计数值,用于位置闭环控制 */
                
                integral_limit(&g_location_pid , 1000 ,-1000);          /* 位置环积分限幅 */
                integral_limit(&g_speed_pid , 200 ,-200);               /* 速度环积分限幅 */
                integral_limit(&g_current_pid , 150 ,-150);             /* 电流环积分限幅 */
                
                if( (g_location_pid.Error <= 20) && (g_location_pid.Error >= -20) )                     /* 设置闭环死区 */
                {
                    g_location_pid.Error = 0;                           /* 偏差太小了,直接清零 */
                    g_location_pid.SumError = 0;                        /* 清除积分 */
                }
                
                g_motor_data.motor_pwm = increment_pid_ctrl(&g_location_pid, g_motor_data.location);    /* 位置环PID控制(最外环) */
                
                if (g_motor_data.motor_pwm >= 120)                      /* 限制外环输出(目标速度) */
                {
                    g_motor_data.motor_pwm = 120;
                }
                else if (g_motor_data.motor_pwm <= -120)
                {
                    g_motor_data.motor_pwm = -120;
                }
                
                g_speed_pid.SetPoint = g_motor_data.motor_pwm;          /* 设置目标速度,外环输出作为内环输入 */
                g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid, g_motor_data.speed);         /* 速度环PID控制(次外环) */
                
                if ( g_motor_data.motor_pwm > 0)                        /* 判断速度环输出值是否为正数 */
                {
                    dcmotor_dir(0);                                     /* 输出为正数,设置电机正转 */
                }
                else
                {
                    g_motor_data.motor_pwm = -g_motor_data.motor_pwm;   /* 输出取反 */
                    dcmotor_dir(1);                                     /* 设置电机反转 */
                }
                    
                if (g_motor_data.motor_pwm >= 100)                      /* 限制外环输出(目标电流) */
                {
                    g_motor_data.motor_pwm = 100;
                }
                g_current_pid.SetPoint = g_motor_data.motor_pwm;        /* 设置目标电流,外环输出作为内环输入 */

                g_motor_data.motor_pwm = increment_pid_ctrl(&g_current_pid, g_motor_data.current);      /* 电流环PID控制(内环) */
                
                if (g_motor_data.motor_pwm >= 8200)                     /* 限制占空比 */
                {
                    g_motor_data.motor_pwm = 8200;
                }
                else if (g_motor_data.motor_pwm <= 0)                   /* 滤掉无效输出 */
                {
                    g_motor_data.motor_pwm = 0;
                }

#if DEBUG_ENABLE  /* 发送基本参数*/

                debug_send_wave_data( 1 ,g_motor_data.location);                                        /* 选择通道1,发送实际位置(波形显示)*/
                debug_send_wave_data( 2 ,g_location_pid.SetPoint);                                      /* 选择通道2,发送目标位置(波形显示)*/
#endif
                dcmotor_speed(g_motor_data.motor_pwm);                                                  /* 设置占空比 */
            }
            val = 0;
        }
        val ++;
     }
}

测试结果:

相关推荐
xiangw@GZ2 小时前
智能锁浮空系统指纹头金属环ESD防护技术分析
单片机·嵌入式硬件
ACP广源盛139246256732 小时前
IX7008 PCIe 交换芯片@ACP#RTX Spark 经济型 8 口扩展芯片(对比 ASM1806)
大数据·人工智能·分布式·嵌入式硬件·gpt·spark·电脑
项目題供诗2 小时前
STM32-DMA直接存储器存储(二十)
stm32·单片机·嵌入式硬件
耳朵东先生3 小时前
STM32 开发利器:SEGGER RTT 日志打印与 Shell 实践解析
单片机·嵌入式硬件
ACP广源盛139246256733 小时前
IX6012 PCIe 交换芯片@ACP#RTX Spark 入门级 12 口存储外设扩展方案(对比 ASM1812)
大数据·人工智能·分布式·嵌入式硬件·gpt·spark·电脑
2601_958352903 小时前
对讲系统音频优化实战:解决回声、啸叫、环境噪音与远场拾音难题
嵌入式硬件·音视频·语音识别·降噪处理·音频处理模块·硬件开发模块
振南的单片机世界3 小时前
RS485组网三要素:负载、距离、终端电阻
arm开发·stm32·单片机·嵌入式硬件
小慧10243 小时前
Esp开发工具命令
单片机
redaijufeng3 小时前
stm32实现串口打印输出_stm32串口打印
stm32·单片机·嵌入式硬件