2022电赛小车我认为有主要是几个主要的问题,我将分这几个部分来讲解
2022电赛,双车稳定行驶_哔哩哔哩_bilibili
一、循迹
循迹我们组用的是五路灰度,灰度跟红外对管的作用是差不多的,在淘宝上就能买到,检测到黑线就输入高电平,那么就需要在单片机上捕获到这个IO口的电平就可以知道哪个灰度检测到了黑线。
步骤很简单 初始化IO口->捕获IO口 代码如下:
#ifndef __GRAY_H_
#define __GRAY_H_
//主函数定义
#include "sys.h"
void GRAY_Init(void);
int Gray_values(void);
#endif
#include "gray.h"
void GRAY_Init(void)//初始化IO口
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA,PD端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化
}
u8 Gray_values()//得到IO口的值并返回
{
static u8 Gray_Value;
Gray_Value=GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4);//获得GPIO的值
return Gray_Value;
}
可以根据自己的需求进行IO口的配置,最后用一个数组来放这几个IO值。
二、蓝牙通信,双车数据传输
首先配对两个小车的蓝牙,将一个蓝牙设置为主一个为从机模式,具体配对方法点击这里。然后就是蓝牙初始化了。初始化比较简单到处都能找得到源码。
#ifndef __USART2_H
#define __USART2_H
#include "sys.h"
#define USART2_MAX_RECV_LEN 37 //最大接收缓存字节数
#define USART2_MAX_SEND_LEN 3 //最大发送缓存字节数
#define USART2_RX_EN 1 //0,不接收;1,接收.
#define DATA1 12550
#define DATA2 12806
#define DATA3 13062
extern u8 USART2_RX_BUF[USART2_MAX_RECV_LEN]; //接收缓冲,最大USART3_MAX_RECV_LEN字节
extern u8 USART2_TX_BUF[USART2_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节
extern vu16 USART2_RX_STA; //接收数据状态
void dataSend(u8 *sendArry);
void usart2_init(u32 bound); //串口2初始化
void u2_printf(char* fmt, ...);
#endif
#include "delay.h"
#include "usart2.h"
#include "stdarg.h"
#include "usart.h"
#include "timer.h"
#include "stdio.h"
#include "string.h"
//串口接收缓存区
u8 USART2_RX_BUF[USART2_MAX_RECV_LEN]; //接收缓冲,最大USART3_MAX_RECV_LEN个字节.
u8 USART2_TX_BUF [USART2_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节
u16 uart2rec;
u8 END_FLAG1;
u8 RECEIVE_FLAG = 0;
//初始化IO 串口3
//pclk1:PCLK1时钟频率(Mhz)
//bound:波特率
void usart2_init(u32 bound)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //串口3时钟使能
USART_DeInit(USART2); //复位串口3
//USART2_TX PA2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PB10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PB10
//USART2_RX PA3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PB11
//设置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART2, &USART_InitStructure); //初始化串口
//使能接收中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断
USART_Cmd(USART2, ENABLE); //使能串口
}
void dataSend(u8 *sendArry) {//发送数据函数,注意这里发的是一个数组,3位数组,第一个与第三个用于判断
int i;
for(i=0;i<3;i++)
{
USART_SendData(USART2,sendArry[i]);
while((USART2->SR&0x40)==0);
}
}
void USART2_IRQHandler(void)//接收数据中断
{
static u8 i = 0;
u8 res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
res = USART_ReceiveData(USART2);
if(res == 0x01 )//帧头判断
{
i = 0;
}
USART2_RX_BUF[i] = res;//将接收到的数据放在数组中
i++;
if(i == 2 && USART2_RX_BUF[2] == 0x06)//帧尾判断
{
uart2rec = (u16)((u16)USART2_RX_BUF[1]<<8 | (u16)USART2_RX_BUF[2]);//将接收到的数据进行移位操作
}
}
}
//串口2,printf 函数
//确保一次发送数据不超过USART3_MAX_SEND_LEN字节
void u2_printf(char* fmt, ...)
{
u16 i, j;
va_list ap;
va_start(ap, fmt);
vsprintf((char*)USART2_TX_BUF, fmt, ap);
va_end(ap);
i = strlen((const char*)USART2_TX_BUF); //此次发送数据的长度
for(j = 0; j < i; j++) //循环发送数据
{
USART_SendData(USART2, USART2_TX_BUF[j]);
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); //循环发送,直到发送完毕
}
}
主要是用好发送函数和接收中断,移位操作是将后两位16进制转换为10进制,
列:发送数组data[3]={0x01,0x31,0x06} 则0x31,0x06->12550 uart2rec=12550
通过改变中间的值改变uartrec的值,判断走哪个问。
三、起始路口的识别
根据灰度的的亮灭则可判断,经过停车位置时,中间三个灯灭,则让其做出指令。
四、分叉路口的识别
在这里有很多种方法,我们用的方法是根据起始的三个灯灭来判断,如果三个灯灭了,若走外圈则利用延时向前冲一段,还有可以利用最外边的灰度进行判断。说起来很抽象,还是直接上代码吧。
五、源码
用了很多标志位导致阅读感很不舒服。大家见谅,主要这代码是一个月前的了我也不想再去改了,主要给大家提供一个思路,欢迎评论区讨论学习。
链接: https://pan.baidu.com/s/1X8O7z_afGnofAl8y89iRAQ?pwd=1234 提取码: 1234
#include "test.h"
#include "control.h"
#include "gray.h"
#include "tb6612.h"
#include "led.h"
#include "usart2.h"
#include "test.h"
#include "encoder.h"
u8 TRACE_FLAG = 1,STOP_FLAG ,START_FLAG = 1;
u8 send_data5[3] = {0x01,0x35,0x06};//13574
u8 send_data6[3] = {0x01,0x36,0x06};//13574
u8 Instruction_Flag;
int cross_num;
void test1()
{
Basic_Speed = 2600; //2400 0.3
Gray_values();
Encoder4();
if(START_FLAG)
{
trace(Basic_Speed);
if(TURN_FLAG == 1)
{
Direction(10,10,0,1); //0 外圈
}
if(Gray_Number >= 3 && Gray_Number < 5 && TURN_FLAG == 0)
{
TURN_FLAG = 1;
Qingling = 0;
}
if(cross_num == 2)
{
START_FLAG = 0;
END_FLAG = 1;
}
}
//停止行进标志位
if(END_FLAG == 1)
{
LED0 = 1;
STOP;
dataSend(send_data5);
if(delay(40))
{
LED0 = 0;
END_FLAG = 0;
START_FLAG = 0;
TRACE_FLAG = 0;
}
}
}
void test2()
{
Basic_Speed = 3300; //3100 0.5
Gray_values();
Encoder4();
if(START_FLAG)
{
trace(Basic_Speed);
if(TURN_FLAG == 1)
{
Direction(10,10,0,1);
}
if(Gray_Number >= 3 && Gray_Number < 5 && TURN_FLAG == 0 )
{
TURN_FLAG = 1;
Qingling = 0;
}
if(cross_num == 3)
{
START_FLAG = 0;
END_FLAG = 1;
}
}
if(END_FLAG == 1)/结束
{
dataSend(send_data5);
LED0 = 1;
BACKWORD;
PWM_L = 0;
PWM_R = 0;
if(delay(30))
{
START_FLAG = 0;
LED0 = 0;
END_FLAG = 0;
DELAY_FLAG = 1;
}
}
}
void test3()
{
Basic_Speed = 2600;
Gray_values();
Encoder4();
if(START_FLAG)
{
trace(Basic_Speed);
if(TURN_FLAG == 1)
{
switch(cross_num)
{
case 0:
Direction(10,10,0,1); break;
case 1:
Direction(10,10,0,1); break;
case 2:
Direction(10,10,0,1); break;
case 3:
Direction(10,10,1,1);
LED1 = 1; break;
case 4:
Direction(10,10,1,1); break;
}
}
if(Gray_Number >= 3 && Gray_Number < 5 && TURN_FLAG == 0)
{
TURN_FLAG = 1;
Qingling = 0;
}
if(cross_num == 4)
{
START_FLAG = 0;
END_FLAG = 1;
}
}
if(END_FLAG == 1)/结束
{
dataSend(send_data5);
LED0 = 1;
BACKWORD;
PWM_L = 0;
PWM_R = 0;
if(delay(30))
{
START_FLAG = 0;
LED0 = 0;
END_FLAG = 0;
DELAY_FLAG = 0;
}
}
}
void test4()
{
Basic_Speed = 4000;
Gray_values();
Encoder4();
if(START_FLAG)
{
if(Instruction_Flag == 0 && Gray_Number >= 3 && TURN_FLAG == 0)
{
TURN_FLAG = 1;
}
if(TURN_FLAG == 1)
{
if(delay(20))
{
Instruction_Flag = 1;
TURN_FLAG = 2;
DELAY_FLAG = 0;
}
}
if(Instruction_Flag == 1 && TURN_FLAG == 2 && Gray_Number >= 3)
{
TURN_FLAG = 3;
}
if(TURN_FLAG == 3)
{
BACKWORD;
PWM_L = 0;
PWM_R = 0;
Instruction_Flag = 2;
if(delay(150))
{
TURN_FLAG = 4;
Instruction_Flag = 3;
FORWARD;
DELAY_FLAG = 0;
TIM_SetCounter(TIM2,0);
}
}
if(Instruction_Flag == 3 && Gray_Number >= 3 && Read_Encoder(2) > 1000)
{
START_FLAG = 0;
END_FLAG = 1;
}
if(Instruction_Flag != 2)
{
trace(Basic_Speed);
}
}
if(END_FLAG)
{
START_FLAG = 0;
BACKWORD;
PWM_L = 0;
PWM_R = 0;
LED0 = 1;
dataSend(send_data5);
if(delay(50))
{
LED0 = 0;
END_FLAG = 0;
START_FLAG = 0;
TRACE_FLAG = 0;
DELAY_FLAG = 0;
}
}
}
版权归原作者 点灯大侠 所有, 如有侵权,请联系我们删除。