2022年电赛小车题c题 软件流程

本文介绍了使用STM32RCT6单片机实现小车运动控制的过程,包括编码器接口模式的TIM4初始化、PWM波输出以及PID控制。强调了编码器输入的5V兼容性问题和芯片TB6612FN的使用注意事项。此外,分享了编码器计数、PWM波生成及电机正反转控制的代码实现,并给出了增量式PI控制器的算法。文章还提醒在系统设计中要注意模块间的相互影响,建议先实现开环控制再进行PID调整。

题目要求要用TI公司的MCU,MSP系列的单片机。这里用stm32来实现所以功能,因为msp接触比较少就不分享了,msp430网上资料比较多比较容易上手。

我们主控用的是stm32rct6,这个单片机很强大有8个定时器,对于小车的运动控制要两个编码器模式的定时器来获取小车的每个时刻的脉冲,得到这个就可以得到当前轮子的实时速度了。要一个通用定时器来输出pwm波控制电机,要一个基本计时器来产生一个10ms中断来进行pid调节和脉冲的读取。这个中断也可以用pwm波的定时器来产生。多翻数据手册(不是中文参考手册)确定自己的需求。

驱动模块用的是TB6612FN,这个芯片一定要把stby角拉高才能驱动,vm是电机的额定功率,vcc是单片机高电平的电压看你是5v,还是3.3v,其实接3.3v就可以了。

下面这个图是例子

 兼容性:

 关于定时器的引脚分配,这篇文章分享:

STM32定时器要点-电子工程世界 (eeworld.com.cn)

其实有一个点一定一定要注意:

编码器是5v输入,要看定时器的配置引脚是不是满足5v的输入条件,配置相应的模式!!!(比赛的时候因为这个错误莫名其妙烧了芯片发现不出来,在快结束的时候烧了,这个bug不会一下就烧你芯片但是出现了就是致命的

软件上的控制就这么多,上干货:

encoder.c

/**************************************************************************
函数功能:把TIM4初始化为编码器接口模式
入口参数:无
返回  值:无
**************************************************************************/
void Encoder_Init_TIM4(void)
{
	RCC->APB1ENR|=1<<2;     //TIM4时钟使能
	RCC->APB2ENR|=1<<3;    //使能PORTb时钟
	GPIOB->CRL&=0X00FFFFFF;//PB6 PB7
	GPIOB->CRL|=0X44000000;//浮空输入
	/* 把定时器初始化为编码器模式 */ 
	TIM4->PSC = 0x0;//预分频器
	TIM4->ARR = ENCODER_TIM_PERIOD-1;//设定计数器自动重装值 
  TIM4->CCMR1 |= 1<<0;          //输入模式,IC1FP1映射到TI1上
  TIM4->CCMR1 |= 1<<8;          //输入模式,IC2FP2映射到TI2上
  TIM4->CCER |= 0<<1;           //IC1不反向
  TIM4->CCER |= 0<<5;           //IC2不反向
	TIM4->SMCR |= 3<<0;	          //SMS='011' 所有的输入均在上升沿和下降沿有效
	TIM4->CR1 |= 0x01;    //CEN=1,使能定时器
}
/**************************************************************************
函数功能:单位时间读取编码器计数
入口参数:定时器
返回  值:速度值
**************************************************************************/
int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;    
   switch(TIMX)
	 {
	   case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;
		 case 3:  Encoder_TIM= (short)TIM3 -> CNT;  TIM3 -> CNT=0;break;	
		 case 4:  Encoder_TIM= (short)TIM4 -> CNT;  TIM4 -> CNT=0;break;	
		 default:  Encoder_TIM=0;
	 }
		return Encoder_TIM;
}

另外一个定时器自己写一下

encoder.h

#ifndef __ENCODER_H
#define __ENCODER_H
#include <sys.h>	 
  

/**************************************************************************/
#define ENCODER_TIM_PERIOD (u16)(65535)   //不可大于65535 因为F103的定时器是16位的。
void Encoder_Init_TIM2(void);
void Encoder_Init_TIM4(void);
int Read_Encoder(u8 TIMX);
#endif

pwm波的输出

PWM.C

void MiniBalance_PWM_Init(u16 arr,u16 psc)
{		 					 
//	MiniBalance_Motor_Init(); //初始化电机控制所需IO
	RCC->APB1ENR|=1<<1;       //TIM3时钟使能    
	RCC->APB2ENR|=1<<3;       //PORTB时钟使能     
	GPIOB->CRL&=0XFFFFFF00;   //PORTB0 1复用输出
	GPIOB->CRL|=0X000000BB;   //PORTB0 1复用输出
	TIM3->ARR=arr;//设定计数器自动重装值 
	TIM3->PSC=psc;//预分频器不分频
	TIM3->CCMR2|=6<<12;//CH4 PWM1模式	
	TIM3->CCMR2|=6<<4; //CH3 PWM1模式	
	TIM3->CCMR2|=1<<11;//CH4预装载使能	 
	TIM3->CCMR2|=1<<3; //CH3预装载使能	  
	TIM3->CCER|=1<<12; //CH4输出使能	   
	TIM3->CCER|=1<<8;  //CH3输出使能	
	TIM3->CR1=0x8000;  //ARPE使能 
	TIM3->CR1|=0x01;   //使能定时器3 										  
} 

PWM.h

#ifndef __MOTOR_H
#define __MOTOR_H
#include <sys.h>	 

#define PWMA   TIM3->CCR4
#define AIN2   PBout(15)
#define AIN1   PBout(14)
//#define BIN2   PCout(5)
#define BIN2   PBout(13)
#define BIN1   PBout(12)


#define AIN4   PCout(6)
#define AIN3   PCout(7)
#define BIN3   PCout(8)
#define BIN4   PCout(9)

#define PWMB   TIM3->CCR3
void MiniBalance_PWM_Init(u16 arr,u16 psc);
void MiniBalance_Motor_Init(void);
#endif

电机驱动的引脚自己定义一下,随意一个四个引脚输出高低电平就可以控制正反转了。

然后产生一个10ms的中断,后来主控坏了只能用c8t6只有四个定时器就只能用time1来进行了

中断里面不仅跑的是采样,而且加入的pid,同时也要对pwm波进行限幅保护电机,同时控制电机正反转。

control.c

//int Target_velocity=30;  //设定速度控制的目标速度为50个脉冲每10ms
//int Target_velocity1=30;  //设定速度控制的目标速度为50个脉冲每10ms
int TIM1_UP_IRQHandler(void)  
{    
	if(TIM1->SR&0X0001)//5ms定时中断
	{   
		encoder();
		PWMA_TASK(20);
		PWMB_TASK(20);	

	}       	
	 return 0;	  
} 
void encoder(void){
		 TIM1->SR&=~(1<<0);                                       //===清除定时器1中断标志位		 
		 Encoder1=Read_Encoder(2);                                  //===读取编码器的值,M法测速,输出为每10ms的脉冲数

		 Encoder2=Read_Encoder(4);                                  //===读取编码器的值,M法测速,输出为每10ms的脉冲数
}
/**************************************************************************
函数功能:速度控制函数,最大60左右.不能超过(5v小电机)
入口参数:脉冲
返回  值:无
**************************************************************************/
void PWMA_TASK(int Target_velocity){
		 
		 Xianfu_Pwm();                                            //===PWM限幅
		 Moto1=Incremental_PI(Encoder1,Target_velocity);           //===速度PI控制器
//		 Moto1=Encoder1;
		 Set_Pwm1(Moto1);                                          //===赋值给PWM寄存器
}

void PWMB_TASK(int Target_velocity){
		 
		 Xianfu_Pwm();                                            //===PWM限幅
		 Moto2=Incremental_PI(Encoder2,Target_velocity);           //===速度PI控制器
		 Set_Pwm2(Moto2);                                          //===赋值给PWM寄存器
}
/**************************************************************************
函数功能:赋值给PWM寄存器
入口参数:PWM
返回  值:无
**************************************************************************/
void Set_Pwm1(int moto1)
{	
			if(moto1<0)			AIN2=0,			AIN1=1,AIN4=0,			AIN3=1;
			else 	          AIN2=1,			AIN1=0,AIN4=1,			AIN3=0;
			PWMA=myabs(moto1);
			PWMC=myabs(moto1);
}

void Set_Pwm2(int moto2)
{	
			if(moto2<0)			BIN2=0,			BIN1=1,BIN4=0,			BIN3=1;
			else 	          BIN2=1,			BIN1=0,BIN4=1,			BIN3=0;
			PWMB=myabs(moto2);
			PWMD=myabs(moto2);
}
/**************************************************************************
函数功能:限制PWM赋值 
入口参数:无
返回  值:无
**************************************************************************/
void Xianfu_Pwm(void)
{	
	  int Amplitude=7100;    //===PWM满幅是7200 限制在7100
    if(Moto1<-Amplitude) Moto1=-Amplitude;	
		if(Moto1>Amplitude)  Moto1=Amplitude;	
		if(Moto2<-Amplitude) Moto2=-Amplitude;	
		if(Moto2>Amplitude)  Moto2=Amplitude;	
}

/**************************************************************************
函数功能:绝对值函数
入口参数:int
返回  值:unsigned int
**************************************************************************/
int myabs(int a)
{ 		   
	  int temp;
		if(a<0)  temp=-a;  
	  else temp=a;
	  return temp;
}
/**************************************************************************
函数功能:增量PI控制器
入口参数:编码器测量值,目标速度
返回  值:电机PWM
根据增量式离散PID公式 
pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
e(k)代表本次偏差 
e(k-1)代表上一次的偏差  以此类推 
pwm代表增量输出
在我们的速度控制闭环系统里面,只使用PI控制
pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)
**************************************************************************/
int Incremental_PI (int Encoder,int Target)
{ 	
   float Kp=120,Ki=100;	
	 static int Bias,Pwm,Last_bias;
	 Bias=Encoder-Target;                //计算偏差
	 Pwm+=Kp*(Bias-Last_bias)+Ki*Bias;   //增量式PI控制器
	 Last_bias=Bias;	                   //保存上一次偏差 
	 return Pwm;                         //增量输出
}

 control.h

#ifndef __CONTROL_H
#define __CONTROL_H
#include "sys.h"
  /**************************************************************************

**************************************************************************/
#define PI 3.14159265
int TIM1_UP_IRQHandler(void);  
void Set_Pwm1(int moto1);
void Set_Pwm2(int moto2);
void Xianfu_Pwm(void);
int myabs(int a);
int Incremental_PI (int Encoder,int Target);
void PWMA_TASK(int Target_velocity);
void PWMB_TASK(int Target_velocity);
void encoder(void);
#endif

基本对于小车运动控制就在这里了,基本上所以的坑我们都踩过了,

最后一些惨痛教训:

1.不能带pid加系统的,小车系统每加一个模块就要重新调pid,所以pid是最后调的,先开环把所以功能先实现,要不然你不知道是pid的问题还是你本身模块驱动的问题或是接线的问题。切记切记! 

2.在做设计的时候要留有余量,特别是比赛的时候,用c8t6是把芯片榨干了,但是最后加模块的时候就很痛苦没有资源了,rc可以满足要求也相对比较便宜。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值