观前提示:本文用来记录和分享我自己做智能小车的经历。本人小白一枚,从码龄可以看出,文章肯定有写的不好的和不正确的地方,欢迎大佬们在评论区指导。
我做智能小车的过程中参考了这几位大佬的文章制作
https://blog.csdn.net/weixin_43924857/article/details/100185086
https://blog.csdn.net/nishiwodebocai21/article/details/85624173
https://blog.csdn.net/Rendezvous1/article/details/108314776
基于stm32的智能小车作品(循迹避障蓝牙)(下)的链接:点击此处
下面开始正文。
序号 | 名称 |
---|---|
1 | 18650电池两节 |
2 | 电池盒 |
3 | stm32f103c8t6最小系统板 |
4 | 小车底盘和四个电机 |
5 | 5路tcrt5000l循迹模块 |
6 | HY SRF05超声波模块 |
7 | JDY-31蓝牙模块 |
8 | st link下载器 |
9 | 主板需要的元器件 |
10 | 杜邦线若干 |
为什么没电机驱动和电源模块呢?
因为电机驱动和电源模块集成在主板上。这里说一下,电机驱动芯片用的是tb6612fng
注意:我画的板好像有问题,作为一个大一新生还没学电路的我并不知道哪里出现了问题,这个问题的主要体现在我最后的成品之后,c8t6的PB引脚好像无法拉高电平,总之就是PB引脚不能正常使用,如果有大佬知道原因,希望能在评论区告诉我,谢谢。
下面是我画的板
我用的驱动是tb6612fng,我的电机最大驱动电压为5v,在这里我只说明驱动的接线,想知道这个驱动的详情请自行搜索学习。
从上面原理图可以看出
第一个驱动的pwm口分别连接PA6、PA7,AIN1、AIN2、BIN1、BIN2分别连接PB4、PB5、PB6、PB7。
第二个驱动的pwm口分别连接PB0、PB1,AIN1、AIN2、BIN1、BIN2分别连接PB12、PB13、PB14、PB15。
由图可一看出pwm的四个引脚对应TIM3的四个通道。
但是在我写程序的时候遇到前面说的PB引脚的问题,因此我只用了通道2和通道4,最后四个电机这样接线
一个通道同时控制两个电机,实测可以正常使用。
综述所述,我只用控制两个pwm口:PA7和PB1,四个IN口PB6、PB7和PB14、PB15就可以让轮子动起来。
先放代码
这是moter.c 文件的代码:
void CarGo(void)
{
//数值越大速度越慢
TIM_SetCompare2(TIM3 , 300);
TIM_SetCompare4(TIM3 , 300);
GPIO_SetBits(GPIOB,GPIO_Pin_6); //控制右边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
GPIO_SetBits(GPIOB,GPIO_Pin_14); //控制左边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
void CarStop(void)
{
TIM_SetCompare2(TIM3 , 900);
TIM_SetCompare4(TIM3 , 900);
GPIO_SetBits(GPIOB,GPIO_Pin_6); //控制右边轮胎
GPIO_SetBits(GPIOB,GPIO_Pin_7);
GPIO_SetBits(GPIOB,GPIO_Pin_14); //控制左边轮胎
GPIO_SetBits(GPIOB,GPIO_Pin_15);
}
void CarLeftAround(void)
{
TIM_SetCompare2(TIM3 , 400);
TIM_SetCompare4(TIM3 , 400);
GPIO_SetBits(GPIOB,GPIO_Pin_6); //控制右边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
GPIO_ResetBits(GPIOB,GPIO_Pin_14); //控制左边轮胎
GPIO_SetBits(GPIOB,GPIO_Pin_15);
}
void CarRightAround(void)
{
TIM_SetCompare2(TIM3 , 400);
TIM_SetCompare4(TIM3 , 400);
GPIO_ResetBits(GPIOB,GPIO_Pin_6); //控制右边轮胎
GPIO_SetBits(GPIOB,GPIO_Pin_7);
GPIO_SetBits(GPIOB,GPIO_Pin_14); //控制左边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
void CarBack(void)
{
TIM_SetCompare2(TIM3 , 300);
TIM_SetCompare4(TIM3 , 300);
GPIO_ResetBits(GPIOB,GPIO_Pin_6); //控制右边轮胎
GPIO_SetBits(GPIOB,GPIO_Pin_7);
GPIO_ResetBits(GPIOB,GPIO_Pin_14); //控制左边轮胎
GPIO_SetBits(GPIOB,GPIO_Pin_15);
}
void CarLeft(void)
{
TIM_SetCompare2(TIM3 , 300);
TIM_SetCompare4(TIM3 , 900);
GPIO_SetBits(GPIOB,GPIO_Pin_6); //控制右边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
GPIO_SetBits(GPIOB,GPIO_Pin_14); //控制左边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
void CarBigLeft(void)
{
TIM_SetCompare2(TIM3 , 100);
TIM_SetCompare4(TIM3 , 900);
GPIO_SetBits(GPIOB,GPIO_Pin_6); //控制右边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
GPIO_SetBits(GPIOB,GPIO_Pin_14); //控制左边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
void CarRight(void)
{
TIM_SetCompare2(TIM3 , 900);
TIM_SetCompare4(TIM3 , 300);
GPIO_SetBits(GPIOB,GPIO_Pin_6); //控制右边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
GPIO_SetBits(GPIOB,GPIO_Pin_14); //控制左边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
void CarBigRight(void)
{
TIM_SetCompare2(TIM3 , 900);
TIM_SetCompare4(TIM3 , 100);
GPIO_SetBits(GPIOB,GPIO_Pin_6); //控制右边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
GPIO_SetBits(GPIOB,GPIO_Pin_14); //控制左边轮胎
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
void TIM3_PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 899;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInit(TIM3 , &TIM_TimeBaseStructure);
//端口复用
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
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);
//PWM通道2
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = 900;
TIM_OC2Init(TIM3 , &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3 , TIM_OCPreload_Enable);
//PWM通道4
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = 900;
TIM_OC4Init(TIM3 , &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3 , TIM_OCPreload_Enable);
TIM_Cmd(TIM3 , ENABLE);
}
//配置电机的引脚
//注意:由于PB引脚的问题,这四个引脚还需在xunji.c的文件中同时配置成上拉输入才能使调速、前进、后退这些功能正常使用
void GPIO_init_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIO的外设时钟
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_14|GPIO_Pin_15;//选择要用的GPIO引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置引脚模式为推挽输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置引脚速度为50MHZ
GPIO_Init(GPIOB,&GPIO_InitStructure);//调用库函数,初始化GPIO
}
注意:由于PB引脚的问题,这四个引脚还需在xunji.c的文件中同时配置成上拉输入才能使调速、前进、后退这些功能正常使用。可以下拉到下面看xunji.c的代码,这里就不重复放出来了。
OUT1、OUT2、OUT3、OUT4、OUT5按顺序接PA1、PA2、PA3、PA4、PA5。
从图中可以知道当检测是白线时是高电平,反之低电平时就是检测到黑线
下面放代码:
xunji.h:
#ifndef __XUNJI_H
#define __XUNJI_H
#include "stm32f10x.h"
#define L1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)
#define L2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)
#define M GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)
#define R2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)
#define R1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5) // L1 L2 M R2 R1
void xunji_config(void);
void Read_xunji_Date(void); //读循迹模块返回的值
#endif
xunji.c:
#include "xunji.h"
void xunji_config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); // 使能PB端口时钟
//配置电机引脚,不知道什么原因要在这里配置成上拉才能实现pwm调速
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7|GPIO_Pin_14|GPIO_Pin_15; //选择对应的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//配置GPIO模式,输入上拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB端口
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); // 使能PA端口时钟
//配置红外引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4| GPIO_Pin_5;; //选择对应的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//配置GPIO模式,浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PC端口
}
//读取红外的信号
void Read_xunji_Date(void)
{
L1;
L2;
M;
R2;
R1;
}
int main(void)
{
SystemInit(); // 配置系统时钟为72M
delay_init(); //延时初始化
xunji_config(); //循迹初始化
TIM3_PWM_Init(); //电机pwm TIM3
CH_SR04_Init(); //超声波定时器 TIM4
USART_Config(); //蓝牙串口
while(1)
{
Read_xunji_Date(); //读循迹线值
//车前4个循迹模块从左到右分别是 L1,L2,M,R2 ,R1
if(L1==1&&L2==1&&M==1&&R2==1&&R1==1)//11111
{
CarGo(); //如果都没有读取到黑线,直走
delay_ms(10);
}
if(L1==1&&L2==1&&M==0&&R2==1&&R1==1)//11011
{
CarGo(); //如果中间一个读取到黑线,直走
delay_ms(10);
}
if(L1==1&&L2==0&&M==0&&R2==0&&R1==1)//10001
{
CarGo(); //如果中间三个读取到黑线,直走
delay_ms(10);
}
if(L1==1&&L2==0&&M==1&&R2==1&&R1==1)//10111
{
CarBigLeft(); //如果第二个读取到黑线,左转
delay_ms(10);
}
if(L1==1&&L2==1&&M==1&&R2==0&&R1==1)//11101
{
CarBigRight(); //如果第四个读取到黑线,右转
delay_ms(10);
}
if(L1==0&&L2==1&&M==1&&R2==1&&R1==1)//01111
{
CarBigLeft(); //如果第一个读取到黑线,左转
delay_ms(10);
}
if(L1==1&&L2==1&&M==1&&R2==1&&R1==0)//11110
{
CarBigRight(); //如果第五个读取到黑线,左转
delay_ms(10);
}
}
}
至此循迹小车就简单的做完了,这是我第一次做的作品,随便用这种方式记录下来。
循迹的方法比较简单,只是为了能实现循迹功能。
整个过程会分两篇,下篇就把超声波和蓝牙写完。
点此下载
提取码:pdqg