赞
踩
目录
可选:L298N电机驱动板、野火MOS搭建的驱动板。
配置基本定时器产生定时中断来执行PID运算
配置定时器输出PWM控制电机
配置定时器读取编码器的计数值
编写位置式PID算法
编写速度控制函数
增加上位机曲线观察相关代码
编写按键控制代码
- TIM_HandleTypeDef TIM_TimeBaseStructure;
-
- /**
- * @brief 初始化基本定时器定时,默认50ms产生一次中断
- * @param 无
- * @retval 无
- */
- void TIMx_Configuration(void)
- {
- HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 1, 3);
- HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
-
- __TIM6_CLK_ENABLE();
-
- TIM_TimeBaseStructure.Instance = TIM6;
- TIM_TimeBaseStructure.Init.Period = 50 * 50 - 1;
- TIM_TimeBaseStructure.Init.Prescaler = 1680 - 1;
- TIM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
- TIM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
- HAL_TIM_Base_Init(&TIM_TimeBaseStructure);
-
- // 开启定时器更新中断
- HAL_TIM_Base_Start_IT(&TIM_TimeBaseStructure);
-
- uint32_t temp = (__HAL_TIM_GET_AUTORELOAD(&TIM_TimeBaseStructure) + 1) / 50.0; // 计算周期,单位ms
- set_computer_value(SEND_PERIOD_CMD, CURVES_CH1, &temp, 1); // 给通道 1 发送目标值
- }
-
- /**
- * @brief 定时器更新事件回调函数
- * @param 无
- * @retval 无
- */
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- if (htim == (&TIM_TimeBaseStructure))
- {
- motor_pid_control(); // 每50ms执行一次PID运算
- }
- }
- TIM_HandleTypeDef DCM_TimeBaseStructure;
-
- /**
- * @brief 初始化控制通用定时器
- * @param 无
- * @retval 无
- */
- void Motor_TIMx_Configuration(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- TIM_OC_InitTypeDef TIM_OCInitStructure;
-
- __HAL_RCC_GPIOA_CLK_ENABLE();
- __TIM1_CLK_ENABLE();
-
- // PA8--PWM_TIM_CH1
- GPIO_InitStruct.Pin = GPIO_PIN_8;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
- GPIO_InitStruct.Alternate = PWM_TIM_GPIO_AF;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
- // PA9--PWM_TIM_CH2
- GPIO_InitStruct.Pin = GPIO_PIN_9;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
- // TIM1 66.7us一次周期
- DCM_TimeBaseStructure.Instance = TIM1;
- DCM_TimeBaseStructure.Init.Period = 5600 - 1;
- DCM_TimeBaseStructure.Init.Prescaler = 1 - 1;
- DCM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
- DCM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
- HAL_TIM_PWM_Init(&DCM_TimeBaseStructure);
-
- /*PWM模式配置*/
- TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
- TIM_OCInitStructure.Pulse = 0;
- TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
- TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
- TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
- TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
-
- /*配置PWM通道*/
- HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_1);
- /*开始输出PWM*/
- HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_1);
-
- /*配置PWM通道*/
- HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_2);
- /*开始输出PWM*/
- HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_2);
- }
-
- /**
- * @brief 设置TIM通道的占空比
- * @param channel 通道 (1,2,3,4)
- * @param compare 占空比
- * @note 无
- * @retval 无
- */
- void TIM1_SetPWM_pulse(uint32_t channel, int compare)
- {
- switch (channel)
- {
- case TIM_CHANNEL_1:
- __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_1, compare);
- break;
-
- case TIM_CHANNEL_2:
- __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_2, compare);
- break;
-
- case TIM_CHANNEL_3:
- __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_3, compare);
- break;
-
- case TIM_CHANNEL_4:
- __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_4, compare);
- break;
- }
- }
- /* 定时器溢出次数 */
- __IO int16_t Encoder_Overflow_Count = 0;
-
- TIM_HandleTypeDef TIM_EncoderHandle;
-
- /**
- * @brief 编码器接口初始化
- * @param 无
- * @retval 无
- */
- void Encoder_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct = {0};
- TIM_Encoder_InitTypeDef Encoder_ConfigStructure;
-
- __HAL_RCC_GPIOC_CLK_ENABLE();
- __HAL_RCC_TIM3_CLK_ENABLE();
-
- // PC6--TIM3_CH1
- GPIO_InitStruct.Pin = GPIO_PIN_6;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
- GPIO_InitStruct.Pull = GPIO_PULLUP;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
- HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
-
- // PC7--TIM3_CH2
- GPIO_InitStruct.Pin = GPIO_PIN_7;
- HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
-
- // TIM3 0.78ms一个周期
- TIM_EncoderHandle.Instance = TIM3;
- TIM_EncoderHandle.Init.Prescaler = 0;
- TIM_EncoderHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
- TIM_EncoderHandle.Init.Period = 65535;
- TIM_EncoderHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
- TIM_EncoderHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
-
- /* 设置编码器倍频数 */
- Encoder_ConfigStructure.EncoderMode = TIM_ENCODERMODE_TI12;
- Encoder_ConfigStructure.IC1Polarity = TIM_ICPOLARITY_RISING;
- Encoder_ConfigStructure.IC1Selection = TIM_ICSELECTION_DIRECTTI;
- Encoder_ConfigStructure.IC1Prescaler = TIM_ICPSC_DIV1;
- Encoder_ConfigStructure.IC1Filter = 0;
- Encoder_ConfigStructure.IC2Polarity = TIM_ICPOLARITY_RISING;
- Encoder_ConfigStructure.IC2Selection = TIM_ICSELECTION_DIRECTTI;
- Encoder_ConfigStructure.IC2Prescaler = TIM_ICPSC_DIV1;
- Encoder_ConfigStructure.IC2Filter = 0;
- HAL_TIM_Encoder_Init(&TIM_EncoderHandle, &Encoder_ConfigStructure);
-
- /* 清零计数器 */
- __HAL_TIM_SET_COUNTER(&TIM_EncoderHandle, 0);
-
- /* 清零中断标志位 */
- __HAL_TIM_CLEAR_IT(&TIM_EncoderHandle, TIM_IT_UPDATE);
- /* 使能定时器的更新事件中断 */
- __HAL_TIM_ENABLE_IT(&TIM_EncoderHandle, TIM_IT_UPDATE);
- /* 设置更新事件请求源为:计数器溢出 */
- __HAL_TIM_URS_ENABLE(&TIM_EncoderHandle);
-
- /* 设置中断优先级 */
- HAL_NVIC_SetPriority(TIM3_IRQn, 0, 1);
- /* 使能定时器中断 */
- HAL_NVIC_EnableIRQ(TIM3_IRQn);
-
- /* 使能编码器接口 */
- HAL_TIM_Encoder_Start(&TIM_EncoderHandle, TIM_CHANNEL_ALL);
- }
-
- /**
- * @brief 定时器更新事件回调函数
- * @param 无
- * @retval 无
- */
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- if (htim == (&TIM_EncoderHandle))
- {
- /* 判断当前计数器计数方向 */
- if (__HAL_TIM_IS_TIM_COUNTING_DOWN(&TIM_EncoderHandle))
- { /* 下溢 */
- Encoder_Overflow_Count--;
- }
- else
- { /* 上溢 */
- Encoder_Overflow_Count++;
- }
- }
- }
- typedef struct
- {
- float target_val; // 目标值
- float actual_val; // 实际值
- float err; // 定义偏差值
- float err_last; // 定义上一个偏差值
- float Kp,Ki,Kd; // 定义比例、积分、微分系数
- float integral; // 定义积分值
- }_pid; // 位置式PID
- _pid pid;
-
- /**
- * @brief PID参数初始化
- * @note 无
- * @retval 无
- */
- void PID_param_init(void)
- {
- /* 初始化参数 */
- pid.target_val = 100.0;
- pid.actual_val = 0.0;
- pid.err = 0.0;
- pid.err_last = 0.0;
- pid.integral = 0.0;
- pid.Kp = 5.0;
- pid.Ki = 2.0;
- pid.Kd = 0.0;
-
- float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
- set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3); // 给通道 1 发送 P I D 值
- }
-
- /**
- * @brief 设置目标值
- * @param val 目标值
- * @note 无
- * @retval 无
- */
- void set_pid_target(float temp_val)
- {
- pid.target_val = temp_val; // 设置当前的目标值
- }
-
- /**
- * @brief 获取目标值
- * @param 无
- * @note 无
- * @retval 目标值
- */
- float get_pid_target(void)
- {
- return pid.target_val; // 设置当前的目标值
- }
-
- /**
- * @brief 设置比例、积分、微分系数
- * @param p:比例系数 P
- * @param i:积分系数 i
- * @param d:微分系数 d
- * @note 无
- * @retval 无
- */
- void set_p_i_d(float p, float i, float d)
- {
- pid.Kp = p; // 设置比例系数 P
- pid.Ki = i; // 设置积分系数 I
- pid.Kd = d; // 设置微分系数 D
- }
-
- /**
- * @brief PID算法实现
- * @param actual_val:实际值
- * @note 无
- * @retval 通过PID计算后的输出
- */
- float PID_realize(float actual_val)
- {
- /*计算目标值与实际值的误差*/
- pid.err = pid.target_val - actual_val;
-
- /*误差累积*/
- pid.integral += pid.err;
-
- /*PID算法实现*/
- pid.actual_val = pid.Kp * pid.err + pid.Ki * pid.integral + pid.Kd * (pid.err - pid.err_last);
-
- /*误差传递*/
- pid.err_last = pid.err;
-
- /*返回当前实际值*/
- return pid.actual_val;
- }
-
- /**
- * @brief 电机位置式 PID 控制实现(定时调用)
- * @param 无
- * @retval 无
- */
- void motor_pid_control(void)
- {
- if (is_motor_en == 1) // 电机在使能状态下才进行控制处理
- {
- float cont_val = 0; // 当前控制值
- static __IO int32_t Capture_Count = 0; // 当前时刻总计数值
- static __IO int32_t Last_Count = 0; // 上一时刻总计数值
- int32_t actual_speed = 0; // 实际测得速度
-
- /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD */
- Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * 65535);
-
- // 编码器物理分辨率为16,初始化配置后为4倍,即16*4
- // 减速电机减速比为30,50为基本定时器周期
- // 转速 = 里程/时间,单位为多少转/min。
- /* 转轴转速 = 单位时间内的计数值 / 编码器总分辨率 * 时间系数 */
- actual_speed = ((float)(Capture_Count - Last_Count) / 16 * 4 / 30) / (50 / 1000.0 / 60.0);
-
- /* 记录当前总计数值,供下一时刻计算使用 */
- Last_Count = Capture_Count;
-
- cont_val = PID_realize(actual_speed); // 进行 PID 计算
-
- if (cont_val > 0) // 判断电机方向
- {
- set_motor_direction(MOTOR_FWD);
- }
- else
- {
- cont_val = -cont_val;
- set_motor_direction(MOTOR_REV);
- }
-
- cont_val = (cont_val > 5500) ? 5500: cont_val; // 速度上限处理
- set_motor_speed(cont_val); // 设置 PWM 占空比
-
- set_computer_value(SEND_FACT_CMD, CURVES_CH1, &actual_speed, 1); // 给通道 1 发送实际值
- }
- }
配置基本定时器产生定时中断来执行PID运算
配置定时器输出PWM控制电机
配置定时器读取编码器的计数值
编写增量式PID算法
编写速度控制函数
增加上位机曲线观察相关代码
编写按键控制代码
同上
同上
同上
- typedef struct
- {
- float target_val; // 目标值
- float actual_val; // 实际值
- float err; // 定义当前偏差值
- float err_next; // 定义下一个偏差值
- float err_last; // 定义最后一个偏差值
- float Kp, Ki, Kd; // 定义比例、积分、微分系数
- }_pid; // 速度环PID
- _pid pid;
-
- /**
- * @brief PID参数初始化
- * @note 无
- * @retval 无
- */
- void PID_param_init()
- {
- /* 初始化参数 */
- pid.target_val = 100;
- pid.actual_val = 0.0;
- pid.err = 0.0;
- pid.err_last = 0.0;
- pid.err_next = 0.0;
- pid.Kp = 6;
- pid.Ki = 2.5;
- pid.Kd = 0;
-
- float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
- set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3); // 给通道 1 发送 P I D 值
- }
-
- /**
- * @brief 设置目标值
- * @param val 目标值
- * @note 无
- * @retval 无
- */
- void set_pid_target(float temp_val)
- {
- pid.target_val = temp_val; // 设置当前的目标值
- }
-
- /**
- * @brief 获取目标值
- * @param 无
- * @note 无
- * @retval 目标值
- */
- float get_pid_target(void)
- {
- return pid.target_val; // 设置当前的目标值
- }
-
- /**
- * @brief 设置比例、积分、微分系数
- * @param p:比例系数 P
- * @param i:积分系数 i
- * @param d:微分系数 d
- * @note 无
- * @retval 无
- */
- void set_p_i_d(float p, float i, float d)
- {
- pid.Kp = p; // 设置比例系数 P
- pid.Ki = i; // 设置积分系数 I
- pid.Kd = d; // 设置微分系数 D
- }
-
- /**
- * @brief PID算法实现
- * @param actual_val:实际值
- * @note 无
- * @retval 通过PID计算后的输出
- */
- float PID_realize(float actual_val)
- {
- /*计算目标值与实际值的误差*/
- pid.err = pid.target_val - actual_val;
-
- /*PID算法实现*/
- pid.actual_val += pid.Kp * (pid.err - pid.err_next) + pid.Ki * pid.err + pid.Kd * (pid.err - 2 * pid.err_next + pid.err_last);
-
- /*传递误差*/
- pid.err_last = pid.err_next;
- pid.err_next = pid.err;
-
- /*返回当前实际值*/
- return pid.actual_val;
- }
-
- /**
- * @brief 电机增量式 PID 控制实现(定时调用)
- * @param 无
- * @retval 无
- */
- void motor_pid_control(void)
- {
- if (is_motor_en == 1) // 电机在使能状态下才进行控制处理
- {
- static float cont_val = 0; // 当前控制值
- static __IO int32_t Capture_Count = 0; // 当前时刻总计数值
- static __IO int32_t Last_Count = 0; // 上一时刻总计数值
- int32_t actual_speed = 0; // 实际测得速度
-
- /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD */
- Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * 65535);
-
- // 编码器物理分辨率为16,初始化配置后为4倍,即16*4
- // 减速电机减速比为30,50为基本定时器周期
- // 转速 = 里程/时间,单位为多少转/min。
- /* 转轴转速 = 单位时间内的计数值 / 编码器总分辨率 * 时间系数 */
- actual_speed = ((float)(Capture_Count - Last_Count) / 16 * 4 / 30) / (GET_BASIC_TIM_PERIOD() / 1000.0 / 60.0);
-
- /* 记录当前总计数值,供下一时刻计算使用 */
- Last_Count = Capture_Count;
-
- cont_val = PID_realize(actual_speed); // 进行 PID 计算
-
- if (cont_val > 0) // 判断电机方向
- {
- set_motor_direction(MOTOR_FWD);
- }
- else
- {
- cont_val = -cont_val;
- set_motor_direction(MOTOR_REV);
- }
-
- cont_val = (cont_val > 5500) ? 5500: cont_val; // 速度上限处理
- set_motor_speed(cont_val); // 设置 PWM 占空比
-
- set_computer_value(SEND_FACT_CMD, CURVES_CH1, &actual_speed, 1); // 给通道 1 发送实际值
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。