手把手教你搭建ROS阿克曼转向小车之(动力电机驱动代码讲解)

        阿克曼转向小车是由两个减速直流电机和一个转向舵机构成,上一篇文章已经介绍了如何驱动舵机,这篇文章将会讲解怎么驱动两个动力电机。控制器的动力电机端口分配如下:

手把手教你搭建ROS阿克曼转向小车之(动力电机驱动代码讲解)_第1张图片

        其中PE13是定时器1的CH3、PE11是定时器1的CH2;PE14是定时器1CH4、PE9是定时器1CH1。具体代码实在HwConfig/hw_STM32F40x.c中:

void MotorInit(uint8_t mName_t,uint32_t mPwm_Arr,uint32_t mPwm_Psc,uint8_t mMotorType){
	GPIO_TypeDef* MOTOR_A_PORT[MOTORn] = {STARBOT_MOTOR1_A_GPIO_PORT, STARBOT_MOTOR2_A_GPIO_PORT, 
											STARBOT_MOTOR3_A_GPIO_PORT,STARBOT_MOTOR4_A_GPIO_PORT};
	GPIO_TypeDef* MOTOR_B_PORT[MOTORn] = {STARBOT_MOTOR1_B_GPIO_PORT, STARBOT_MOTOR2_B_GPIO_PORT, 
											STARBOT_MOTOR3_B_GPIO_PORT,STARBOT_MOTOR4_B_GPIO_PORT};
	TIM_TypeDef * MOTOR_TIM[MOTORn] = {STARBOT_MOTOR1_TIM, STARBOT_MOTOR2_TIM, \
											STARBOT_MOTOR3_TIM, STARBOT_MOTOR4_TIM};

	const uint32_t  MOTOR_TIM_CLK[MOTORn] = {STARBOT_MOTOR1_TIM_CLK, STARBOT_MOTOR2_TIM_CLK, \
												STARBOT_MOTOR3_TIM_CLK, STARBOT_MOTOR4_TIM_CLK};  
	const uint16_t  MOTOR_A_CHANNEL[MOTORn] = {STARBOT_MOTOR1_A_CHANNEL, STARBOT_MOTOR2_A_CHANNEL, \
												STARBOT_MOTOR3_A_CHANNEL, STARBOT_MOTOR4_A_CHANNEL};
	//const uint16_t  MOTOR_B_CHANNEL[MOTORn] = {STARBOT_MOTOR1_B_CHANNEL, STARBOT_MOTOR2_B_CHANNEL, \
	//											STARBOT_MOTOR3_B_CHANNEL, STARBOT_MOTOR4_B_CHANNEL};
	const uint16_t  MOTOR_A_PINSOU[MOTORn] = {STARBOT_MOTOR1_A_PINSOU,STARBOT_MOTOR2_A_PINSOU,\
												STARBOT_MOTOR3_A_PINSOU,STARBOT_MOTOR4_A_PINSOU};
	const uint16_t  MOTOR_B_PINSOU[MOTORn] = {STARBOT_MOTOR1_B_PINSOU,STARBOT_MOTOR2_B_PINSOU,\
												STARBOT_MOTOR3_B_PINSOU,STARBOT_MOTOR4_B_PINSOU};
	const uint32_t  MOTOR_A_PORT_CLK[MOTORn] = {STARBOT_MOTOR1_A_GPIO_CLK, STARBOT_MOTOR2_A_GPIO_CLK, \
												STARBOT_MOTOR3_A_GPIO_CLK, STARBOT_MOTOR4_A_GPIO_CLK};
	const uint32_t  MOTOR_B_PORT_CLK[MOTORn] = {STARBOT_MOTOR1_B_GPIO_CLK, STARBOT_MOTOR2_B_GPIO_CLK, \
												STARBOT_MOTOR3_B_GPIO_CLK, STARBOT_MOTOR4_B_GPIO_CLK};
	const uint32_t  MOTOR_AF_TIM[MOTORn] = {STARBOT_MOTOR1_AF_TIM,STARBOT_MOTOR2_AF_TIM,\
												STARBOT_MOTOR3_AF_TIM,STARBOT_MOTOR4_AF_TIM};
	const uint16_t  MOTOR_A_PIN[MOTORn] = {STARBOT_MOTOR1_A_PIN, STARBOT_MOTOR2_A_PIN, \
												STARBOT_MOTOR3_A_PIN, STARBOT_MOTOR4_A_PIN,};
	const uint16_t  MOTOR_B_PIN[MOTORn] = {STARBOT_MOTOR1_B_PIN, STARBOT_MOTOR2_B_PIN,\
												STARBOT_MOTOR3_B_PIN, STARBOT_MOTOR4_B_PIN};
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_OCInitTypeDef       TIM_OCInitStructure;
	TIM_BDTRInitTypeDef      TIM_BDTRInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(MOTOR_A_PORT_CLK[mName_t] | MOTOR_B_PORT_CLK[mName_t], ENABLE);
	RCC_APB2PeriphClockCmd(MOTOR_TIM_CLK[mName_t], ENABLE);
	if(1 == mMotorType)
	{
		/** init motor_ gpio **/
		GPIO_InitStructure.GPIO_Pin   = MOTOR_A_PIN[mName_t];
		GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;							//复用功能
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;						//速度100MHz
		GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 							//推挽复用输出
		GPIO_InitStructure.GPIO_PuPd   = GPIO_PuPd_UP;  
		GPIO_Init(MOTOR_A_PORT[mName_t], &GPIO_InitStructure);

		GPIO_InitStructure.GPIO_Pin = MOTOR_B_PIN[mName_t];
		GPIO_Init(MOTOR_B_PORT[mName_t], &GPIO_InitStructure);

		GPIO_PinAFConfig(MOTOR_A_PORT[mName_t],MOTOR_A_PINSOU[mName_t],MOTOR_AF_TIM[mName_t]);  //GPIOA8复用为定时器1
		GPIO_PinAFConfig(MOTOR_B_PORT[mName_t],MOTOR_B_PINSOU[mName_t],MOTOR_AF_TIM[mName_t]);  //GPIOA7复用为定时器1
		
		//init timx
		//pwm value ((1 + psc)/168M)*(1+arr)
		//eg: ((1+143)/168M)*(1+9999) = 0.02s --10000 count use 0.02s
		//set arduino pwm value 490hz 255 count 
		//((1 + 575)/168M)(1 + 254) = (1 / 490)
		//TIM_DeInit(MOTOR_TIM[mName_t]);
		TIM_TimeBaseInitStructure.TIM_Period = mPwm_Arr-1;      					//arr 自动重装载值
		TIM_TimeBaseInitStructure.TIM_Prescaler = mPwm_Psc-1;     					//psc 定时器分频
		TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;    
		TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
		TIM_TimeBaseInitStructure.TIM_RepetitionCounter  = 0;						//重复计数次数然后进入中断
		TIM_TimeBaseInit(MOTOR_TIM[mName_t], &TIM_TimeBaseInitStructure); 

		TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                      		//选择定时器模式:TIM脉冲宽度调制模式2
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;          		//比较输出使能
		TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;       		//互补输出允许 TIM_OutputNState_Disable
		TIM_OCInitStructure.TIM_Pulse = 0;
		TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;              		//High为占空比高极性,此时占空比为50%,Low则为反极性,占空比为50%
		TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;		      		//指定空闲状态下的TIM输出比较的引脚状态。		
		if(mName_t == MOTOR1 )
		{
			TIM_OC3Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);           
			TIM_OC3PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);

			TIM_OC2Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
			TIM_OC2PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
			TIM_SetCompare2(MOTOR_TIM[mName_t], 0);	
			TIM_SetCompare3(MOTOR_TIM[mName_t], 0);
		}
		else if(mName_t == MOTOR2)
		{
			TIM_OC1Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);           
			TIM_OC1PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);

			TIM_OC4Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
			TIM_OC4PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
			TIM_SetCompare1(MOTOR_TIM[mName_t], 0);	
			TIM_SetCompare4(MOTOR_TIM[mName_t], 0);				
		}
		else if( mName_t == MOTOR3)
		{                      
			TIM_OC1Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);           
			TIM_OC1PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);

			TIM_OC2Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);
			TIM_OC2PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable);
			TIM_SetCompare1(MOTOR_TIM[mName_t], 0);	
			TIM_SetCompare2(MOTOR_TIM[mName_t], 0);	
		}
		else if(mName_t == MOTOR4)
		{
			TIM_OC3Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);           
			TIM_OC3PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable); 
			
			TIM_OC4Init(MOTOR_TIM[mName_t], &TIM_OCInitStructure);           
			TIM_OC4PreloadConfig(MOTOR_TIM[mName_t],TIM_OCPreload_Enable); 
			TIM_SetCompare3(MOTOR_TIM[mName_t], 0);	
			TIM_SetCompare4(MOTOR_TIM[mName_t], 0);	
		}
		TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
		TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
		TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
		//bit7~5 = 111,则deadtime = (32 + (bit4~bit0)* 16*1/fosc)ns = (32+31)*16*1/72000000 = 14us
		死区时间  72:1us 172:3us 205:5us		
		TIM_BDTRInitStructure.TIM_DeadTime = 0x94;
		TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;               
		TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;
		TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
		TIM_BDTRConfig(MOTOR_TIM[mName_t],&TIM_BDTRInitStructure);
		
		TIM_ARRPreloadConfig(MOTOR_TIM[mName_t],ENABLE);
		TIM_Cmd(MOTOR_TIM[mName_t], ENABLE);
		TIM_CtrlPWMOutputs(MOTOR_TIM[mName_t], ENABLE);
	}
}

         其中Arr和Psc根据实际电机适配的频率进行设置,        TIM_CtrlPWMOutputs(MOTOR_TIM[mName_t], ENABLE),该函数不能省略,否则PWM无法输出。假如让电机1正转则需要设置CH3的占空比为期望的输出,把CH2的占空比设置为0;假如让电机1反转则需要设置CH3的占空比为0,把CH2的占空比设置为期望的输出;电机2同理,具体代码实现如下:

void MotorSpin(const vMotorStr* Src){
	int msDir_t = 0;
	msDir_t = Src->dutyVal * Src->mDir;
	if(1 == Src->mDirveType){
		if(msDir_t>0){
			TIM_SetCompareX(MOTOR_TIM[Src->mName],  MOTOR_A_CHANNEL[Src->mName],0);
			TIM_SetCompareX(MOTOR_TIM[Src->mName],  MOTOR_B_CHANNEL[Src->mName],abs(Src->dutyVal));
		}else if(msDir_t<0){
			TIM_SetCompareX(MOTOR_TIM[Src->mName],  MOTOR_A_CHANNEL[Src->mName],abs(Src->dutyVal));
			TIM_SetCompareX(MOTOR_TIM[Src->mName],  MOTOR_B_CHANNEL[Src->mName],0);
		}else{
			TIM_SetCompareX(MOTOR_TIM[Src->mName],  MOTOR_A_CHANNEL[Src->mName],0);
			TIM_SetCompareX(MOTOR_TIM[Src->mName],  MOTOR_B_CHANNEL[Src->mName],0);
		}			
	}
}

         我们只需要调用MotorSpin函数然后给定转动的PWM期望值即可让电机转动起来。

 

你可能感兴趣的:(单片机,嵌入式硬件)