✅作者简介:大家好我是:麦克斯科技,希望一起努力,一起进步!
个人主页:麦克斯科技
系列专栏:两轮平衡小车制作保姆式教程
️非常欢迎大家在评论区留言交流,互相学习!
提前声明:博客中给出的代码经过多个项目测试,实测能用,性能稳定,请大家放心使用!
本系列博客将从硬件到软件详细介绍“如何制作一辆两轮自平衡小车”,笔者毫无保留,以最通俗易懂的语言,以最简单的实现方案,分享自己从0到1制作平衡小车的全过程,相信跟着我的教程,大家也能顺利制作一台属于自己的平衡车。系列专栏:两轮平衡小车制作保姆式教程
首先,给大家提前交个底,其实制作一台平衡小车并不难,用到的主要模块就是陀螺仪,而最主要的控制算法就是PID算法,而且平衡小车对陀螺仪与PID算法的掌握程度要求并不是很高,所以适合初学者来作为项目练手。
该系列教程一共分为4个板块,分为《硬件选型》、《软件模块》、《直立环、速度环、转向环》、《调参保姆级教程》,4个板块条理清晰,层次分明,简明扼要,请大家跟着我开始学习吧!
编码器和电机的底层程序,其实就是STM32定时器编码器模式与TB6612的使用,非常简单,这里给出STM32F103的范例程序。
我的资源分配方案如下:
编码器1–PA0、PA1 TIM5 右
编码器2–PB6、PB7 TIM4 左
PWMA:B0
PWMB: B1
AIN1:PB12
AIN2:PB13
BIN1:PB14
BIN2:PB15
//**********************编码器时钟初始化*********************
void Encoder_Count_RCC(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
}
//**********************编码器引脚初始化*********************
void Encoder_Count_GPIO(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
//**********TIM4,B6,B7****************
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//**********TIM5,A0,A1****************
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
//**********************编码器功能初始化*********************
void Encoder_Count_Configuration(void)
{
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
//**********TIM4,B6,B7***********************************
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_Period=65535; //65536-1
TIM_TimeBaseInitStruct.TIM_Prescaler=0; //1-1
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);
TIM_ICStructInit(&TIM_ICInitStruct);
TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter=0xF;
TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInit(TIM4, &TIM_ICInitStruct);
TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;
TIM_ICInitStruct.TIM_ICFilter=0xF;
TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInit(TIM4, &TIM_ICInitStruct);
TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Falling,TIM_ICPolarity_Rising);
TIM_Cmd(TIM4,ENABLE);
//**********TIM5,A0,A1***********************************
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_Period=65535; //65536-1
TIM_TimeBaseInitStruct.TIM_Prescaler=0; //1-1
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStruct);
TIM_ICStructInit(&TIM_ICInitStruct);
TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter=0xF;
TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInit(TIM5, &TIM_ICInitStruct);
TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;
TIM_ICInitStruct.TIM_ICFilter=0xF;
TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInit(TIM5, &TIM_ICInitStruct);
TIM_EncoderInterfaceConfig(TIM5,TIM_EncoderMode_TI12,TIM_ICPolarity_Falling,TIM_ICPolarity_Rising);
TIM_Cmd(TIM5,ENABLE);
}
//**********************编码器初始化*********************
void Encoder_Count_Init(void)
{
Encoder_Count_RCC();
Encoder_Count_GPIO();
Encoder_Count_Configuration();
}
//******************编码器数据读取********************************
int Encoder_Value(TIM_TypeDef* TIMx)
{
int channal_val=0;
channal_val = TIMx ->CNT;
if(channal_val>>15)
{
channal_val = (channal_val&0x7FFF)-32767;
}
return channal_val;
}
//****************编码器清零*************************************
void Encoder_Count_Clear(TIM_TypeDef* TIMx)
{
TIMx ->CNT = 0;
}
#ifndef _HAL_ENCODER_H
#define _HAL_ENCODER_H
#include "stm32f10x.h"
//**************************************************
void Encoder_Count_RCC(void);
void Encoder_Count_GPIO(void);
void Encoder_Count_Configuration(void);
void Encoder_Count_Init(void);
int Encoder_Value(TIM_TypeDef* TIMx);
void Encoder_Count_Clear(TIM_TypeDef* TIMx);
#endif
/***
配置PWM通道 产生PWM 一个tb6612可以同时驱动两路电机
***/
//**********************配置系统时钟*********************************
void PWM_RCC(void)
{
//使能GPIO外设(PWM引脚B0 B1 B4 B5 时钟配置)和AFIO复用功能模块时钟 B4 B5是JTDO下载引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //打开time3的中断时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
}
//**********************配置GPIO管脚****************B0 B1 //B4 B5//***************
void PWM_GPIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//PWM管脚PWM1配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//PWM管脚PWM2配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
driver_pin_init();
}
//**********************时钟中断配置函数*********************************
void PWM_TIM3_Configuration(void)
{
TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,DISABLE); //关闭映射功能
TIM_TimeBaseStructure.TIM_Period=100; //计数100,PWM频率10KHz
TIM_TimeBaseStructure.TIM_Prescaler=72; //不分频
TIM_TimeBaseStructure.TIM_ClockDivision=0; //不滤波
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); //初始化
TIM_Cmd(TIM3,ENABLE); //打开定时器外设
//***配置PWM1**********
// TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式1
// TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //打开PWM使能
// TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性
// TIM_OC1Init(TIM3, & TIM_OCInitStructure); //初始化 使用通道1
// TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable); //打开PWM中断使能,否则只能执行一次
//***配置PWM2**********
// TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式1
// TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //打开PWM使能
// TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性
// TIM_OC2Init(TIM3, & TIM_OCInitStructure); //初始化 使用通道2
// TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); //打开PWM中断使能,否则只能执行一次
//
//***配置PWM3**********
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式1
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //打开PWM使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性
TIM_OC3Init(TIM3, & TIM_OCInitStructure); //初始化 使用通道3
TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable); //打开PWM中断使能,否则只能执行一次
//
// //***配置PWM4**********
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式1
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //打开PWM使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性
TIM_OC4Init(TIM3, & TIM_OCInitStructure); //初始化 使用通道4
TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable); //打开PWM中断使能,否则只能执行一次
}
//**************************配置优先级***********************************
void TIM3_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure; //为结构体定义结构体变量
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //对优先级进行分组
NVIC_InitStructure.NVIC_IRQChannel =TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure); //初始化
}
//**********************PWM初始化函数*********************************
void PWM_Init(void)
{
PWM_RCC(); //PWM时钟配置
PWM_GPIO(); //PWM管脚配置
PWM_TIM3_Configuration(); //占空比时钟控制
TIM3_NVIC_Configuration(); //优先级配置
TIM_SetCompare3(TIM3,0); //防止上电就乱动 PB0
TIM_SetCompare4(TIM3,0); //防止上电就乱动 PB1
NEncoder.left_motor_dir = 1;
NEncoder.right_motor_dir = 1;
}
/***驱动引脚配置 PB12 13 14 15***/
void driver_pin_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = AIN1_PIN | AIN2_PIN | BIN1_PIN | BIN2_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
L_MOTOR_GO;
R_MOTOR_GO;
}
#ifndef _HAL_PWM_H
#define _HAL_PWM_H
#include "stm32f10x.h"
#define AIN1_PIN GPIO_Pin_12
#define AIN2_PIN GPIO_Pin_13
#define BIN1_PIN GPIO_Pin_14
#define BIN2_PIN GPIO_Pin_15
//电机初始化相关函数
void PWM_RCC(void); //时钟配置
void PWM_GPIO(void); //管脚配置
void PWM_TIM3_Configuration(void); //占空比时钟控制
void TIM3_NVIC_Configuration(void); //优先级
void PWM_Init(void); //初始化
void driver_pin_init(void);
#endif