【32单片机篇】智能小车

本文介绍了如何通过SU-03语音识别模块控制具有循迹、跟随和避障功能的智能小车,详细阐述了硬件设备如电机驱动、传感器、以及OLED显示屏的使用,并展示了相应的软件编程实现,包括主函数和各个模式控制函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、项目概述

        该智能小车具有循迹、跟随、避障三种模式,可以通过SU-03语音识别模块实现三种模式的切换,并且在oled显示屏显示当前模式。在循迹模式下,小车可以通过循迹模块跟随赛道中的黑线位置实时改变方向,并沿赛道前行;在跟随模式下,小车可以通过跟随模块实时跟随前方物体而移动;在避障模式下,小车利用超声波模块检测前方距离,并根据距离判断障碍物位置,并及时躲避前方障碍。

二、硬件部分

1.小车底盘套件

2.32开发板

3.电池盒(4节)

4.面包板

5.L9110s电机驱动模块

6.循迹模块(2个)

7.红外跟随模块(2个)

8.SU-03语音模块

9.OLED显示屏

10.超声波

11.sg-90舵机

12.杜邦线若干

详细硬件模块介绍见【51单片机篇】智能小车博文 

•硬件接线 

•电机驱动模块 
B-1A -- PB1       B-2A -- PB0       A-1A -- PB10        A-1B -- PB2
•循迹小车
循迹模块(左) -- PB3         循迹模块(右) -- PB4
•跟随小车
跟随模块(左) -- PA8          跟随模块(右) -- PA9
•避障小车
sg90:PB9         Trig:PA10         Echo:PA11
•OLED模块
SCL -- PB6       SDA -- PB7
•语音模块
A25 -- PA15(循迹)     A26 -- PA13(跟随)       A27 -- PA14(避障)

三、软件部分

main.c

#define MIDDLE 0
#define LEFT 1
#define RIGHT 2

#define BZ 1
#define XJ 2
#define GS 3
#define ST 4

#define LeftWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
#define RightWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)

#define LeftWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)
#define RightWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)

#define A25 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15)
#define A26 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_13)
#define A27 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_14)

char dir;

void xunjiMode()
{
	if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_RESET)
		goForward();
	if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_RESET)
		goLeft();
	if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_SET)
		goRight();
	if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_SET)
		stop();
}

void gensuiMode()
{
	if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_RESET)
		goForward();
	if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_RESET)
		goRight();
	if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_SET)
		goLeft();
	if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_SET)
		stop();
}

void bizhangMode()
{
	double disMiddle;
	double disLeft;
	double disRight;

	if(dir != MIDDLE){
		sgMiddle();
		dir = MIDDLE;
		HAL_Delay(450);
	}
	disMiddle = get_distance();
	
	if(disMiddle > 35){
		//前进
		goForward();
	}else if(disMiddle < 10){
		goBack();
		
	}else
	{
		//停止
		stop();
		//测左边距离
		sgLeft();
		HAL_Delay(450);
		disLeft = get_distance();
		
		sgMiddle();
		HAL_Delay(450);
		
		sgRight();
		dir = RIGHT;
		HAL_Delay(450);
		disRight = get_distance();
		
		if(disLeft < disRight){
			goRight();
			HAL_Delay(150);
			stop();
		}
		if(disRight < disLeft){
			goLeft();
			HAL_Delay(150);
			stop();
		}
	}
	HAL_Delay(50);
}
	
main函数里:
  initSG90();
  HAL_Delay(1000);
  dir = MIDDLE;
  Oled_Init();
  Oled_Screen_Clear();
  Oled_Show_Str(2,2,"-----Ready----");
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		//满足寻迹模式的条件
		if(A25 == 0 && A26 == 1 && A27 == 1){
			if(mark != XJ){
				Oled_Screen_Clear();
				Oled_Show_Str(2,2,"-----XunJi----");
			}
			mark = XJ;
			xunjiMode();
		}
		//满足跟随模式的条件
		if(A25 == 1 && A26 == 0 && A27 == 1){
			if(mark != GS){
				Oled_Screen_Clear();
				Oled_Show_Str(2,2,"-----GenSui----");
			}
			mark = GS;
			gensuiMode();
		}
		//满足避障模式的条件
		if(A25 == 1 && A26 == 1 && A27 == 0){
			if(mark != BZ){
				Oled_Screen_Clear();
				Oled_Show_Str(2,2,"-----BiZhang----");
			}
			mark = BZ;
			bizhangMode();
		}
		//停下
		if(A25 == 0 && A26 == 0 && A27 == 0){
			if(mark != ST){
				Oled_Screen_Clear();
				Oled_Show_Str(2,2,"----Stop----");
			}
			mark = ST;
			stop();
		}
  }
  /* USER CODE END 3 */
}

motor.c

#include "motor.h"
#include "gpio.h"

void goForward(void)
{
	//左轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_RESET);
	//右轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
}
void goBack(void)
{
	//左轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_SET);
	//右轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
}
void goLeft(void)
{
	//左轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_RESET);
	//右轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
}
void goRight(void)
{
	//左轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_RESET);
	//右轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
}
void stop(void)
{
	//左轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_RESET);
	//右轮
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
}

sr04.c(超声波)

#include "sr04.h"
#include "gpio.h"
#include "tim.h"

void TIM2_Delay_us(uint16_t n_us)
{
    /* 使能定时器2计数 */
    __HAL_TIM_ENABLE(&htim2);
    __HAL_TIM_SetCounter(&htim2, 0);
    while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );
    /* 关闭定时器2计数 */
    __HAL_TIM_DISABLE(&htim2);
}

double get_distance(void)
{
		int cnt=0;
		//1. Trig ,给Trig端口至少10us的高电平
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);//拉高
		TIM2_Delay_us(20);
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);//拉低

		//2. echo由低电平跳转到高电平,表示开始发送波
		//波发出去的那一下,开始启动定时器
		while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == GPIO_PIN_RESET);//等待输入电平拉高
		HAL_TIM_Base_Start(&htim2);
		__HAL_TIM_SetCounter(&htim2,0);

		//3. 由高电平跳转回低电平,表示波回来了
		while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == GPIO_PIN_SET);//等待输入电平变低
		//波回来的那一下,我们开始停止定时器
		HAL_TIM_Base_Stop(&htim2);

		//4. 计算出中间经过多少时间
		cnt = __HAL_TIM_GetCounter(&htim2);

		//5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
		return (cnt*340/2*0.000001*100); //单位:cm
}

sg90.c(舵机)

#include "sg90.h"
#include "gpio.h"
#include "tim.h"

void initSG90(void)
{
	HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4); //启动定时器4
	__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为0度
}

void sgMiddle(void)
{
	__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
}

void sgRight(void)
{
	__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
}

void sgLeft(void)
{
	__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 25); //将舵机置为180度
}

 oled.c

#include "oled.h"
#include "i2c.h"
#include "oledfont.h"

void Oled_Write_Cmd(uint8_t dataCmd)
{
	
	HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
										&dataCmd, 1, 0xff);
}

void Oled_Write_Data(uint8_t dataData)
{
	HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
										&dataData, 1, 0xff);
}

void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address  
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128   
	Oled_Write_Cmd(0xA1);//set segment remap 
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	
	Oled_Write_Cmd(0xAF);//--turn on oled panel		
}

void Oled_Screen_Clear(void){
	char i,n;
	Oled_Write_Cmd (0x20);                    //set memory addressing mode
	Oled_Write_Cmd (0x02);                    //page addressing mode

	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xb0+i);               //éè??ò3μ??·£¨0~7£?
		Oled_Write_Cmd(0x00);                 //éè????ê??????aáDμíμ??·
		Oled_Write_Cmd(0x10);                 //éè????ê??????aáD??μ??·   
		for(n=0;n<128;n++)Oled_Write_Data(0x00); 			
	}	
}

void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
	unsigned int  i;
	Oled_Write_Cmd(0xb0+(row*2-2));                           //page 0
	Oled_Write_Cmd(0x00+(col&0x0f));                          //low
	Oled_Write_Cmd(0x10+(col>>4));                            //high	
	for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
		Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
	}

	Oled_Write_Cmd(0xb0+(row*2-1));                           //page 1
	Oled_Write_Cmd(0x00+(col&0x0f));                          //low
	Oled_Write_Cmd(0x10+(col>>4));                            //high
	for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
		Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
	}		
}


/******************************************************************************/
// 函数名称:Oled_Show_Char 
// 输入参数:oledChar 
// 输出参数:无 
// 函数功能:OLED显示单个字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){
	while(*str!=0){
		Oled_Show_Char(row,col,*str);
		str++;
		col += 8;	
	}		
}

代码太多,在这不一 一展示,有需要的去看源码资源。 

https://gitee.com/GeekerGao/stm32-intelligent-car

四.项目实现

语音控制智能小车

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿gao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值