基于freertos的闭环控制遥控小车
基于stm32HAL库编写及移植,MCU为f407vet6 采用了freertos作为框架,便于后期增加功能,同时将闭环调速与主程序分离,并将ps2信号接收置于最高优先级。一方面可以灵活控制每层的运行周期,另一方面避免低优先级程序阻塞导致整个程序运行受到影响。
预计会将云台的控制加入,采用freertos延迟中断形式
1.UART不定长收发,基于IDLE中断帧判断。(与树莓派进行通信进行符文模块追踪,得到2位16进制模拟值(更新至stm32f4xxit.h与stm32f4xxit.c中 2.增加了控制电调及云台舵机的定时器控制,以及角度环PID计算(更新至stm32f4xxit.h与stm32f4xxit.c中
IDLE中断实现DMA不定长收发原理
对IDLE中断使能并开启串口dma接收
void USRT_DMA_IDLE_Start()
{
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //使能IDLE中断
HAL_UART_Receive_DMA(&huart1,Uart1RxBuff,UART_DMA_BUFF_LEN_MAX); //开启DMA接收
}
DMA中断回调
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
UART_Rx_Callback(&huart1);
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
通过IDLE中断帧判断,如果未触发IDLE中断,则继续向缓存下一位写入
void UART_Rx_Callback(UART_HandleTypeDef *huart)
{
//判断IDLE中断
if(!__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))
{
return;
}
//清除IDLE中断--直接读取SR、DR寄存器即可清除IDLE中断
__HAL_UART_CLEAR_IDLEFLAG(huart);
// temp = huart->Instance->SR;
// temp = huart->Instance->DR;
UART1_Rx_IDLE_Callback();
}
当IDLE中断触发后,进入IDLE回调,完成一帧的接收
void UART1_Rx_IDLE_Callback()
{
//暂停DMA
HAL_UART_DMAStop(&huart1);
//获取DMA缓存区剩余长度
//实际长度 = 总长度-剩余长度
Uart1RxBuffLen = UART_DMA_BUFF_LEN_MAX-__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
//回发测试
HAL_UART_Transmit(&huart1,Uart1RxBuff,Uart1RxBuffLen,100);
if(state==1){
if(Uart1RxBuff[1]>17)
AngleV+=Angle_PID_P( 128-Uart1RxBuff[1]);
if(Uart1RxBuff[0]>17)
AngleP+=Angle_PID_V(128-Uart1RxBuff[0]);
}
//开始DMA接收
HAL_UART_Receive_DMA(&huart1,Uart1RxBuff,UART_DMA_BUFF_LEN_MAX);
}
//说明持续更新中...
可调用函数分别封装在两个库中,ps2.h中封装了ps通信用函数,encoder.h中封装了包含闭环控制、编码器更新以及模拟值转化为电机pwm的函数。 关于encoder.h的部分代码
int WHEEL_SPD_UPD_A(void){
int Spd=(int)(int16_t)__HAL_TIM_GetCounter(&htim3)-10000;//由于类型转换太麻烦就直接设定10000初始值,减掉(计数器默认返回无符号整形)
return 60*25*Spd/1320;
}//编码器线数11,减速比1:30,采用ti1ti2双边沿触发计数,计数周期40ms,得到结果为每分钟转速
官方对于ti1ti2双边沿计数的示例,相当于11*30*4个脉冲每圈
关于pid闭环计算
int Incremental_PID_calc_A(int spdw,int spdt){//spdw:期望速度,spdt:实际速度
static int bias=0, last_bias=0, prev_bias=0;
bias=spdt-spdw;
double P=kp*(bias-last_bias);
double I=ki*bias;
double D=kd*(bias-2*last_bias+prev_bias);
prev_bias=last_bias;
last_bias=bias;
static int PID;PID-=(int)(P+I+D);
if(PID>1000)//pid限幅
PID=1000;
if(PID<0)
PID=0;
return PID;
}
采用了增量式pid对轮速进行闭环计算,将比较得到的结果经过pid计算得到pwm输出值(其中重装载值为1000)。 实现转速闭环的原理
void PWM_SET(char a,int pwm){
if(pwm<0)
pwm=-pwm;
if(a=='A')
__HAL_TIM_SET_COMPARE(&htim9,TIM_CHANNEL_1,pwm);
if(a=='B')
__HAL_TIM_SET_COMPARE(&htim9,TIM_CHANNEL_2,pwm);
if(a=='C')
__HAL_TIM_SET_COMPARE(&htim12,TIM_CHANNEL_1,pwm);
if(a=='D')
__HAL_TIM_SET_COMPARE(&htim12,TIM_CHANNEL_2,pwm);
}//pwm输出
手柄接收到方向控制为模拟值,无摇杆时为128,为了避免抖动导致错误的移动,设置了手柄控制盲区。
//将模拟值转化为pwm输出
void AutoCtrl(uint8_t X_val,uint8_t Y_val){
A=0,B=0,C=0,D=0;
if(((X_val>=114)&&(X_val<=138))&&((Y_val>=114)&&(Y_val<=138)))
{ PWM_SET('A',0);
PWM_SET('B',0);
PWM_SET('C',0);
PWM_SET('D',0);
}//所有轮子刹车
else{//计算每个轮子速度
if(X_val>138)
{A+=0.3*(X_val-138);
D+=0.3*(X_val-138);
C+=0.3*(114-X_val);
B+=0.3*(114-X_val);}
if(X_val<114)
{A-=0.3*(114-X_val);
D-=0.3*(114-X_val);
B-=0.3*(X_val-138);
C-=0.3*(X_val-138);}
if(Y_val>138){
A+=0.5*(Y_val-100);
B+=0.5*(Y_val-100);
C+=0.5*(Y_val-100);
D+=0.5*(Y_val-100);
}
if(Y_val<114){
A+=0.5*(Y_val-130);
B+=0.5*(Y_val-130);
C+=0.5*(Y_val-130);
D+=0.5*(Y_val-130);
}
//跟据结果进行方向控制及pwm输出
if(A>=0)
{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
PWM_SET('A',Incremental_PID_calc_A(A,V_A));}
else
{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);
PWM_SET('A',Incremental_PID_calc_A(-A,-V_A));}
if(B>=0)
{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_RESET);PWM_SET('B',Incremental_PID_calc_B(B,V_B));}
else
{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_11,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_10,GPIO_PIN_RESET);PWM_SET('B',Incremental_PID_calc_B(-B,-V_B));
}
if(C>=0)
{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_6,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_7,GPIO_PIN_RESET); PWM_SET('C',Incremental_PID_calc_C(C,V_C));}
else
{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_7,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_6,GPIO_PIN_RESET); PWM_SET('C',Incremental_PID_calc_C(-C,-V_C));}
if(D>=0)
{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_4,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_5,GPIO_PIN_RESET); PWM_SET('D',Incremental_PID_calc_D(D,V_D));}
else
{HAL_GPIO_WritePin(GPIOD,GPIO_PIN_5,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_4,GPIO_PIN_RESET); PWM_SET('D',Incremental_PID_calc_D(-D,-V_D));}
}
}
//手柄信号读取(注意不要在一个周期内多次读取datakey,否则有可能出现未知错误,推测为通信的时序问题)
void StartDefaultTask(void *argument)
{
for(;;)
{
PS2_LX=PS2_AnologData(PSS_LX); //读取模拟值
PS2_LY=PS2_AnologData(PSS_LY);
PS2_RX=PS2_AnologData(PSS_RX);
PS2_RY=PS2_AnologData(PSS_RY);
PS2_KEY=PS2_DataKey(); //更新按键及模拟值并返回按键
osDelay(40);
}
}
//定时更新计数器并重置
void StartTask02(void *argument)
{
for(;;)
{ CNT_init();//重置计数器
osDelay(40);//计数四十毫秒
V_B=WHEEL_SPD_UPD_B();
V_A=WHEEL_SPD_UPD_A();
V_C=WHEEL_SPD_UPD_C();
V_D=WHEEL_SPD_UPD_D();
}
}
//将得到的ps手柄模拟值转化为pwm输出及换向
void StartTask03(void *argument)
{
for(;;)
{
AutoCtrl(255-PS2_LX,PS2_LY) ;//封装好的模拟值转换
osDelay(30);
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。