项目——STM32小车项目

一、设计内容

1、 已有材料:STM32小车相关硬件
2、 开发工具:Keil uVision5
3、 资料:相关硬件说明书、指导老师提供的部分基础工程代码及教学视频
4、 实现功能:小车基本动作控制驱动、小车调速控制、蓝牙实现小车遥控、小车的自动避障(超声波+红外线)、小车的循迹功能

二、芯片引脚介绍

在这里插入图片描述

三、开发环境配置

1) 安装MDK——keil5.28
① 下载md528a.exe
② Customer information全部空格 安装
③ 安装Keil.STM32F1xx_DFP.1.0.5.pack直接next
④ 解压keygen_new2032.rar 管理员身份运行 破解keil5
在这里插入图片描述
2) 新建STM32F103工程

① 选择工程路径和命名

在这里插入图片描述

② 芯片型号选择
在这里插入图片描述

③ 函数库选择
在这里插入图片描述

④ 创建一个.c文件 保存为main.c 加入keil中
在这里插入图片描述

⑤ 编译
在这里插入图片描述

⑥ 出错解决
在这里插入图片描述

3) 将程序下载进小车
① 跳线帽接好,芯片对应的USB线连接电脑
在这里插入图片描述

② 下载程序
在这里插入图片描述

③ 成功标志

在这里插入图片描述

三、小车基本动作控制驱动

1、原理分析
在这里插入图片描述

  1. 双路电机驱动对应的接口为M1+ M1- M2+ M2-
  2. 通过上面的芯片引脚端口图,找到对应的芯片引脚为GPIOB组的Pin_6 Pin7 Pin8 Pin9
  3. 可以看到M1+ M1-是控制同条电路,而M2+ M2-是控制另外一条电路
  4. 也就是说,小车左边前后轮为一组控制,小车右边前后轮为一组控制
  5. M1+对应的是右前轮,M1-对应的是右后轮,而M2则为左边
  6. 当M1+与M1-输出相同电平,都无法唯一确定电流的去向,表现为右轮不动
  7. 当M1+与M1-输出相反电平,若M1+ M1-为10,则右边前进,而01则为右边后退

2、代码编写步骤

  1. 开启使能时钟
  2. GPIO结构体初始化 端口配置->速度配置->模式选择
  3. 引脚高低电平设置 GPIO_SetBits() GPIO_ResetBits()

3、 实现代码

(电机初始化)

void Motor_Init(void){
	//1、使能GPIO时钟---STM32芯片编程模块必须先做这一步
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	//2、GPIO的结构体初始化
	GPIO_InitTypeDef  aaa;//定义一个用来初始化GPIO的结构体
	aaa.GPIO_Pin  = GPIO_Pin_5 | GPIO_Pin_6| GPIO_Pin_7| GPIO_Pin_8 | GPIO_Pin_9;//PB6 PB7 PB8 PB9对应电机四个轮  PB5对应使能电池
	aaa.GPIO_Speed  = GPIO_Speed_50MHz;
	aaa.GPIO_Mode   = GPIO_Mode_Out_PP;//普通推挽输出
	GPIO_Init(GPIOB,&aaa);//调用官方的初始化函数 完成对PB组引脚初始化
	
	//3、PB5 = 1,使能电池供电
	GPIO_SetBits(GPIOB,GPIO_Pin_5);
}

(小车基础动作)

//小车前进函数
void Car_Forward()
{
	//PB6 = 1  PB8 =1
	GPIO_SetBits(GPIOB,GPIO_Pin_6 | GPIO_Pin_8);
	//PB7 = 0  PB9 = 0
	GPIO_ResetBits(GPIOB,GPIO_Pin_7 | GPIO_Pin_9);
}

//小车后退函数
void Car_Back()
{
	//PB7 = 1  PB9 = 1
	GPIO_SetBits(GPIOB,GPIO_Pin_7 | GPIO_Pin_9);
	
	//PB6 = 0  PB8 = 0
	GPIO_ResetBits(GPIOB,GPIO_Pin_6 | GPIO_Pin_8);
}
//小车停止函数
void Car_Stop()
{
	//PB6 = 1  PB7 = 1
	GPIO_ResetBits(GPIOB,GPIO_Pin_6 | GPIO_Pin_7);
	
	//PB8 = 0  PB9 = 0
	GPIO_ResetBits(GPIOB,GPIO_Pin_8 | GPIO_Pin_9);
}
//小车前左转函数
void Car_Left()
{
	//PB6 = 1  PB7 = 1  PB8 = 1
	GPIO_SetBits(GPIOB,GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8);
	
	//PB9 = 0
	GPIO_ResetBits(GPIOB,GPIO_Pin_9);
}
//小车前右转函数
void Car_Right()
{
	//PB8 = 1  PB9 = 1  PB6 = 1
	GPIO_SetBits(GPIOB,GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_6);
	
	//PB7=0
	GPIO_ResetBits(GPIOB,GPIO_Pin_7);
}

四、小车调速控制

1、原理分析

使用计数器,控制高电平在总时间的比例,实现小车的速度控制。
例如
在20us时间里,高电平输出时间为10us,实现小车半速前行;
在20us时间里,高电平输出时间为5us,实现小车1/4速度前行;

2、代码编写步骤

  1. 开启GPIOB、AFIO、TIM4的使能时钟
  2. 初始化GPIO
  3. 初始化基本定时器
  4. 利用定时器实现对应通道的电平输出控制
  5. 开启定时器

3、实现代码

(电机初始化)

void Motor_Init(void){
	//1、开始使能时钟---STM32芯片编程模块必须先做这一步
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//电机调速添加
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//电机调速添加
	
	//2、GPIO结构初始化 将对应端口设置作复用功能
	GPIO_InitTypeDef  aaa;//定义一个用来初始化GPIO的结构体
	aaa.GPIO_Pin  = GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 | GPIO_Pin_9;//
	aaa.GPIO_Speed  = GPIO_Speed_50MHz;
	aaa.GPIO_Mode   = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_Init(GPIOB,&aaa);//调用官方的初始化函数 完成对PB组引脚初始化
	
	//3、基本定时器TIM4初始化(分频系数  计数模式  自动重装载值  分频因子)
	TIM_TimeBaseInitTypeDef ccc;
	ccc.TIM_Prescaler = 72-1;//72Mhz/72 = 1Mhz  1个脉冲对应1us
	ccc.TIM_CounterMode = TIM_CounterMode_Up;//递增计数模式
	ccc.TIM_Period      = 2000-1;//ARR 2000  自动重装载值  刚好周期为2ms
	ccc.TIM_ClockDivision = TIM_CKD_DIV1;//上面的预分频系数够了  这里设置为1分频即不分频
	TIM_TimeBaseInit(TIM4,&ccc);
	
	//4、定时器TIM4输出通道1 2 3 4的初始化
	TIM_OCInitTypeDef ddd;
	ddd.TIM_OCMode = TIM_OCMode_PWM1;//PWM模式1---递增模式下  CNT<CCR时输出有效电平   CNT>CCR时输出另一种电平
	ddd.TIM_OCPolarity = TIM_OCPolarity_High;//极性高----高电平有效  配合上面的模式,即左边输出高 右边输出低
	ddd.TIM_Pulse      = 0;// 比较值 0就是默认都只输出100%低电平
	ddd.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OC1Init(TIM4,&ddd);//注意这里是OC1---对应通道1
	TIM_OC2Init(TIM4,&ddd);//注意这里是OC2---对应通道2
	TIM_OC3Init(TIM4,&ddd);//注意这里是OC3---对应通道3
	TIM_OC4Init(TIM4,&ddd);//注意这里是OC4---对应通道4

	//5、开启定时器4计数器--开始工作计数
	TIM_Cmd(TIM4,ENABLE);
}

(小车基础动作的调速->通过一个函数实现小车的五种基础动作)

//speed->[0,1999]
void Car_Speed_Init(int Speed1,int Speed2,int Speed3,int Speed4){
	TIM4->CCR1 = Speed1;
	TIM4->CCR2 = Speed2;
	TIM4->CCR3 = Speed3;
	TIM4->CCR4 = Speed4;
}

(例子说明)

CCR1 CCR2 CCR3 CCR4 功能
1500 0 1500 0 前进
0 1600 0 1600 后退
1000 0 1200 0 差速左转
1400 0 1000 0 差速右转

五、蓝牙遥控小车

1、原理分析

蓝牙 与芯片 与手机连接示意图:

AT指令模式:蓝牙->芯片 默认波特率38400
透穿模式:蓝牙->手机 默认波特率9600

2、蓝牙连接电脑

  1. 按照小车底板的排座接口接好蓝牙
  2. 将板子的USB直连 同时用USB线连到电脑 即蓝牙直接和电脑相连
  3. 电脑端运行串口助手软件 开始使用AT指令调试蓝牙
    在这里插入图片描述
  4. 运行XCOM串口助手 连接上蓝牙
    在这里插入图片描述
  5. 拔下蓝牙 按住蓝牙上的小按键 不松开再插回去 蓝牙慢闪
  6. 通过发送文本框输入
a)	AT回车            蓝牙会反馈OK
b)	AT+NAME? 回车    蓝牙会反馈自己的名称
c)	AT+NAME=你设置的新蓝牙名字 回车  设置新的蓝牙名称
d)	AT+UART? 回车        查询蓝牙透传模式下的通信波特率
e)	AT+UART=9600,0,0 回车 设置蓝牙波特率为9600 停止位1  无校验位

3、蓝牙连接手机

  1. 跳线帽短接STM32和蓝牙
    在这里插入图片描述
  2. 下载<蓝牙串口>app
  3. 输入<1234>实现手机与蓝牙的连接

4、蓝牙遥控小车

代码编写步骤

  1. 开启使能 GPIOA USART1 AFIO 时钟
  2. 配置USART1 USART_Init()
  3. 配置GPIOA的 PA9 PA10——对应蓝牙端口的引脚
  4. 开启串口接收中断USART_ITConfig()
  5. 开启串口 USART_Cmd()

实现代码

void UART1_Init(void)
{
	//1、使能 GPIOA USART1 AFIO 时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	//2、配置串口1
	USART_InitTypeDef aaa;
	aaa.USART_BaudRate   = 9600;// 和蓝牙一致的波特率
	aaa.USART_WordLength = USART_WordLength_8b;//有效数据长度8bit
	aaa.USART_StopBits   = USART_StopBits_1;//1bit停止位
	aaa.USART_Parity     = USART_Parity_No;//不要校验位
	aaa.USART_Mode       = USART_Mode_Tx | USART_Mode_Rx;//收发模式
	aaa.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不需要硬件流控
	USART_Init(USART1,&aaa);
	
	//3、初始化GPIOA结构体  配置蓝牙对应的引脚
	GPIO_InitTypeDef  bbb;
	bbb.GPIO_Pin  = GPIO_Pin_9;//TX
	bbb.GPIO_Speed  = GPIO_Speed_50MHz;
	bbb.GPIO_Mode   = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_Init(GPIOA,&bbb);
	bbb.GPIO_Pin  = GPIO_Pin_10;//RX
	bbb.GPIO_Speed  = GPIO_Speed_50MHz;
	bbb.GPIO_Mode   = GPIO_Mode_IN_FLOATING;//浮空输入模式
	GPIO_Init(GPIOA,&bbb);
	
	//3、配置USART1的中断优先级
	NVIC_InitTypeDef  ccc;//定义一个用来初始化中断优先级的结构体
	ccc.NVIC_IRQChannel = USART1_IRQn;//USART1全局中断号,类似大家的身份证号  让芯片知道是哪个中断
	ccc.NVIC_IRQChannelPreemptionPriority = 0x01;//第一优先级数值 0x00~0x0F  数字越小 优先级越高
	ccc.NVIC_IRQChannelSubPriority = 0x01;//第二优先级数值 0x00~0x0F  数字越小 优先级越高
	ccc.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&ccc);
	
	//4、开启串口接收中断  串口1有数据过来就会跳转到中断函数运行代码
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	
	//5、开启串口
	USART_Cmd(USART1,ENABLE);
}
//处理串口1的中断函数
void USART1_IRQHandler(void)
{
	char RData=0;//定义一个用来临时存放串口接收数据的变量
	//先读取并判断串口的接收数据中断标志位有没有触发 
	if(USART_GetITStatus(USART1,USART_IT_RXNE)==1)
	{
		
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除串口1的接收中断标志位
		RData = USART1->DR;//接收串口1的一个字节数据 这个函数是库函数

		//接收完成再判断该变量  手机发过来不同的数字 小车有不同的动作
		if(RData == '0')
			Car_Stop();//小车停止
		else if(RData == '1')
			Car_Forward();//小车前进
		else if(RData == '2')
			Car_Back();//小车后退
		else if(RData == '3')
			Car_Left();//小车前左转
		else if(RData == '4')
			Car_Right();//小车前右转
	}
}

六、小车超声波避障

1、舵机转向位置控制

  1. 原理
 利用计时器,根据不同的值给予不同时间的高电平,使其转动不同的方向。
 1、该舵机要求提供的PWM周期为20ms
 2、舵机控制角度为0~180° 0.5ms~2.5ms的高电平周期对应的转动角度是0~180°
 3、调整其20ms pwm周期内的高电平比例可控制其输出角度
  1. 代码步骤
1.使能时钟  GPIOB1   AFIO  TIM3
2.GPIO PB1初始化
3.基本定时器TIM3初始化(分频系数  计数模式  自动重装载值  分频因子)
4.定时器TIM3输出通道4的初始化
5.开启定时器3计数器--开始工作计数
  1. 代码实现

(舵机初始化)

void steeringEngine_Init(void)
{
	//1、使能时钟  GPIOB1   AFIO  TIM3
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	//2、GPIO PB1初始化
	GPIO_InitTypeDef  bbb;
	bbb.GPIO_Pin  = GPIO_Pin_1;//TX
	bbb.GPIO_Speed  = GPIO_Speed_50MHz;
	bbb.GPIO_Mode   = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_Init(GPIOB,&bbb);
	
	
	//3、基本定时器TIM3初始化(分频系数  计数模式  自动重装载值  分频因子)
	TIM_TimeBaseInitTypeDef ccc;
	ccc.TIM_Prescaler = 72-1;//72Mhz/72 = 1Mhz  1个脉冲对应1us
	ccc.TIM_CounterMode = TIM_CounterMode_Up;//递增计数模式
	ccc.TIM_Period      = 20000-1;//ARR 20000  自动重装载值  刚好周期为20ms
	ccc.TIM_ClockDivision = TIM_CKD_DIV1;//上面的预分频系数够了  这里设置为1分频即不分频
	TIM_TimeBaseInit(TIM3,&ccc);
	
	//4、定时器TIM3输出通道4的初始化
	TIM_OCInitTypeDef ddd;
	ddd.TIM_OCMode = TIM_OCMode_PWM1;//PWM模式1---递增模式下  CNT<CCR时输出有效电平   CNT>CCR时输出另一种电平
	ddd.TIM_OCPolarity = TIM_OCPolarity_High;//极性高----高电平有效  配合上面的模式,即左边输出高 右边输出低
	ddd.TIM_Pulse      = 500;// 比较值  CCR500*1us = 500us = 0.5ms高电平  默认舵机转到0°位置
	ddd.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OC4Init(TIM3,&ddd);//注意这里是OC4---对应通道4

	//5、开启定时器3计数器--开始工作计数
	TIM_Cmd(TIM3,ENABLE);
}

(舵机位置赋值)

TIM3->CCR4 = 500;//0*
TIM3->CCR4 = 1000;//45*
TIM3->CCR4 = 1500;//90*
TIM3->CCR4 = 2000;//135*
TIM3->CCR4 = 2500;//180*

2、超声波模块驱动与测距

  1. 原理分析
    在这里插入图片描述
    在这里插入图片描述
  2. 代码步骤
//超声波模块初始化
1.启动使能时钟 GPIOB GPIOA TIM2
2.特定的引脚初始化——PB11 PB12
3.定时器初始化——TIM2
4.先关闭定时器

//超声波测距
1.先控制TRIG引脚输出一个至少10us高电平  启动超声波模块
2.等待ECHO引脚被超声波模块自己拉高 在拉高的瞬间我们开始计时即可
3.高电平开始计时
4.跳出来时  count是多少就代表高电平持续了多少us

//超声波避障
while(1){
1.小车距离障碍物大于30 前进
2.小车距离障碍物小于30 停止
3.当小车距离障碍物小于40  一边后退一边查找方向
}

//查找方向
1.右转45?
2.右转90?
3.左转45?
4.左转90?
5.继续后退
  1. 代码实现
    (超声波模块初始化)
void HCSR_Init(void)
{
	//1、使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	//2、配置PB11--TRIG 为输出模式
	GPIO_InitTypeDef  aaa;//定义一个用来初始化GPIO的结构体
	aaa.GPIO_Pin   = GPIO_Pin_11;
	aaa.GPIO_Speed = GPIO_Speed_50MHz;
	aaa.GPIO_Mode  = GPIO_Mode_Out_PP;//普通推挽输出
	GPIO_Init(GPIOB,&aaa);

	//3、配置PB12--ECHO 为输入模式
	aaa.GPIO_Pin   = GPIO_Pin_12;
	aaa.GPIO_Speed = GPIO_Speed_50MHz;
	aaa.GPIO_Mode  = GPIO_Mode_IPU;//上拉输入模式(自行百度了解上拉)
	GPIO_Init(GPIOB,&aaa);
	
	//4、测距时计时用的定时器的配置 TIM3  TIM4 已经用了  TIM2
	TIM_TimeBaseInitTypeDef ccc;//初始化定时器用的结构体
	ccc.TIM_Prescaler = 72-1;//72Mhz/72 = 1Mhz  1个脉冲对应1us
	ccc.TIM_CounterMode = TIM_CounterMode_Up;//递增计数模式
	ccc.TIM_Period      = 65536-1;//ARR 20000  16Bit定时器 自动重装载值  刚好周期为20ms
	ccc.TIM_ClockDivision = TIM_CKD_DIV1;//上面的预分频系数够了  这里设置为1分频即不分频
	TIM_TimeBaseInit(TIM2,&ccc);
	
	//5、先关闭定时器TIM2
	TIM_Cmd(TIM2,DISABLE);
}

(超声波测距)

//获取超声波测距的函数--单位厘米
float HCSR_GetDistance(void)
{
	//1、先控制TRIG引脚输出一个至少10us高电平  启动超声波模块
	GPIO_SetBits(GPIOB,GPIO_Pin_11);
	delay_us(15);
	GPIO_ResetBits(GPIOB,GPIO_Pin_11);

	//2、等待ECHO引脚被超声波模块自己拉高 在拉高的瞬间我们开始计时即可
	while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12) == 0);
	//开启定时器2让TIM2的计数器开始计数 并且在开启计数前  清空计数值
	TIM2->CNT = 0;//设置计数器的计数值初值为0
	TIM_Cmd(TIM2,ENABLE);
	
	//3、高电平开始计时
	while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12) == 1);
	//ECHO高电平结束  结束计数
	TIM_Cmd(TIM2,DISABLE);
	
	//4、跳出来时  count是多少就代表高电平持续了多少us
	//可以开始计算距离  厘米单位
	return 0.017*(TIM2->CNT); //0.017根据公式计算而得
}

(超声波避障实现)

void turn_to_find(){
	while(1){
		TIM3->CCR4 = 1000;//right 45*
		if(HCSR_GetDistance()>30.0){//大于30  小车右转45度
			Car_Speed_Init(1999,0,0,0);
			delay_ms(300);
			break;
		}
		TIM3->CCR4 = 500;//right 90*
		if(HCSR_GetDistance()>30.0){//大于30  小车右转90度
			Car_Speed_Init(1999,0,0,0)
			delay_ms(600);
			break;
		}
		TIM3->CCR4 = 2000;//left 45*
		if(HCSR_GetDistance()>30.0){//大于30  小车左转45度
			Car_Speed_Init(0,0,1999,0)
			delay_ms(300);
			break;
		}
		TIM3->CCR4 = 2500;//left 90*
		if(HCSR_GetDistance()>30.0){//大于30  小车左转90度
			Car_Speed_Init(0,0,1999,0)
			delay_ms(600);
			break;
		}
		//都找不到 小车继续后退
		Car_Speed_Init(0,1999,0,1999);
		delay_ms(100);
		break;
	}
	TIM3->CCR4 = 1500;//舵机摆正
};

//main函数
TIM3->CCR4 = 1500;//舵机摆正
while(1)
{
//1. 大于30 小车前进
Car_Speed_Init(1800,0,1800,0);
while(HCSR_GetDistance()>30.0) delay_ms(500);

//2. 小于30 小车停止
Car_Speed_Init(0,0,0,0);

//3. 小于40 小车一边后退一边找方向
while(HCSR_GetDistance()<40.0)
{Car_Back(0,1800,0,1800);delay_ms(500);turn_to_find();}
}

七、小车红外避障(结合超声波避障)

1、原理分析

通过判断红外线的返回信号,判断左右两边是否有障碍物。

2、代码步骤

//红外线避障模块初始化
1.使能时钟
2.特定引脚初始化

//红外避障+超声波避障
1. 大于30且两边无障碍 小车前进
2. 大于30但左边有障碍 小车在右边45 90找方向
3. 大于30但右边有障碍 小车在左边45 90找方向
4. 小于30 小车一边后退一边查找方向

3、代码实现
(红外线避障模块初始化)

void hongwai_init(void){
	//1、使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//2、初始化端口红外线端口 PB10 PA11
	GPIO_InitTypeDef  aaa;//定义一个用来初始化GPIO的结构体
	aaa.GPIO_Pin   = GPIO_Pin_10;
	aaa.GPIO_Speed = GPIO_Speed_50MHz;
	aaa.GPIO_Mode  = GPIO_Mode_IPU;//上拉输入模式
	GPIO_Init(GPIOB,&aaa);
	bbb.GPIO_Pin   = GPIO_Pin_11;
	bbb.GPIO_Speed = GPIO_Speed_50MHz;
	bbb.GPIO_Mode  = GPIO_Mode_IPU;//上拉输入模式
	GPIO_Init(GPIOA,&aaa);
}

//GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)==0 为左边有障碍
//GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11)==0 为右边有障碍

(红外避障+超声波避障)

TIM3->CCR4 = 1500;//舵机摆正
while(1)
{
//1. 大于30且两边无障碍 小车前进
Car_Speed_Init(1800,0,1800,0);
while(HCSR_GetDistance()>30.0&&GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11)==1&&GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10)==1) 
delay_ms(500);

//2. 大于30但左边有障碍而右边无障碍 小车在右边45 90找方向
if(HCSR_GetDistance()>30.0&&GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11)==1&&GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10)==0)
	turn_right_to_find();
	
//3. 大于30但右边有障碍而左边无障碍 小车在左边45 90找方向
if(HCSR_GetDistance()>30.0&&GGPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11)==0&&GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10)==1)
	turn_left_to_find();

//4. 小于30 小车一边后退一边查找方向
if(HCSR_GetDistance()<30.0){
	while(HCSR_GetDistance()<40.0)
	{Car_Back(0,1800,0,1800);delay_ms(500);turn_to_find();}
}
}

八、小车沿白线循迹

1、原理分析

1.使用4路红外循迹返回的信号,完成小车沿线循迹功能的实现
2.车头4路循迹模块对应的芯片引脚为(从左到右)分别为PA12,PB13,PB14,PB15
3.端口遇到白色信号为0,未遇到信号未1

2、代码实现

(循迹模块初始化)

void xunji_init(void)
{
	//1、使能时钟  GPIOB GPIOA
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
	
	//2、GPIO PB13 PB14 PB15初始化
	GPIO_InitTypeDef eee;	
	eee.GPIO_Pin =  GPIO_Pin_13 | GPIO_Pin_14  | GPIO_Pin_15;
	eee.GPIO_Mode = GPIO_Mode_IPU;//配置GPIO模式,输入上拉       
	eee.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &eee);

	//3、GPIO PA12 初始化
	eee.GPIO_Pin =  GPIO_Pin_12;
	eee.GPIO_Mode = GPIO_Mode_IPU;//配置GPIO模式,输入上拉       
	eee.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &eee);
};

(获取循迹模块的信号)

//从左到右读入四个端口的信号
uint8_t read_xunji_data(void){
	uint16_t buff = 0; 
	buff = (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12)<<3)+
			(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13)<<2)+
			(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14)<<1)+
			(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15)<<0);
	return buff;
};

(实现小车沿白线循迹——如果沿黑线只需要调换01)

//main()
while(1){
	//1.白线在左车头
	if(data==0111||data==1011||data==0011||data==0001){
		Car_Speed_Init(1800,0,0,0);
		delay_ms(500);
	}
	//2.白线在右车头
	else if(data==1110||data==1101||data==1100||data==1000){
		Car_Speed_Init(0,0,1800,0);
		delay_ms(500);
	}
	//3.其他情况直行
	else{
		Car_Speed_Init(1800,0,1800,0);
		delay_ms(500);
	}
}

九、实验总结——嵌入式开发的流程步骤

  1. 了解硬件背后原理,信号状态代表了现实情况的什么信息。
  2. 根据系统的库函数,对特定使用的模块进行初始化。
  3. 巧妙利用计时器,实现电平不同时间的输出来操作硬件,以及获取关键信息。
  4. 根据基础的功能实现,自己按照一定的逻辑,实现智能小车的避障等功能。

十、小车实物图介绍

在这里插入图片描述

版权声明:本文为CSDN博主「Your_Julia」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Dedication_/article/details/118361553

生成海报
点赞 0

Your_Julia

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

基于Stm32的WiFi多功能LED

(限于本人水平,此项目中中还存在不足,欢迎大家指正探讨) 多功能LED设计,拥有自动和手动两种模式 1)自动模式:可以通过人体红外传感器检测是否

工程师教你如何学习单片机

1、万事开头难、要勇敢迈出第一步。 开始的时候,不要老是给自己找借口,说KEIL不会建项目啦、没有实验板啦之类的。遇到困难要一件件攻克,不会建项目,就先学它,这方面网上教程很多,随便找找看一下,做几次就懂了。 然后可以参考别的人程序,抄过来也无所