搜索
bottom↓
回复: 0

《DMF407电机控制专题教程_V1.0》第20章

[复制链接]

出0入234汤圆

发表于 2022-8-19 09:51:05 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2022-8-18 17:23 编辑

1)实验平台:正点原子DMF407电机开发板
2)平台购买地址: https://detail.tmall.com/item.htm?&id=677230699323
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/stm32dj/ATK-DMF407.html
4)对正点原子电机开发板感兴趣的同学可以加群讨论: 592929122 lQLPJxaFi2zaB4UWWrDAMgIsFEW2pwLb3abnwDMA_90_22.png

lQDPJxaFi2nfFizMjM0CbLCPlxn_FVheIQLb3aGrwFQA_620_140.jpg

lQLPJxaFi2nfFhLMkM0BXrDNvOUyeU_FPgLb3aGvQNIA_350_144.png

第20章 步进电机闭环系统(位置环)


        本章我们将学习步进电机的闭环系统。经过我们前面的学习,我们知道步进电机它拥有非常优秀的开环能力,控制器不需要传感器反馈电机信号,只要在不超频且不超载的情况下,脉冲数与旋转位置成正比关系。那我们为什么还要引入闭环系统呢?因为在实际的应用中单独靠开环还是有很多不确定因素,比如步进电机的机械老化导致的丢步或堵转,还有一些外部因素比如:突然给电机加了一个比较大的负载或者快速启停都是有可能出现丢步、过冲甚至堵转现象,控制器是无法知晓并纠正偏差的,这些情况在一些要求高精度的场合是不允许的,否则有可能导致严重后果,所以我们引入闭环系统使控制器可以及时发现偏差并纠正偏差。
本章分为如下几个小节:
20.1 步进电机闭环系统组成
20.2 步进电机闭环系统原理
20.3 步进电机位置环控制实现(增量式/位置式)


20.1 步进电机闭环系统组成

        同直流有刷电机的闭环系统类似,步进电机的闭环系统的组成由:步进电机+编码器构成,编码器反馈步进电机的实际旋转位置给控制器,这样就可以控制器知晓是否有按要求到达指定目标位置,当有偏差就可及时纠偏了。
        接着来看总结的一些闭环系统与开环系统之间的优缺点:
1B88A5F0-5BC9-4d2a-A570-AC04208BFD20.png
表20.1.1 开/闭环系统优缺点对比

20.2 步进电机闭环系统原理
image002.jpg
图20.2.1 步进电机闭环系统控制原理

        图20.2.1为步进电机闭环系统的整体控制流程,其中编码器作为反馈通道,反馈步进电机的实际位置,系统将实际位置与目标位置进行比较,计算差值,再把偏差值代入到PID控制器中,之后控制器输出期望值,最后作用于步进电机使其旋转至指定位置停止!这就是位置闭环控制。
20.3 步进电机位置闭环控制实现(增量式/位置式)
20.3.1 硬件设计

1、例程功能
        本实验使用步进电机接口三控制
        当按下KEY0一次启动步进电机转动到目标位置,在按一次KEY0关闭电机转动,按下KEY1增加目标位置,按下KEY2减少目标位置;
        可通过串口1即USB_TTL接口连接正点原子PID调试助手,查看PID波形;
        注意驱动器使用8细分,也就是旋转一圈所需要1600个脉冲数;
        LED0作为程序运行状态指示灯,闪烁周期200ms。
2、硬件资源
1)LED灯:LED0 – PE0
2)独立按键
    KEY0 – PE2
        KEY1 – PE3
        KEY2 – PE4
3)定时器:  TIM8_CH3:PI7,对应步进电机接口三
                         TIM3_CH1 : PC6(编码器接口1)
                         TIM3_CH2 : PC7(编码器接口2)
                         TIM6定时采样
   方向引脚:DIR3: PB2
   脱机引脚:EN3: PF11
4)步进电机
5)步进电机驱动器
6)12-50V的DC电源
20.3.2 程序设计
20.3.2.1 步进电机位置闭环控制的配置步骤
1)初始化定时器
        初始化定时器通道IO,设置ARR、PSC,计数方式以及脉冲输出模式等
2)初始化编码器接口
        初始化编码器接口模式定时器三的通道一和通道二,用来采集反馈信号
3)PID算法实现
        实现增量式/位置式PID算法,调整好比例P,积分I,微分D的参数
4)位置闭环运动控制
        设置电机旋转方向,以及将PID控制器输出的期望值,作用于电机运动
5)编写中断服务函数
        编码器溢出计数,定时控制电机,设置比较值
20.3.2.2 程序流程图
image004.png
图20.3.2.2.1 闭环控制流程图

20.3.2.3 程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。首先先看下stepper_motor.h头文件:
#define PULSE_REV               1600                           /* 每圈脉冲数*/
#define TIM_FREQ                168000000U                      /* 定时器主频 */
#define T1_FREQ                 (TIM_FREQ/168)          /* 频率ft值*/
/* 对定时器的频率做单位换算,避免数值太大溢出 */
#define FREQ_UINT       (T1_FREQ/(SECOND/SAMPLING_PERIOD))  
#define ENCODER_SPR     (float)(600*4)    /* 编码器单圈线数*4倍频 */
#define MPR                     5                         /* 步进电机旋转一圈,丝杠的距离;单位:mm/r  */
#define PPM                     (ENCODER_SPR/MPR)/* 每mm内编码器的脉冲数;单位:Pules/mm */
#define MPP                     ((float)(MPR)/ENCODER_SPR)         /* 编码器单步步进距离 */
/* 编码器和步进电机驱动器的比值(表示每个编码器输出线数对应的步进电机脉冲数) */
#define FEEDBACK_CONST  (float)(PULSE_REV/ENCODER_SPR)  


//电机参数结构体
typedef struct
{
    uint8_t state;                 /*电机状态*/
    uint8_t dir;                   /*电机方向*/
    float speed;                   /*电机实际速度*/
    float setspeed;                /*电机实际速度*/
    int32_t location;        /*电机位置*/
} Motor_TypeDef;

/******************************************************************************************/
/* 步进电机引脚定义*/

#define STEPPER_MOTOR_1       1                              /* 步进电机接口序号 */
#define STEPPER_MOTOR_2       2
#define STEPPER_MOTOR_3       3
#define STEPPER_MOTOR_4       4
/*     步进电机方向引脚定义     */

#define STEPPER_DIR1_GPIO_PIN                          GPIO_PIN_14
#define STEPPER_DIR1_GPIO_PORT                         GPIOF
#define STEPPER_DIR1_GPIO_CLK_ENABLE()         do{  __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)           /* PC口时钟使能 */

#define STEPPER_DIR2_GPIO_PIN                          GPIO_PIN_12
#define STEPPER_DIR2_GPIO_PORT                         GPIOF
#define STEPPER_DIR2_GPIO_CLK_ENABLE()         do{  __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)           /* PC口时钟使能 */

#define STEPPER_DIR3_GPIO_PIN                          GPIO_PIN_2
#define STEPPER_DIR3_GPIO_PORT                         GPIOB
#define STEPPER_DIR3_GPIO_CLK_ENABLE()         do{  __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)           /* PC口时钟使能 */

#define STEPPER_DIR4_GPIO_PIN                          GPIO_PIN_2
#define STEPPER_DIR4_GPIO_PORT                         GPIOH
#define STEPPER_DIR4_GPIO_CLK_ENABLE()         do{  __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0)           /* PC口时钟使能 */

/*     步进电机脱机引脚定义     */

#define STEPPER_EN1_GPIO_PIN                           GPIO_PIN_15
#define STEPPER_EN1_GPIO_PORT                          GPIOF
#define STEPPER_EN1_GPIO_CLK_ENABLE()          do{  __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)           /* PC口时钟使能 */

#define STEPPER_EN2_GPIO_PIN                           GPIO_PIN_13
#define STEPPER_EN2_GPIO_PORT                          GPIOF
#define STEPPER_EN2_GPIO_CLK_ENABLE()          do{  __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)           /* PC口时钟使能 */

#define STEPPER_EN3_GPIO_PIN                           GPIO_PIN_11
#define STEPPER_EN3_GPIO_PORT                          GPIOF
#define STEPPER_EN3_GPIO_CLK_ENABLE()          do{  __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)           /* PC口时钟使能 */

#define STEPPER_EN4_GPIO_PIN                           GPIO_PIN_3
#define STEPPER_EN4_GPIO_PORT                          GPIOH
#define STEPPER_EN4_GPIO_CLK_ENABLE()          do{  __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0)           /* PC口时钟使能 */
        上面主要包含两部分,第一部分的宏定义是步进电机本身的参数和闭环控制所需要用到的一些参数,包含步进电机单圈脉冲数,定时器频率,其中宏FEEDBACK_CONST 是细分后步进电机单圈脉冲数与编码器单圈脉冲数的比值,因为在闭环系统中,反馈的PID期望值为编码器的脉冲数,我们需要转换成与步进电机相关脉冲数;第二部分为四个步进电机接口的相关宏定义。接着看下PID头文件pid.c:
/* PID相关参数 */

#define  INCR_LOCT_SELECT  0          /*0:选择位置式  1:增量式控制*/
#if INCR_LOCT_SELECT
/*定义PID参数相关宏*/
#define  L_KP      0.5f                     /* P参数*/
#define  L_KI      0.05f                    /* I参数*/
#define  L_KD      0.00f                    /* D参数*/

#define SMAPLSE_PID_SPEED  20   /*采样率 单位ms*/
#else
#define  L_KP      0.5f                     /* P参数*/
#define  L_KI      0.05f                    /* I参数*/
#define  L_KD      0.00f                    /* D参数*/

#define SMAPLSE_PID_SPEED  20   /*采样率 单位ms*/
#endif

/*定义位置PID参数相关宏*/
/*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]*/
    __IO float  IngMin;
    __IO float  IngMax;
    __IO float  OutMin;
    __IO float  OutMax;
} PID_TypeDef;
   首先通过宏INCR_LOCT_SELECT设置0或1选择位置式还是增量式PID算法,接着通过不同的宏定义选择不同的P、I、D参数以及采样时间,PID_TypeDef 定义的是PID的结构体,主要参与PID算法计算相关。接着看下PID初始化以及PID的控制算法:
/*
* @brief       初始化PID参数
* @param       无
* @retval      无
*/
void pid_init(void)
{
        /*位置环初始化*/
    g_location_pid.SetPoint=(float)(50*PPM);           /* 设定目标Desired Value*/
    g_location_pid.ActualValue=0.0;                     /* 设定目标Desired Value*/
    g_location_pid.SumError=0.0;                          /* 积分值*/
    g_location_pid.Error=0.0;                             /* Error[1]*/
    g_location_pid.LastError=0.0;                 /* Error[-1]*/
    g_location_pid.PrevError=0.0;                 /* Error[-2]*/
    g_location_pid.Proportion=L_KP;                     /* 比例常数 Proportional Const*/
    g_location_pid.Integral= L_KI;                /* 积分常数 Integral Const*/
    g_location_pid.Derivative=L_KD;                     /* 微分常数 Derivative Const*/  
    g_location_pid.IngMax = 20;
    g_location_pid.IngMin = -20;
    g_location_pid.OutMax = 150;                          /* 输出限制 */
    g_location_pid.OutMin = -150;   
}
/**
  * 函数名称:位置闭环PID控制设计
  * 输入参数:当前控制量
  * 返 回 值:目标控制量
  * 说    明:无
  */
int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value)
{
     PID->Error = (float)(PID->SetPoint - Feedback_value);   /*速度档位偏差*/
#if  INCR_LOCT_SELECT
        /*E[k]项*/
    PID->ActualValue += (PID->Proportion * (PID->Error - PID->LastError))
                        + (PID->Integral * PID->Error)     /* E[k-1]项*/
                        + (PID->Derivative * (PID->Error - 2 * PID->LastError +
                                                                        PID->PrevError));  /* E[k-2]项*/
    PID->PrevError = PID->LastError;                      /* 存储误差,用于下次计算*/
    PID->LastError = PID->Error;
#else
    PID->SumError += PID->Error;
    if(PID->SumError > PID->IngMax)
    {
        PID->SumError = PID->IngMax;
    }
    else if(PID->SumError < PID->IngMin)
    {
        PID->SumError = PID->IngMin;
    }
   
    PID->ActualValue = (PID->Proportion * PID->Error)                                   /*E[k]项*/
                       + (PID->Integral * PID->SumError)                     /*E[k-1]项*/
                       + (PID->Derivative * (PID->Error - PID->LastError));
                                                                                                                                        /*E[k-2]项*/
    PID->LastError = PID->Error;
#endif
    if(PID->ActualValue > PID->OutMax)
    {
        PID->ActualValue = PID->OutMax;
    }
    else if(PID->ActualValue < PID->OutMin)
    {
        PID->ActualValue = PID->OutMin;
    }
    return ((int32_t)(PID->ActualValue));                /*返回实际控制数值*/

}
初始化PID结构体成员,赋值初始值,其中L_KP、L_KI、L_KD需要不断调试,选择可以让电机最为稳定可靠的数据,increment_pid_ctrl函数为PID控制器,他的入口参数第一个为目标值,第二个为反馈回来的实际值,然后通过宏INCR_LOCT_SELECT选择相应公式并计算输出期望值。接着来看步进电机的闭环控制代码:
/*PID闭环系统代码*/

Motor_TypeDef  g_step_motor;                          /*电机参数变量*/
volatile uint16_t step_angle = 65535;        /*设置的步进角度*/
/**
* @brief 函数功能: 步进电机运动控制
* @param 输入参数: Dir:步进电机运动方向 0:反转 1正转
           Frequency:步进电机频率,0:停止
* @retval 返 回 值: void
*/
/* location_m如果这个值设置100,那就是20ms的采样频率内要输出100个脉冲信号 */
void stepper_motion_ctrl(uint8_t Dir, uint16_t location_m)
{
    float Step_Delay = 0.0;  /* 步进延时 */

    if(location_m == 0)
{
                /* 当期望值与目标值一致时,代表已到指定位置,停止输出 */
        HAL_TIM_OC_Stop_IT(&g_atimx_handle,ATIM_TIMX_PWM_CH3);
        step_angle = 0;                   /* 清空数据 */
    }        
    else
    {
        if(Dir==CW)
            ST3_DIR(CW);
        else
            ST3_DIR(CCW);
        
/* 经过PID计算得到的结果是编码器的输出期望值,
* 将其转换为步进电机所需的脉冲数( 编码器期望值 * (步进电机一圈所需脉冲数 / 编码器一圈计数
* 值))
*/      
                /*单个脉冲的时间宽度,单位: ms*/
        Step_Delay=(float)(SMAPLSE_PID_SPEED    /(location_m*FEEDBACK_CONST));
        /*ms转成s Step_Delay =Step_Delay/1000;*/      
        /*
           T : 单个脉冲所需的时间
           c : 需要求解的比较值
           f : 定时器计数频率
           T  = c *(1/f)
           Step_Delay/1000 = c*1/1000000 (单位是s)
           c = Step_Delay*1000  求出整一个脉冲的计数值,需要除以2 一个完整的脉冲翻转2次
           所要的计数值:c/2 = Step_Delay*1000/2 = Step_Delay*500
        */            
        step_angle = (uint16_t)(Step_Delay*500);/* 这里除以2,半周期翻转一次 */
        HAL_TIM_OC_Start_IT(&g_atimx_handle,ATIM_TIMX_PWM_CH3);
    }
}
上述代码是整个步进电机位置闭环控制的核心代码,首先函数stepper_motion_ctrl有两个入口参数,第一个为旋转方向,第二个为经过PID计算输出的期望值,也就是编码器的期望值,注意这个函数是每20ms在中断里边调用一次,即20ms执行一次该函数。函数内部操作如下:
①        判断期望值是否为0。如果为0,代表已到目标位置,关闭定时器脉冲输出;
②        不为0时,代表还未到达目标位置,需继续驱动步进电机旋转,所以首先设置旋转方向,然后将编码器的期望值转换成步进电机的脉冲数期望值,并求出单个脉冲需要的时间,知道了单个脉冲时间就可以通过T  = c *(1/f),求出比较值c的值,然后能定时器输出以及输出中断。接着将求得的比较值c作用到定时器上,在输出比较中断回调函数中实现调用:
/**
  * @brief          定时器比较中断
  * @param         htim:定时器句柄指针
  * @note           无
  * @retval         无
  */
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    __IO uint32_t tim_count=0;
    __IO uint32_t tmp = 0;
    if(htim->Instance == ATIM_TIMX_PWM)
    {
        tim_count=__HAL_TIM_GET_COUNTER(&g_atimx_handle);
        tmp = (tim_count+step_angle)&0xFFFF;
        __HAL_TIM_SetCompare(&g_atimx_handle,TIM_CHANNEL_3, tmp);
    }
}
接着看下闭环控制的周期调用:
/* HAL 通用回调接口函数 */

/**
* @brief       定时器更新中断回调函数
* @param        htim:定时器句柄指针
* @note        此函数会被定时器中断函数共同调用的
* @retval      无
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    static uint16_t val=0;
    static uint16_t sum_pulse=0;
    if(htim->Instance == GTIM_TIMX_ENCODER)
    {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle))
        {
            g_timx_encode_count--;
        }
        else
        {
            g_timx_encode_count++;
        }        
    }
    else if(htim->Instance == BTIM_TIMX_INT)
    {
        g_encode_now = gtim_get_encode();                        /* 获取编码器值,用于计算速度 */

        if(val == SMAPLSE_PID_SPEED)
        {
            g_speed=g_encode_now- g_encode_old;
            sum_pulse+=abs(g_speed);
            g_encode_old=g_encode_now;
        /* 实际速度(使用编码器运算)速度 = 一分钟的脉冲数/一圈所需要的脉冲数 就可以求出RPM */
            g_step_motor.speed=g_speed * ((1000/SMAPLSE_PID_SPEED)*60.0) /
                                                                                        (uint16_t)(ENCODER_SPR);                                         g_step_motor.setspeed=((float)(1000000/
                        (uint16_t)(step_angle*2))/PULSE_REV)*60;/*设定速度(脉冲运算转速)*/

            if(g_run_flag)/* 电机启动 */
            {
                g_step_motor.location=gtim_get_encode();
                g_motor_pwm=increment_pid_ctrl(&g_location_pid,
                                                                                        g_step_motor.location);

                (g_motor_pwm > 0) ? (g_step_motor.dir=CW) :
                                                                        (g_step_motor.dir=CCW);
                g_motor_pwm=abs(g_motor_pwm);               
                stepper_motion_ctrl(g_step_motor.dir,g_motor_pwm);
                g_send_flag=1;
            }
            val = 0;
        }
        val ++;
    }   
}
闭环控制函数是在基本定时器TIM6的更新中断里边被循环调用,每20ms调用一次,也就是闭环控制的采样周期是20ms。
在main函数里面编写如下代码:
int main(void)
{     
    uint8_t key,t;
    uint8_t flag_stop=0;
    char buf[32];
    uint8_t debug_cmd=0;
   
    HAL_Init();                                      /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);             /* 设置时钟,168Mhz */
    delay_init(168);                                 /* 延时初始化 */
    usart_init(115200);                              /* 串口初始化为115200 */
    rs232_init(115200);                              /* 串口初始化为115200 */
    led_init();                                      /* 初始化LED */
    key_init();                                      /* 初始化按键 */
    lcd_init();                                      /* 初始化LCD */
    btim_timx_int_init(1000-1 , 84-1);
    gtim_timx_encoder_chy_init(0xFFFF , 0);
    stepper_init(0xFFFF, 168 - 1);        
    pid_init();
   
    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10,10,200,16,16,"Stepper Motor Test",g_point_color);
    lcd_show_string(10,30,200,16,16,"KEY0:Start/Stop",g_point_color);
    lcd_show_string(10,50,200,16,16,"KEY1:SET++",g_point_color);
    lcd_show_string(10,70,200,16,16,"KEY2:SET--",g_point_color);
    printf("KEY0翻转电机状态,开启/关闭\r\n");
    printf("KEY1增加目标位置\r\n");
    printf("KEY2减少目标位置\r\n");

   
    #if DEBUG_ENABLE /*开启调试*/
        debug_init();/*PID调试初始化*/
        /* 初始化同步数据(选择第x组PIDX,目标速度地址,P,I,D参数)到上位机 */
        debug_send_initdata(TYPE_PID1,User_SetPoint,L_KP,L_KI,L_KD);
    #endif   
    while (1)
    {
        #if DEBUG_ENABLE  /*Debug发送部分*/
            if(g_send_flag)
            {
                g_send_flag=0;
                         /* 选择通道1 发送实际速度 */
                debug_send_wave_data(1,(int16_t)g_step_motor.location);
                         /* 选择通道2 发送设置的目标速度 */
                debug_send_wave_data(2,(int16_t)*User_SetPoint);     
            }
        #endif   
        t++;
        if(t % 20 == 0)
        {
            sprintf(buf,"Set_Locaion:%f",*User_SetPoint);
            lcd_show_string(10,90,200,16,16,buf,g_point_color);
            sprintf(buf,"Encode:%d    ",gtim_get_encode());
            lcd_show_string(10,110,200,16,16,buf,g_point_color);
            sprintf(buf,"Motor_Speed :%3d RPM ",(int16_t)g_step_motor.speed);
            lcd_show_string(10,130,200,16,16,buf,g_point_color);
            sprintf(buf,"Set_Speed :%3d RPM ",(int16_t)g_step_motor.setspeed);
            lcd_show_string(10,150,200,16,16,buf,g_point_color);
            LED0_TOGGLE();                                              /*LED0(红灯) 翻转*/        
        }
        delay_ms(10);
        /**********************************按键部分***************************/     
        key = key_scan(0);
        if(key == KEY0_PRES)                                 /* 电机状态翻转 */
        {
            flag_stop=!flag_stop;
            if(flag_stop)
            {
                g_run_flag=1;
                /* 启动电机 */
                HAL_TIM_OC_Start_IT(&g_atimx_handle,ATIM_TIMX_PWM_CH3);

            }
            else
            {
                g_run_flag=0;
                /* 关闭电机 */
                HAL_TIM_OC_Stop_IT(&g_atimx_handle,ATIM_TIMX_PWM_CH3);
            }  
        }
        if(key == KEY1_PRES)                          /* 步数 ++ */
        {
            *User_SetPoint+=(20*PPM);
            if(*User_SetPoint>=60*PPM)  
                *User_SetPoint=60*PPM;
            lcd_show_num(100,90,*User_SetPoint,3,16,g_point_color);
        }
        if(key == KEY2_PRES)                          /* 步数 --  */
        {
            *User_SetPoint-=(20*PPM);
            if(*User_SetPoint<=-60*PPM)  
                *User_SetPoint=-60*PPM;
            lcd_show_num(100,90,*User_SetPoint,3,16,g_point_color );
        }
        /**********************************Debug接收部分**********************/        
        #if DEBUG_ENABLE                                 /* Debug接收部分 */
                 debug_receive_pid(TYPE_PID1,(float*)&g_location_pid.Proportion,                           (float*)&g_location_pid.Integral,(float*)&g_location_pid.Derivative);
                       /* 设置目标调节范围 */
debug_set_point_range((float)(60*PPM),(float)(-60*PPM),120*PPM);
                debug_cmd = debug_receive_ctrl_code();         /* 读取命令 */
                if(debug_cmd == HALT_CODE)                        /* 停机 */
                {
                    g_run_flag = 0;                                /* 标记停机 */
                    HAL_TIM_OC_Stop_IT(&g_atimx_handle,TIM_CHANNEL_3);
                }
                else if (debug_cmd == RUN_CODE)                          /* 运行(解除刹车和停机) */
                {
                    g_run_flag = 1;                                 /* 运行标记 */
                    HAL_TIM_OC_Start_IT(&g_atimx_handle,TIM_CHANNEL_3);/*启动电机*/
                    debug_send_motorstate(RUN_STATE);           /* 电机运行 */
                }
                else if (debug_cmd == BREAKED)                         /* 刹车 */
                {
                    *User_SetPoint = g_step_motor.location;        
                    debug_send_motorstate(BREAKED_STATE);/* 电机刹车 */
                }
        #endif
    }
}
mian函数中主要初始化一些外设,包括PID初始化,定时器初始化、步进电机初始化等等,其中当按下按键KEY0控制步进电机状态开启/关闭,按下KEY1增加目标位置,按下KEY2减少目标位置。
20.3.3 下载验证
硬件连接好之后下载程序到开发板,液晶屏和串口都会显示提示信息,打开正点原子PID调试助手(注意接上串口1的USB_TTL连接至电脑),当我们按下按键KEY0电机开始启动,通过调节按键KEY1和KEY2,可以看到PID调试助手位置变化曲线:
image006.jpg
图20.3.3.1 PID调试助手波形图

由图20.3.3.1我们发现该曲线更像是一条斜线,是因为我们对步进电机电机做了限速,所以PID输出的期望值大小被限制了,导致每次位置增量值一致,所以看起来类似条斜线。



阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-25 21:32

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表