前言
本项目基于使用STM32的最基本技术,在实验室独自完成的项目,经过细心调试,不断试错改进,使智能小车呈现不同的功能
一,硬件
配置
- STM32F103C8T6最小系统板
- TB6612FNG电机驱动模块
- 智能小车底盘,4个直流电机
- 其余螺丝零件等
连接
PB0 ---> BIN2 PB1 ---> BIN1(电机控制模式输入端)
PB13---> AIN1 PB12---> AIN2
BIN2---> BO2 BIN1---> BO1(电机控制模式输出端)
AIN1---> AO1 AIN2---> AO2
AO1,AO2 ---> CN5,CN8(左轮)
BO1,BO2---> CN9,CN10(右轮)
二,环境配置
略,见
三,代码
1.初始化
- 在main.c中初始化电机对应的GPIO
TB6612_GPIO_Init(); //初始化AIN和BIN的GPIO
- 原函数的编写
*********************GPIO控制AIN和BIN*******************************
* @brief 初始TB6612的 AIN1 AIN2 BIN1 BIN2
* @brief 连接AIN1--PB13 AIN2-PB12
BIN1--PB1 BIN2--PB0
* @param
* @return
*/
void TB6612_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_1|GPIO_Pin_0; //LED1-->PE.5 端口配置, 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_1|GPIO_Pin_0); //PE.5 输出高
}
2.编写PWM函数
- PWM函数初始化
- PWM设置占空比函数
//*********************电机驱动*******************************
TIM3_PWM_Init(999,1439); //舵机PWM =====> 转速
//72MHZ/(359+1) = 200000HZ
//200000/(1999+1) = 100HZ
//arr(1999):自动装载值
//psc(359):时钟预分频数
//将时钟基准除以psc得到频率
//计数到1999+1自动归零
//举例:(999,719)
//72MHZ/(719+1) = 100000HZ
//100000HZ/(999+1) = 100HZ
TIM1_PWM_Init(1999,359); //电机的PWM =====> 决定了PWM波的一个周期多长
TIM_SetCompare1(TIM1,500);// =====> PWM波控制转速
TIM_SetCompare4(TIM1,500);// =====> 捕获/比较寄存器的值 为 1000*********
- 定时器1的PWM初始化
//TIM1 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
/**
* @brief 定时器1的PWM初始化 TIM1CH1--PA8 TIM1CH4--PA11
* @param
* @return
*/
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); //使能定时器1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//设置该引脚为复用输出功能,输出TIM1 CH4的PWM脉冲波形 GPIOA.11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //TIM_CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
//设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形 GPIOA.8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
//初始化TIM1
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的时间基数单位
//初始化TIM1 Channel1 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 OC1
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //使能TIM1在CCR1上的预装载寄存器
//初始化TIM1 Channel4 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC4Init(TIM1, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 OC4
TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable); //使能TIM1在CCR4上的预装载寄存器
TIM_Cmd(TIM1, ENABLE); //使能TIM1
TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能,高级定时器必须开启这个
}
- PWM波控制转速:相当于用TIMx的一个周期的时间减去这个Compare1,
使得TIMx的周期从后面开始的Compare1的时间为TIMx的前部分时间的反向。即若前部分时间为高电平,则Compare1段所在时间为低电平。若前部分时间为低电平,则Compare1段所在时间为高电平
/**
* @brief 设置 TIMx 捕捉比较 1 寄存器值
* @param TIMx:其中 x 可以是 1 到 17(除了 6 和 7)以选择 TIM 外设。
* @param Compare1:指定捕捉比较1寄存器的新值。
* @retval None
*/
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)
{
/* 检查参数 */
assert_param(IS_TIM_LIST8_PERIPH(TIMx));
/* 设置捕捉比较 1 寄存器值 */
TIMx->CCR1 = Compare1;
}
3.编写电机运动驱动函数
根据线序来编写,左边两个轮子,一样的线序,右边两个轮子,一样的线序
- 向前无延时
/**
* @brief 向前无延时
* @param
* @return
*/
void Forward(void)
{
AIN1 =1; // ===> 此处应该指的是:线序 ,不是轮子
AIN2 =0;
BIN1 =1;
BIN2 =0;
//数字越大,转速越快
TIM_SetCompare1(TIM1,1500);
TIM_SetCompare4(TIM1,1500);
}
- 向后无延时
/**
* @brief 向后无延时
* @param
* @return
*/
void Backward(void)
{
AIN1 =0; // ===> 向前的线序,反向线序,就是向后
AIN2 =1; // ===> A ---> 左边两个轮子的线序
BIN1 =0; // ===> B ---> 右边两个轮子的线序
BIN2 =1;
TIM_SetCompare1(TIM1,1500);
TIM_SetCompare4(TIM1,1500);
}
- 向右无延时
右转:相当于左面的两个电机前进,右边的两个电机后退
/**
* @brief 向右无延时
* @param
* @return
右转:相当于右面的两个电机前进,左边的两个电机后退
******************* 前进 后退
******************* 前进 后退
*/
void Rightward(void)
{
AIN1 =1; // ===> A ---> 左边两个轮子的线序 ---> 不用改变
AIN2 =0; // ===> 前进 的 线序
BIN1 =0; // ===> B ---> 右边两个轮子的线序 ---> 改变
BIN2 =1; // ===> 后退 的 线序
TIM_SetCompare1(TIM1,1500);
TIM_SetCompare4(TIM1,1500);
}
- 向左无延时
左转:相当于右面的两个电机前进,左边的两个电机后退
/**
* @brief 向左无延时
* @param
* @return
左转:相当于右面的两个电机前进,左边的两个电机后退
******************* 后退 前进
******************* 后退 前进
*/
void Leftward(void)
{
AIN1 =0; // ===> A ---> 左边两个轮子的线序 ---> 改变成后退
AIN2 =1; // ===> 后退 的 线序
BIN1 =1; // ===> B ---> 右边两个轮子的线序 ---> 不用改变
BIN2 =0; // ===> 前进 的 线序
TIM_SetCompare1(TIM1,1500);
TIM_SetCompare4(TIM1,1500);
}
注:此处还可以 改变占空比 来调节转向
左边轮子速度慢,右边轮子速度快 ===> 实现 左转
右边轮子速度慢,左边轮子速度快 ===> 实现 右转