基于STM32F103C8T6设计的平衡车
gitee:https://gitee.com/brimon-zzy/stm32-balance-car-BriBox-V1
电机驱动使用tb6612
陀螺仪使用mpu6050
屏幕使用iic0.96oled
程序编写
编码器:
PB6:左轮A PB7:左轮B————TIM4
PA0:右轮A PA1:右轮B————TIM2
文件:encoder.c encoder.h 使用TIM2和TIM4的编码器模式。

/************************************************************************** 函数功能:把TIM2初始化为编码器接口模式 入口参数:无 返回 值:无 **************************************************************************/ void Encoder_Init_TIM2(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能定时器4的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOB TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器 TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //设定计数器自动重装值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;////TIM向上计数 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3 TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter = 10; TIM_ICInit(TIM2, &TIM_ICInitStructure); TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM的更新标志位 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //Reset counter TIM_SetCounter(TIM2,0); TIM_Cmd(TIM2, ENABLE); } /************************************************************************** 函数功能:把TIM4初始化为编码器接口模式 入口参数:无 返回 值:无 **************************************************************************/ void Encoder_Init_TIM4(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//使能定时器4的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能PB端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入 GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器 TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //设定计数器自动重装值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;////TIM向上计数 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3 TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter = 10; TIM_ICInit(TIM4, &TIM_ICInitStructure); TIM_ClearFlag(TIM4, TIM_FLAG_Update);//清除TIM的更新标志位 TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //Reset counter TIM_SetCounter(TIM4,0); TIM_Cmd(TIM4, ENABLE); }

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; }

int Read_Speed(u8 TIMX) { int value_1; switch(TIMX) { case 2: value_1 = (short)TIM_GetCounter(TIM2); TIM_SetCounter(TIM2, 0); break; case 4: value_1 = (short)TIM_GetCounter(TIM4); TIM_SetCounter(TIM4, 0); break; default: value_1 = 0; } return value_1; }

/************************************************************************** 函数功能:TIM4中断服务函数 入口参数:无 返回 值:无 **************************************************************************/ void TIM4_IRQHandler(void) { if(TIM4->SR&0X0001)//溢出中断 { } TIM4->SR&=~(1<<0);//清除中断标志位 } /************************************************************************** 函数功能:TIM2中断服务函数 入口参数:无 返回 值:无 **************************************************************************/ void TIM2_IRQHandler(void) { if(TIM2 -> SR & 0X0001)//溢出中断 { } TIM2 -> SR &= ~(1<<0);//清除中断标志位 }

void TIM2_IQRHandler(void) { if(TIM_GetITStatus(TIM2, TIM_IT_Update) != 0) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } void TIM4_IQRHandler(void) { if(TIM_GetITStatus(TIM4, TIM_IT_Update) != 0) { TIM_ClearITPendingBit(TIM4, TIM_IT_Update); } }
PWM输出:

//PWM输出初始化 //arr:自动重装值 //psc:时钟预分频数 //TIM1_PWM_Init(7199,0);//PWM频率=72000/(7199+1)=10Khz void TIM1_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外设时钟使能 //设置该引脚为复用输出功能,输出TIM1 CH1 CH4的PWM脉冲波形 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11; //TIM_CH1 //TIM_CH4 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx TIM_OC4Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能 TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH4预装载使能 TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器 TIM_Cmd(TIM1, ENABLE); //使能TIM1 }
外部中断EXTI:

void MPU6050_EXTI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource5); EXTI_InitStructure.EXTI_Line=EXTI_Line5; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器 //////////////////外部中断5优先级配置也就是MPU6050 INT引脚的配置///////////因为是控制中断,故此优先级应是最高。 NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //使能外部中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; //抢占优先级0, NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道 NVIC_Init(&NVIC_InitStructure); }
电机:
PB12:AIN2 PB13:AIN1
PB14:BIN1 PB15:BIN2

void Motor_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50MHZ GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB AIN1=0,AIN2=0; BIN1=0,BIN1=0; }

int PWM_MAX = 7200, PWM_MIN = -7200; void Limit(int *Moto1, int *Moto2) { if(Moto1 > PWM_MAX) Moto1 = PWM_MAX; if(Moto1 < PWM_MIN) Moto1 = PWN_MIN; if(Moto2 > PWM_MAX) Moto2 = PWM_MAX; if(Moto2 < PWM_MIN) Moto2 = PWN_MIN; }
因为单片机时钟是72MHz,TIM1设定的是1分频,7199 + 1.所以正好是100kHz对应电机的频率。所以限制PWM最高7200

int myabs(int a) { int temp; if(a<0) temp=-a; else temp=a; return temp; } void Load(int moto1, int moto2) { if(moto1<0) AIN2=0, AIN1=1; else AIN2=1, AIN1=0; TIM_SetCompare1(TIM1, myabs(moto1)); // TIM1->CCR1 = mybas(moto1) if(moto2<0) BIN1=1, BIN2=0; else BIN1=0, BIN2=1; TIM_SetCompare4(TIM1, myabs(moto2)); // TIM1->CCR4 = myabs(moto2) }