文章目录[隐藏]
一、设计内容
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、原理分析
- 双路电机驱动对应的接口为
M1+ M1- M2+ M2-
- 通过上面的
芯片引脚端口图
,找到对应的芯片引脚为GPIOB组的Pin_6 Pin7 Pin8 Pin9
- 可以看到M1+ M1-是控制同条电路,而M2+ M2-是控制另外一条电路
- 也就是说,
小车左边前后轮为一组控制,小车右边前后轮为一组控制
- M1+对应的是右前轮,M1-对应的是右后轮,而M2则为左边
- 当M1+与M1-输出相同电平,都无法唯一确定电流的去向,表现为右轮不动
- 当M1+与M1-输出相反电平,
若M1+ M1-为10,则右边前进,
而01则为右边后退
2、代码编写步骤
- 开启使能时钟
- GPIO结构体初始化
端口配置->速度配置->模式选择
- 引脚高低电平设置
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、代码编写步骤
- 开启GPIOB、AFIO、TIM4的使能时钟
- 初始化GPIO
- 初始化基本定时器
- 利用定时器实现对应通道的电平输出控制
- 开启定时器
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、蓝牙连接电脑
- 按照小车底板的排座接口接好蓝牙
- 将板子的USB直连 同时用USB线连到电脑 即蓝牙直接和电脑相连
- 电脑端运行串口助手软件 开始使用AT指令调试蓝牙
- 运行XCOM串口助手 连接上蓝牙
- 拔下蓝牙 按住蓝牙上的小按键 不松开再插回去 蓝牙慢闪
- 通过发送文本框输入
a) AT回车 蓝牙会反馈OK
b) AT+NAME? 回车 蓝牙会反馈自己的名称
c) AT+NAME=你设置的新蓝牙名字 回车 设置新的蓝牙名称
d) AT+UART? 回车 查询蓝牙透传模式下的通信波特率
e) AT+UART=9600,0,0 回车 设置蓝牙波特率为9600 停止位1 无校验位
3、蓝牙连接手机
- 跳线帽短接STM32和蓝牙
- 下载<蓝牙串口>app
- 输入<1234>实现手机与蓝牙的连接
4、蓝牙遥控小车
代码编写步骤
- 开启使能 GPIOA USART1 AFIO 时钟
- 配置USART1
USART_Init()
- 配置GPIOA的 PA9 PA10——对应蓝牙端口的引脚
- 开启串口接收中断
USART_ITConfig()
- 开启串口
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、该舵机要求提供的PWM周期为20ms
2、舵机控制角度为0~180° 0.5ms~2.5ms的高电平周期对应的转动角度是0~180°
3、调整其20ms pwm周期内的高电平比例可控制其输出角度
- 代码步骤
1.使能时钟 GPIOB1 AFIO TIM3
2.GPIO PB1初始化
3.基本定时器TIM3初始化(分频系数 计数模式 自动重装载值 分频因子)
4.定时器TIM3输出通道4的初始化
5.开启定时器3计数器--开始工作计数
- 代码实现
(舵机初始化)
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.启动使能时钟 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.继续后退
- 代码实现
(超声波模块初始化)
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);
}
}
九、实验总结——嵌入式开发的流程步骤
- 了解硬件背后原理,信号状态代表了现实情况的什么信息。
- 根据系统的库函数,对特定使用的模块进行初始化。
- 巧妙利用计时器,实现电平不同时间的输出来操作硬件,以及获取关键信息。
- 根据基础的功能实现,自己按照一定的逻辑,实现智能小车的避障等功能。
十、小车实物图介绍
版权声明:本文为CSDN博主「Your_Julia」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Dedication_/article/details/118361553
暂无评论