STM32控制两路直流电机_1

手头上有一个差分驱动的小车,使用两个直流电机驱动,要实现小车的在给定速度下运动,完成直线行驶,转向,加速,刹车等复杂运动。
使用的电机是12v供电的直流电机,带编码器反馈,这样就可以采用闭环速度控制,这里电机使用PWM驱动,速度控制框图如下:
这里写图片描述
由以上框图可知,STM32通过定时器模块输出PWM波来控制两个直流电机的转动,通过改变PWM占空比的大小可以改变电机的转速,由于我们的控制目标是实现电机运行在速度范围内任意给定的速度,这里就需要采用闭环控制的思想,通过编码器获取电机的实时转速,通过与给定速度做差,将偏差作为PID控制器的输入,通过PID控制改变PWM占空比的大小,从而使电机的速度运行在给定的速度上。
这里使用的电机驱动芯片为TB6612,该芯片可以十分方便的驱动两个直流电机的运行,其驱动逻辑表如下:
这里写图片描述
AIN1,AIN2的不同组合可以实现电机的正反转和停车,PWMA为PWM的输入引脚,通过输入不同的占空比可以改变电机转速的快慢。BIN1,BIN2,PWMB是控制另一路电机的引脚。
首先我们需要利用STM32的定时器模块输出两路PWM波,这是使电机转起来的第一步。初始化PWM:

//初始化PWM引脚
void motorPWMPin_init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_11 ;//TIM1_Chn_1,TIM1_Chn_2
	GPIO_Init(GPIOE,&GPIO_InitStructure);

	GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_TIM1);
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_TIM1);
}

//初始化PWM
void motorPWM_init(void)
{
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrecture;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
	TIM_TimeBaseInitStrecture.TIM_Period = 400;/*PWM's frequency is 20KHz*/
	TIM_TimeBaseInitStrecture.TIM_Prescaler =21-1;//将TIM1的时钟频率设定为8MHz
	TIM_TimeBaseInitStrecture.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStrecture.TIM_CounterMode = TIM_CounterMode_Up;//定时器向上计数
	TIM_TimeBaseInitStrecture.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStrecture);

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High ;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;

	TIM_OC1Init(TIM1,&TIM_OCInitStructure);
	TIM_OC2Init(TIM1,&TIM_OCInitStructure);
	
//	TIM_Cmd(TIM1,ENABLE);
	TIM_CtrlPWMOutputs(TIM1,ENABLE);
}

然后初始化电机控制引脚,程序如下:

//初始化电机控制引脚
void motorCtrlPin_init(void)
{
   GPIO_InitTypeDef  GPIO_InitStructure;
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
  
	//PE7,PE8控制电机A,PE9,PE10控制电机B
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_10;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
   GPIO_Init(GPIOE, &GPIO_InitStructure);
}

需要注意的是设置PWM输出引脚时要讲引脚复用到定时器TIM1,而电机控制引脚只需要设置成简单的推挽输出模式即可。
接着我们需要使用两个定时器的编码器功能用于读取电机的实时转动速度,这里我使用的是定时器3和定时器4.
这里的编码器是精度较低的霍尔感应式编码器,但是基本满足控制精度的要求,驱动代码如下:

void encoderA_init(void)
{
    GPIO_InitTypeDef          GPIO_InitStructure;
	NVIC_InitTypeDef          NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef   TIM_TimeBaseStructure;
    TIM_ICInitTypeDef         TIM_ICInitStructure;
	
	/*CLOCK Enable*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);    //PC6,PC7
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用引脚模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//100MHz
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; 
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉
	
		/*Configure PC6,PC7 as encoder A,B Input*/
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3);
  GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM3);
	
  GPIO_Init(GPIOC,&GPIO_InitStructure); //initialize PORTC
	
	/* Timer configuration in Encoder mode */
	/* Enable the TIM3 Update Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x01;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
  
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Prescaler = 0;   //不分频
  TIM_TimeBaseStructure.TIM_Period = 65535;  //设置为最大
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up ; //向上计数  
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
	
	TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising   , TIM_ICPolarity_Rising   );//上升沿计数
  TIM_ICStructInit(&TIM_ICInitStructure);
  TIM_ICInitStructure.TIM_ICFilter = 10;//设置滤波系数
  TIM_ICInit(TIM3, &TIM_ICInitStructure);
	TIM_ClearFlag(TIM3, TIM_FLAG_Update);      //清除更新中断
  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //使能更新中断
  TIM3->CNT = 0;//将计数值设为0
	TIM_Cmd(TIM3, ENABLE);//enable TIM3
	printf("Encoder_A initializztion is OK\n");
}

TIM4的编码器模式配置与此相似,不再赘述。
小车的运动控制和转速控制PID算法,请见下节;

  • 46
    点赞
  • 341
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
以下是基于STM32PWM控制L298N控制两路直流电机的基本程序: ```c #include "stm32f4xx.h" #define IN1 GPIO_Pin_0 #define IN2 GPIO_Pin_1 #define IN3 GPIO_Pin_2 #define IN4 GPIO_Pin_3 void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = IN1 | IN2 | IN3 | IN4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); } void TIM_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3); GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_TIM3); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period = 9999; TIM_TimeBaseStructure.TIM_Prescaler = 83; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 5000; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM3, &TIM_OCInitStructure); TIM_OC2Init(TIM3, &TIM_OCInitStructure); TIM_Cmd(TIM3, ENABLE); } int main(void) { GPIO_Config(); TIM_Config(); while (1) { GPIO_SetBits(GPIOA, IN1); GPIO_ResetBits(GPIOA, IN2); GPIO_SetBits(GPIOA, IN3); GPIO_ResetBits(GPIOA, IN4); TIM_SetCompare1(TIM3, 5000); TIM_SetCompare2(TIM3, 5000); delay_ms(2000); GPIO_SetBits(GPIOA, IN1); GPIO_ResetBits(GPIOA, IN2); GPIO_ResetBits(GPIOA, IN3); GPIO_SetBits(GPIOA, IN4); TIM_SetCompare1(TIM3, 5000); TIM_SetCompare2(TIM3, 5000); delay_ms(2000); GPIO_ResetBits(GPIOA, IN1); GPIO_SetBits(GPIOA, IN2); GPIO_ResetBits(GPIOA, IN3); GPIO_SetBits(GPIOA, IN4); TIM_SetCompare1(TIM3, 5000); TIM_SetCompare2(TIM3, 5000); delay_ms(2000); GPIO_ResetBits(GPIOA, IN1); GPIO_SetBits(GPIOA, IN2); GPIO_SetBits(GPIOA, IN3); GPIO_ResetBits(GPIOA, IN4); TIM_SetCompare1(TIM3, 5000); TIM_SetCompare2(TIM3, 5000); delay_ms(2000); } } ``` 在此示例中,使用了GPIOA的Pin0到Pin3来控制L298N,使用了TIM3的CH1和CH2来输出PWM信号。程序实现了四个不同的电机运行状态,每个状态间隔2秒钟。你可以根据实际需要修改GPIO引脚和PWM时钟分频系数、占空比等参数。
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值