基於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) }