基于STM32的智能小车(基于江科大STM32入门教程)
相信你已经学完了江科大STM32的入门教程了吧,如果不知道接下来做什么,那就跟着我做一个简单项目来加深印象吧,聪明的你指定是一看就会,一做就对;希望本片文章能对你在学习嵌入式的道路上有所帮助。如果有写的不对的地方请大家多多包涵并指出,如果有跟好的方案也可以私信我,我会及时采纳更新。
相信你已经学完了江科大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手册链接
| 协议类型 | 版本 | 核心功能 |
|---|---|---|
| SPP协议 | V2.1 | 串口透传 |
| BLE协议 | V4.0 | 低功耗串口透传 |
| 引脚 | 功能描述 |
|---|---|
| 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函数的控制逻辑我没写,都是一些简单逻辑;如果大家真的想要源码的话可以私信我,如果要的人多的话,我会把代码放到百度网盘分享给大家。
更多推荐




所有评论(0)