The previous article [Advanced Motor Control - PID Speed Control](https://bbs.eeworld.com.cn/thread-1166004-1-1.html) explained the speed loop control of the motor, which can control the motor to reach the **specified speed** quickly and accurately. This article introduces the position loop control of the motor, which enables the motor to rotate to the **specified position** quickly and accurately. # 1 Review of the difference between position control and speed control [previous article](https://bbs.eeworld.com.cn/thread-1166004-1-1.html), the structure diagram of the motor **speed PID control** is as follows, the target value is the **set speed**, and the **motor speed** is obtained through the encoder as feedback to achieve motor speed control.
Let's look at the motor **position PID control**, its structure diagram is as follows, the target value is the **set position**, and the **accumulated number of pulses of the motor** is obtained through the encoder as feedback to achieve motor position control.
>So: Comparing the two pictures, the main difference between speed control and position control is the difference in control quantity. # 2 Core Program After understanding the difference between speed control and position control, you can modify the program below. ## 2.1 Encoder related
### 2.1.1 Motor and encoder parameters The encoder part needs to be set according to the actual parameters of your own motor. For example, the motor I use: - The number of physical pulses of the encoder in one circle is 11 - The timer encoder mode achieves 4 times the frequency by setting the frequency multiplication - The reduction ratio of the motor's reduction gear is 1:34 Therefore, the total number of pulses in one circle of the motor, that is, the number of pulses that the timer can read is `11*4*34= 1496`. ```c #define ENCODER_RESOLUTION 11 /*Number of physical pulses in one revolution of the encoder*/ #define ENCODER_MULTIPLE 4 /*Encoder multiplication, set by the encoder mode of the timer*/ #define MOTOR_REDUCTION_RATIO 34 /*Motor reduction ratio*/ /*Total number of pulses in one revolution of the motor (number of pulses that the timer can read) = number of physical pulses of the encoder*encoder multiplication*motor reduction ratio*/ /* 11*4*34= 1496*/ #define TOTAL_RESOLUTION ( ENCODER_RESOLUTION*ENCODER_MULTIPLE*MOTOR_REDUCTION_RATIO ) ``` > To learn more about the use of encoders, please refer to the previous article: [Encoder counting principle and motor speed measurement principle - multi-picture analysis](https://bbs.eeworld.com.cn/thread-1156849-1-1.html) ### 2.1.2 The timer encoder mode configures some macro definitions of the timer for encoder capture. ```c #define ENCODER_TIM_PSC 0 /*Counter division*/ #define ENCODER_TIM_PERIOD 65535 /*Counter maximum value*/ #define CNT_INIT 0 /*Counter initial value*/ ``` The configuration mainly focuses on the reload value, multiplier, and overflow interrupt settings. ```c /* TIM4 channel 1 channel 2 quadrature encoder*/ void TIMx_encoder_init(void) { GPIO_InitTypeDef GPIO_InitStruct; /*GPIO*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; /*Time base*/ TIM_ICInitTypeDef TIM_ICInitStruct; /*Input channel*/ NVIC_InitTypeDef NVIC_InitStructure; /*Interrupt*/ /*GPIO initialization*/ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); /*Enable GPIO clock AHB1*/ GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; /*Multiplex function*/ GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; /*Speed 100MHz*/ GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_In it(GPIOB, &GPIO_InitStruct); GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4); GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_TIM4); /*Time base initialization*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); /*Enable timer clock APB1*/ TIM_DeInit(TIM4); TIM_TimeBaseStructInit(&TIM_TimeBaseStruct); TIM_TimeBaseStruct.TIM_Prescaler = ENCODER_TIM_PSC; /*Prescaler*/ TIM_TimeBaseStruct.TIM_Period = ENCODER_TIM_PERIOD; /*Period (reload value)*/ TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; /*Continuous up counting mode*/ TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStruct); /*Encoder mode configuration: capture channel 1 and channel 2 simultaneously (i.e. 4 times frequency), polarity is Rising*/ TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_ICStructInit(&TIM_ICInitStruct); TIM_ICInitStruct.TIM_ICFilter = 0; /*Filter parameters of input channel*/ TIM_ICInit(TIM4,&TIM_ICInitStruct); /*Initialize input channel*/ TIM_SetCounter(TIM4, CNT_INIT); /*Set CNT to initial value*/ TIM_ClearFlag(TIM4,TIM_IT_Update); /*Clear interrupt flag*/ TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); /*Enable interrupt*/ TIM_Cmd(TIM4,ENABLE); /*Enable CR register*/ /*Interrupt configuration*/ NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn; //Timer 4 interrupt NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //Preemption priority 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01; //Sub priority 1 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); } ``` > For more detailed introduction on timer encoder mode configuration, please refer to the previous article: [Motor Control Basics - Timer Encoder Mode Usage and Speed Calculation](https://bbs.eeworld.com.cn/thread-1159198-1-1.html)
### 2.1.3 Read the encoder value To read the value, you can directly read the original value here. There is no need to set the initial value of the count after reading because the overflow interrupt is used. ```c uint32_t read_encoder(void) { uint32_t encoderNum = 0; encoderNum = (TIM4->CNT); return encoderNum; } ``` ### 2.1.4 Encoder count value overflow processing In the overflow interrupt, the main judgment is whether it is an upward overflow or a downward overflow. Because the motor can rotate forward and reverse, the direction of the overflow needs to be recorded. ```c /* Number of timer overflows*/ __IO int16_t EncoderOverflowCnt = 0; //Timer 4 interrupt service function void TIM4_IRQHandler(void) { if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET) //Overflow interrupt { if((TIM4->CR1 & TIM_CounterMode_Down) != TIM_CounterMode_Down) { EncoderOverflowCnt++;/*Encoder count value [upward] overflow*/ } else { EncoderOverflowCnt--;/*Encoder count value [downward] overflow*/ } } TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //Clear interrupt flag bit} ``` ## 2.2 PID calculation related### 2.2.1 Periodic timing timer configuration, by setting the **automatic reload value** and **timer frequency division** to achieve the timing of the specified period. ```c void TIMx_calcPID_init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE); ///Enable TIM7 clock TIM_TimeBaseInitStructure.TIM_Period = arr; //Auto reload value TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //Timer division TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //Up counting mode TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM7,&TIM_TimeBaseInitStructure);//Initialize TIM7 TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE); //Allow timer 6 update interrupt TIM_Cmd(TIM7,DISABLE); //Do not enable timer 7 during initialization NVIC_InitStructure.NVIC_IRQChannel=TIM7_IRQn; //Timer 6 interrupt NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //Preemption priority 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //Sub priority 3 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); } TIMx_calcPID_init(100-1,8400-1);/*10ms timing, this sentence is called in the main function*/ ``` In the timer interrupt, PID calculation is performed every 10ms```c void TIM7_IRQHandler(void) { if(TIM_GetITStatus(TIM7,TIM_IT_Update)==SET) //Overflow interrupt { AutoReloadCallback(); } TIM_ClearITPendingBit(TIM7,TIM_IT_Update); //Clear interrupt flag } ``` > To learn more about the configuration and use of basic timers, please refer to the previous article: [Motor Control Basics - Timer Basics and PWM Output Principles](https://bbs.eeworld.com.cn/thread-1157028-1-1.html)
### 2.2.2 PID calculation is performed in the callback function of the PID motor control logic cycle timer. The two commented-out sentences in the program are speed control codes, which are used to compare with position control. It can be clearly seen from the comparison that the difference between position control and speed control lies in the control amount passed into PID. ```c void AutoReloadCallback() { static __IO int encoderNow = 0; /*Total value at current moment*/ static __IO int encoderLast = 0; /*Total value at previous moment*/ int encoderDelta = 0; /*Change of encoder between current moment and previous moment*/ int res_pwm = 0; /*PWM value calculated by PID*/ /*【1】Read encoder value*/ encoderNow = read_encoder() + EncoderOverflowCnt*ENCODER_TIM_PERIOD;/*Get current cumulative value*/ encoderDelta = encoderNow - encoderLast; /*Get change value*/ encoderLast = encoderNow;/*Update last cumulative value*/ /*【2】PID operation, get PWM control value*/ //res_pwm = pwm_val_protect((int)PID_realize(encoderDelta));/*Pass in encoder's [change value] to realize motor [speed] control*/ res_pwm = pwm_val_protect((int)PID_realize(encoderNow));/*Pass in the encoder's [total value] to realize motor [position] control*/ /*[3] PWM control of motor*/ set_motor_rotate(res_pwm); /*[4] Upload data to the host computer for display*/ //set_computer_value(SEND_FACT_CMD, CURVES_CH1, &encoderDelta, 1); /*Send the actual motor [speed] value to channel 1*/ set_computer_value(SEND_FACT_CMD, CURVES_CH1, &encoderNow, 1); /*Send the actual motor [position] value to channel 1*/ } ``` # 3 Experimental demonstration In the experiment, specifying a target value of 1496 can make the motor rotate forward for 1 circle. If a target value of -1496 is specified, the motor will rotate reversely for 2 circles because it is a relative position. When 14960 revolutions are specified for 10 turns, if the PID parameters are not appropriate, static errors, continuous jitter, or slow error elimination will occur. By continuously adjusting the parameters, you can actually feel the regulatory effects of each PID item. **Video**: