相信你已经学完了江科大STM32的入门教程了吧,如果不知道接下来做什么,那就跟着我做一个简单项目来加深印象吧,聪明的你指定是一看就会,一做就对;希望本片文章能对你在学习嵌入式的道路上有所帮助。如果有写的不对的地方请大家多多包涵并指出,如果有跟好的方案也可以私信我,我会及时采纳更新。

一. 小车功能

1.避障

2.循迹

3.蓝牙控制

二. 智能小车所需硬件

1.STM32F103C8T6芯片一枚

2.TB6612电机驱动一到两个

3.SG90舵机一个

4.HC-SR04超声波测距模块一个

5.HC-04蓝牙模块一个

6.小车框架一个(自带四个直流电机)

7.ST-LINK V2烧录器一个

8.循迹模块三个

9.导线若干

10.面包板一块(如果大家用洞洞板的话还需要准备电烙铁和万用表)

三. PWM驱动电机旋转(让小车动起来)

小车模型可在某多多上搜索“STM32智能小车框架”也是只需要几块钱[旺柴]

1. TB6612电机驱动介绍

TB6612是东芝半导体公司生产的一款直流电机驱动器件 。

结构与功能:

具有大电流MOSFET-H桥结构,双通道电路输出,可同时驱动2个直流电机。每通道输出高1A的连续驱动电流,启动峰值电流达2A/3A(连续脉冲/单脉冲)。

控制模式:

有正转、反转、制动、停止4种电机控制模式,PWM支持频率高达100kHz,还具备待机状态。

保护电路:

片内集成了低压检测电路与热停机保护电路,工作温度范围为-20℃~85℃。

主要引脚功能:

1. VM(3V~13.5V)和VCC(2.7V~5.5V)分别为电机驱动电压输入和逻辑电平输入端

2. STBY置高电平为正常模式,置低电平为待机状态

3. BO1\2,AO1\2为2路电机控制输出端接电机正负极

4. PWMA\B,BIN1\2,AIN1\2为控制信号输入端,接GPIO控制

2. 程序

PWM.c

代码有相应注释,基本上每句都有,这里就不过多解释了,不懂的地方可以私信问我

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIO时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启TIM2时钟
	
	GPIO_InitTypeDef GPIO_InitStructure;//结构体变量命名
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//设置为复用推挽
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |  GPIO_Pin_1;//右侧电机使用pin_0输出PWM波形,左测电机使用pin_1
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);//配置内部时钟为时钟源,若不配置此项,默认也是开启内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStructure);//时基单元结构体初始化
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV2;//给点滤波时钟
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100-1;     //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;  //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;//重复计数器(高级定时器)
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化时基单元
    /*
    GPIOA_Pin_0引脚对应TIM2_CH1通道
    GPIOA_Pin_1引脚对应TIM2_CH2通道
    */
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM1模式
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性选择,高电平有效
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//启用输出比较
	TIM_OCInitStructure.TIM_Pulse = 0x00;   //CCR,占空比等于CCR/ARR
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);//初始化输出比较
	
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM1模式
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性选择,高电平有效
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//启用输出比较
	TIM_OCInitStructure.TIM_Pulse = 0x00;   //CCR,占空比等于CCR/ARR
	TIM_OC2Init(TIM2,&TIM_OCInitStructure);//初始化输出比较
	
	TIM_Cmd(TIM2,ENABLE);//使能TIM2
	
}

void PWM_SetCompare1(uint16_t Speed)//设置通道一的比较值CCR
{
	TIM_SetCompare1(TIM2,Speed);
}

void PWM_SetCompare2(uint16_t Speed)//设置通道二的比较值CCR
{
	TIM_SetCompare2(TIM2,Speed);
}

 Motor.c(电机模块)

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

void Motor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;//由3 4引脚控制右侧电机,5 6引脚控制左侧电机
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	PWM_Init();
}
/*
    设置右侧电机正反转
    GPIOA_Pin_3\4引脚接TB6612 AIN1\2用于控制接在AO1\2引脚上的电机
    控制原理可以看上面发的图片
*/
void Motor_SetRightSpeed(int8_t Speed)
{
	if(Speed >= 0)
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_3);
		PWM_SetCompare1(Speed);
	}
	else 
	{
		GPIO_ResetBits(GPIOA,GPIO_Pin_4);
		GPIO_SetBits(GPIOA,GPIO_Pin_3);
		PWM_SetCompare1(- Speed);
	}
}

/*
    同理右侧电机
    GPIOA_Pin_5\6引脚接TB6612 BIN1\2用于控制接在BO1\2引脚上的电机
*/

void Motor_SetLeftSpeed(int8_t Speed)
{
	if(Speed >= 0)
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_6);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		PWM_SetCompare2(Speed);
	}
	else 
	{
		GPIO_ResetBits(GPIOA,GPIO_Pin_6);
		GPIO_SetBits(GPIOA,GPIO_Pin_5);
		PWM_SetCompare2(- Speed);
	}
}
/*
    这里前进后退转弯程序;逻辑很简单,就不过多解释了
*/

void Car_Run(void)
{
	Motor_SetLeftSpeed(50);
	Motor_SetRightSpeed(50);
}

void Car_Stop(void)
{
	Motor_SetLeftSpeed(0);
	Motor_SetRightSpeed(0);
}

void Car_RightRun(void)
{
	Motor_SetLeftSpeed(50);
	Motor_SetRightSpeed(0);
}

void Car_LiftRun(void)
{
	Motor_SetRightSpeed(50);
	Motor_SetLeftSpeed(0);
}

void Car_Break(void)
{
	Motor_SetLeftSpeed(-50);
	Motor_SetRightSpeed(50);
}

代码写完就可以在主函数进行相应测试了,如果大家用的洞洞板,最好在焊接之前测试一下电机,别到时候焊好了发现电机是坏的,别问我怎么知道的[微笑]

四. 避障功能

1. HC-SR04避障模块介绍

核心通过“发射超声波+接收反射波”计算距离

核心参数:

测距范围2cm-400cm,精度可达3mm,工作电压5V(新款支持3.3~5.5V ;不过5V测量更远,相差大概50cm),适配Arduino、STM32等主流控制器。

工作原理(4步):

1. 向模块的“Trig”引脚发送一个≥10μs的高电平信号,触发测距。
2. 模块自动发射8个40kHz超声波,并开始计时。
3. 超声波碰到障碍物反射,被模块“Echo”引脚接收,计时停止。
4. 控制器读取“Echo”引脚的高电平持续时间,按“距离=时间×340m/s÷2”(除以2是因为声波往返)计算出实际距离。

2. 程序

HC-SR04.h

上电瞬间如果显示0的话,可以取多组数据取均值。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/*
    Trgi触发引脚(发送超声波),接GPIO设置为输出;引脚发送最少10us高电平
    Echo接收引脚(接收超声波),接GPIO设置为输入;从高电平到低电平之间的时间就是测距时间
*/

uint16_t CNT_Ms;

void HCSR04_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//Trig推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//Echo接收到信号会至高电平,设置为下拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM4);
	
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period = 1000-1;//arr 
	TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;//psc 72000000预分配72得到1us;计时1000得到1ms
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStructure);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM4,DISABLE);//先关闭定时器
}

void TIM4_IRQHandler(void)//每1ms计数加一
{
	if(TIM_GetITStatus(TIM4,TIM_IT_Update) == SET)
	{
		CNT_Ms ++;
		TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
	}
}
float HCSR04_GetDistance(void)
{
	GPIO_SetBits(GPIOB,GPIO_Pin_13);//设置Trgi为高点平发送超声波
	Delay_us(20);                   //延时20us
	GPIO_ResetBits (GPIOB,GPIO_Pin_13);//设置为低电平
	
	while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12) == RESET);//设置Echo为低电平
	TIM_Cmd(TIM4,ENABLE);                                    //开启定时器
	while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12) == SET);  //等待接收超声波信号
	TIM_Cmd(TIM4,DISABLE);                                   //关闭定时器
	
	uint16_t CNT_Us = TIM_GetCounter(TIM4);      //取出计数器为满1ms的us数       
	uint16_t CNT_All = CNT_Ms * 1000 + CNT_Us;//计的毫秒数加上微秒数为总时间
	float distance = (float)CNT_All / 58.0;//微秒除58得到厘米
	TIM_SetCounter(TIM4,0);//计算完别忘了清空计数器和CNT_Ms
	CNT_Ms = 0;
    float Distance = 0;
    for(uint8_t i = 0;i < 3;i ++)
    {
        Distance += distance;
    }
	Delay_ms(60);
	
	return Distance / 3;
}

Servo.h(舵机模块)

舵机时序图(我们计时定时器20ms,设置CCR0.5ms舵机角度-90°以此类推)

#include "stm32f10x.h"                  // Device header

void Servo_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIO和TIM3时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;//结构体变量命名
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//输出比较通道默认设置为复用推挽
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//初始化Pin_7引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化GPIO
	
	TIM_InternalClockConfig(TIM3);//使用内部时钟源
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStructure);//时基单元结构体初始化
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 20000-1;     //ARR,自动重装载计数器
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;  //PSC,预分频器
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化时基单元
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM1模式
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//极性选择,高电平有效
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//启用输出比较
	TIM_OCInitStructure.TIM_Pulse = 0x00;   //CCR,占空比等于CCR/ARR
	TIM_OC2Init(TIM3,&TIM_OCInitStructure);//初始化输出比较
	
	TIM_Cmd(TIM3,ENABLE);
}

/*
    占空比Duty = CCR/(ARR+1)
    计时20ms
    角度Angle = 0;CCR等于500为-90°(也可以理解为0°,看你怎么摆了)
*/

void Servo_SetAngle(float Angle)//计算角度
{
	TIM_SetCompare2(TIM3,Angle / 180 * 2000 + 500);
}

五. 蓝牙控制

1. HC-04模块介绍

1. HC-04是双模蓝牙串口透传模块,支持SPP(经典蓝牙2.1)+ BLE(低功耗蓝牙4.0)双协议

2. 串口通讯参数

接口:UART(3.3V TTL电平)

波特率:支持1200~921600bps(默认9600bps)

透传功能:连接后自动进入串口透传模式,UART数据与蓝牙数据双向透明传输

AT指令协议

AT指令用来设置模块的参数,模块在未连线状态下可以进行AT指令操作,连线后进 入串口透传模式。连线后,18脚置高电平或15脚置低电平100ms后,也会进入AT指令 状态;18脚置低电平(或者悬空)或15脚置高电平(或者悬空)100ms后,会退出AT 指令状态,返回透传状态。

模块启动大约需要200ms,所以最好在模块上电250ms以后才进行AT指令操作。在 这250mS时间内,也不要往模块串口发送数据。除特殊说明外,AT指令的参数设置立即 生效。同时,参数和功能的修改,掉电不会丢失。

AT指令格式:由AT+组成,结尾不用加回车换行。

默认出厂参数:

波特率9600N81,SPP蓝牙名HC-04,BLE蓝牙名HC-04LE;SPP配对密码1234, BLE没有配对密码。

(详细指令可搜索相应手册查看)HC-04手册链接

HC-04基础通讯协议
协议类型 版本 核心功能
SPP协议 V2.1 串口透传
BLE协议 V4.0 低功耗串口透传

HC-04引脚定义
引脚 功能描述
STATE 状态指示:未连接输出低电平,连接成功输出高电平
RXD 串口接收脚(接单片机TX)
TXD 串口发送脚(接单片机RX)
GND 电源地
VCC 电源输入(3.3V~6V)
KEY 清除配对记忆(需拉高电平200ms以上)

2. 程序

USART.h

#include "stm32f10x.h"                  // Device header
#include "stdio.h"

uint8_t Serial_RxFlag;//如果在主函数调用,可使用extern声明
uint16_t Serial_RxData;

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIO,USAET1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;            
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//TX引脚官方推荐使用浮空模式,可能会导致电平跳变,所以设置为复用推挽
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//RX引脚,默认上拉输入,接收引脚
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不选择流控
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//设置为发送与接收模式
	USART_InitStructure.USART_Parity = USART_Parity_No;//不选择奇偶校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位为1
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//位宽8位
	USART_Init(USART1,&USART_InitStructure);
	
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启USART中断
	
	NVIC_PriorityGroupConfig (NVIC_PriorityGroup_1);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//选择USART1中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1,ENABLE);
}

void USART1_IRQHandler(void)
{

	if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)//判断接收移位寄存器空
	{

		Serial_RxData = USART_ReceiveData(USART1);//将接收到的数据放在全局变量
		Serial_RxFlag = 1;//设置标志位,可在main函数中判断如果标志位为1,然后在接收数据,接收之后别忘了手动清除吧标志位,置0;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);		//清除标志位
	}
}	

void Serial_SendData(uint16_t Data)//发送数值
{
	USART_SendData(USART1,Data);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//判断发送移位寄存器非空
	USART_ClearFlag(USART1,USART_FLAG_TXE);//如果读取数据会自动清除标志位,可以不用手动清除
}

void Serial_SendArray(uint8_t* arr,uint32_t count)//发送数组
{
	uint32_t i;
	for(i = 0;i <= count;i ++)
	{
		Serial_SendData(arr[i]);
	}
}

void Serial_SendString(char* str)//发送字符串
{
	uint32_t i;
	for(i = 0;str[i] != '\0'; i ++)
	{
		Serial_SendData(str[i]);
	}
}

此程序只能接收的单个数据,如果大家要接收字符串需要进行相应修改;大家可根据接收数据在main函数进行相应操作(例如:接收数据等于0x01让小车前进)蓝牙程序可在微信小程序搜索。(江科大的蓝牙小程序只支持低功耗模式)

蓝牙发送指令和用串口助手发送差不多,一定难不到聪明的你;具体操作可观看江科大蓝牙模块视频教程视频链接

六. 寻迹模块

1. TCRT5000寻迹模块介绍

工作原理:TCRT5000模块由红外发射二极管和红外接收管组成。红外发射二极管持续发射波长为950纳米的红外线,当发射出的红外线没有被反射回来或反射强度不足时,红外接收管处于关断状态,模块输出端为高电平;当被检测物体出现在检测范围内,红外线被反射回来且强度足够大时,红外接收管饱和导通,模块输出端为低电平。和一般的传感器模块差不多,很好理解,有4个引脚

VCC:接电源正极(3.3~5V)

GND:接地

DO:数字开关量接口(有反射输出低电平,没反射输出高电平)

AO:模拟输出接口

2.程序

Tracker.c

#include "stm32f10x.h"                  // Device header

void Tracker_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//设置为上拉输入,默认是未检测到黑线,也可以设置为浮空输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_8;//左侧9,右侧10
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
}

uint8_t GPIO_GetDate(void)
{
	uint8_t Date;
	if((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_9) == SET) && (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) == SET))//如果左右都未检测到黑线返回1
	{
		Date = 1;
	}
	if((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_9) == RESET) && (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) == SET))//如果左侧检测到黑线,右侧没检测到返回2
	{
		Date = 2;
	}
	if((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_9) == SET) && (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) == RESET))//如果右侧检测到黑线,左侧没检测到返回3
	{
		Date = 3;
	}
	if((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_9) == RESET) && (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) == RESET))
	{
		Date = 0;
	}
	return Date;
}

代码写完就可以在main函数中通过判断返回值控制小车左转右转和前进停止,逻辑很简单,相信一定难不到大家。

七. 结尾

到这里,基于STM32的蓝牙小车控制逻辑就介绍完啦。如果在实际调试中遇到超声波检测不稳定(我就遇到了,上电一瞬间显示0,还不知道怎么解决)、小车动作异常等问题,可以尝试增加输入消抖延时,或者检查硬件接线是否接触良好。如果你在实践中摸索出了更优化的控制逻辑,欢迎在评论区分享你的思路~也可以私信我交流更多单片机项目细节!main函数的控制逻辑我没写,都是一些简单逻辑;如果大家真的想要源码的话可以私信我,如果要的人多的话,我会把代码放到百度网盘分享给大家。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐