
本文旨在让每一个人能够跟着步骤完成智能小车的基本功能代码也会有很多不足之处还请大家自行优化文章前言学习是在动手的前提下进步的只学习理论知识就是会很乱不知道怎么进行下去该学什么该怎么学代码该怎么写该怎么读不瞒大家我也是这个情况这个文章也是我在记录自己学习智能小车项目的记录希望能够对大家有所帮助。完成模块依次为1.电机驱动——能够实现后续的基本测试2.红外反射的循迹模块3.红外反射的避障模块4.超声波测距避障模块——舵机转动超声波模块测距5.蓝牙模块如果使用TB6612驱动电机推荐参考基于STM32智能循迹避障小车_stm32智能小车-CSDN博客如果使用L298N的驱动电机可以参考这个学长我最开始使用的L298N可能是源于淘宝买的套件问题一直不能驱动故而抛弃了套件使用自己最开始就有的TB6612【含源代码】STM32F1控制L298N直流电机驱动PWM调速、电机调速、STM32F103C8T6智能小车教程、控制电机正反转_stm32控制l298n驱动模块-CSDN博客一、前置准备前置知识需要学习stm32 有一点点标准库的基础可以去b站看江科大的视频讲的很详细学完串口那一块就可以制作了软件用的是Keil5开发语言C语言手机连接蓝牙模块软件是蓝牙调试器。需要准备的器件小车底盘面包板stm32f103c8t6电机驱动模块(tb6612)、蓝牙模块(hc06)、超声波测距模块(HC-SR04)、红外寻迹模块(TCRT5000)、舵机(sg90)、杜邦线若干、电源模块(可以换成充电宝用5伏那一口)、黑色胶布(用于制作轨迹)、超声波测距模块云台。二、模块接线由于我很早之前买过一个小车的套件直接使用这个模块的接线如图我把蓝牙模块接到了PA9和PA10三、代码介绍1、代码模板基础我在做这个智能小车直接使用的江科大源码如下2.电机驱动因为我是三轮小车。电机驱动两个轮子我把PWM1和PWM2接在PA0和PA1不需反接【如果是四轮可参考上述学长链接需要反接】对Motor稍作更改Motor.c#include stm32f10x.h // Device header #include PWM.h /** * 函 数直流电机初始化 * 参 数无 * 返 回 值无 */ void Motor_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOA的时钟 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); //将PA6,PA7,PA8,PA9引脚初始化为推挽输出 PWM_Init(); //初始化直流电机的底层PWM } /** * 函 数直流电机设置速度 * 参 数Speed 要设置的速度范围-100~100 * 返 回 值无 */ void Motor_SetLeftSpeed(int8_t Speed) { if (Speed 0) //如果设置正转的速度值 { GPIO_SetBits(GPIOB, GPIO_Pin_6); //PB6置高电平 GPIO_ResetBits(GPIOB, GPIO_Pin_7); //PB7置低电平设置方向为正转 PWM_SetCompare1(Speed); //PWM设置为速度值 } else if(Speed 0) { GPIO_ResetBits(GPIOB, GPIO_Pin_6); GPIO_ResetBits(GPIOB, GPIO_Pin_7); PWM_SetCompare1(Speed); } else //否则即设置反转的速度值 { GPIO_ResetBits(GPIOB, GPIO_Pin_6); //PB6置低电平 GPIO_SetBits(GPIOB, GPIO_Pin_7); //PB7置高电平设置方向为反转 PWM_SetCompare1(-Speed); //PWM设置为负的速度值因为此时速度值为负数而PWM只能给正数 } } void Motor_SetRightSpeed(int8_t Speed) { if (Speed 0) //如果设置正转的速度值 { GPIO_SetBits(GPIOB, GPIO_Pin_8); //PB8置高电平 GPIO_ResetBits(GPIOB, GPIO_Pin_9); //PB9置低电平设置方向为正转 PWM_SetCompare2(Speed); //PWM设置为速度值 } else if(Speed 0) { GPIO_ResetBits(GPIOB, GPIO_Pin_8); GPIO_ResetBits(GPIOB, GPIO_Pin_9); PWM_SetCompare2(Speed); } else //否则即设置反转的速度值 { GPIO_ResetBits(GPIOB, GPIO_Pin_8); //PB8置低电平 GPIO_SetBits(GPIOB, GPIO_Pin_9); //PB9置高电平设置方向为反转 PWM_SetCompare2(-Speed); //PWM设置为负的速度值因为此时速度值为负数而PWM只能给正数 } }Motor.h#ifndef __MOTOR_H #define __MOTOR_H void Motor_Init(void); void Motor_SetLeftSpeed(int8_t Speed); void Motor_SetRightSpeed(int8_t Speed); #endif在电机驱动代码的基础下接下来完成小车前进后退左转右转自转等代码封装这里可以直接以下面这种形式进行封装#include stm32f10x.h #include Motor.h void Car_Init(void) { Motor_Init(); } void Go_Ahead(void) { Motor_SetLeftSpeed(70); Motor_SetRightSpeed(70); } void Go_Back(void) { Motor_SetLeftSpeed(-70); Motor_SetRightSpeed(-70); } void Turn_Left(void) { Motor_SetLeftSpeed(0); Motor_SetRightSpeed(40); } void Turn_Right(void) { Motor_SetLeftSpeed(40); Motor_SetRightSpeed(0); } void Self_Left(void) { Motor_SetLeftSpeed(-40); Motor_SetRightSpeed(40); } void Self_Right(void) { Motor_SetLeftSpeed(40); Motor_SetRightSpeed(-40); } void Car_Stop(void) { Motor_SetLeftSpeed(0); Motor_SetRightSpeed(0); }这样直接调用类似 Go_Ahead()等形式就可以我考虑到轮胎误差可能有左轮摩擦力更大右轮摩擦力较小等情况导致同样电机速率而小车走歪所有对封装进行简单修改可以在调用的时候直接填写参数修改如下Car.c#include stm32f10x.h #include Motor.h int8_t Speed1; int8_t Speed2; void Car_Init(void) { Motor_Init(); } void Go_Ahead(Speed1,Speed2) { Motor_SetLeftSpeed(Speed1); Motor_SetRightSpeed(Speed2); } void Go_Back(Speed1,Speed2) { Motor_SetLeftSpeed(Speed1); Motor_SetRightSpeed(Speed2); } void Turn_Left(Speed1,Speed2) { Motor_SetLeftSpeed(Speed1); Motor_SetRightSpeed(Speed2); } void Turn_Right(Speed1,Speed2) { Motor_SetLeftSpeed(Speed1); Motor_SetRightSpeed(Speed2); } void Self_Left(Speed1,Speed2) { Motor_SetLeftSpeed(Speed1); Motor_SetRightSpeed(Speed2); } void Self_Right(Speed1,Speed2) { Motor_SetLeftSpeed(Speed1); Motor_SetRightSpeed(Speed2); } void Car_Stop(void) { Motor_SetLeftSpeed(0); Motor_SetRightSpeed(0); }这里Car的初始化中调用了Motor的初始化main中调用Car_Init();就相当于调用了Motor_Init();Car.h#ifndef __CAR_H #define __CAR_H void Car_Init(void); void Go_Ahead(int8_t Speed1,int8_t Speed2); void Go_Back(int8_t Speed1,int8_t Speed2); void Turn_Left(int8_t Speed1,int8_t Speed2); void Turn_Right(int8_t Speed1,int8_t Speed2); void Self_Left(int8_t Speed1,int8_t Speed2); void Self_Right(int8_t Speed1,int8_t Speed2); void Car_Stop(void); #endif对电机进行调试先完成轮胎正常旋转才能进行下面步骤Main.c测试#include stm32f10x.h // Device header #include OLED.h #include Key.h #include Delay.h #include Car.h /* --------------------------------------------- 驱动电机 PWM → Motor(电机速度) → Car(小车的前进,后退,左转,右转) → 完成电机正常驱动之后 Track(红外循迹) → Irobstacle(红外避障) --------------------------------------------- */ int8_t Speed; //定义速度变量 int main(void) { /*模块初始化*/ Car_Init(); Car_Stop(); //初始化先让小车暂停 while (1) { Go_Ahead(7070) Delay_ms(2000); //延时2s Car_Stop(); } }如果上面轮胎已经能够旋转可以进行接下来的循迹模块3.红外反射的循迹模块我直接把循迹逻辑封装可以在主程序直接调用Track.c#include stm32f10x.h // Device header #include Car.h #include Delay.h uint8_t L ; uint8_t R ; void Track_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin GPIO_Pin_13 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); } // PB13左边循迹 PB12右边循迹 void Auto_Tracing() { L GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13); //左红外 R GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12); //右红外 // 没有黑线电平0往前走 if(L0R0) { Go_Ahead(40,40); } // 有黑线电平1往后走 else if(L1R1) { Go_Back(-40,-40); Delay_ms(500); } // 左拐(左边黑线) else if(L1R0) { Turn_Left(0,40); Delay_ms(300); } // 右拐(右边黑线) else if(L0R1) { Turn_Right(40,0); Delay_ms(300); } }Track.h#ifndef __TRACK_H #define __TRACK_H void Track_Init(void); void Auto_Tracing(); #endifMain.c测试对循迹模块进行调试#include stm32f10x.h // Device header #include OLED.h #include Key.h #include Delay.h #include Car.h #include Track.h /* --------------------------------------------- 驱动电机 PWM → Motor(电机速度) → Car(小车的前进,后退,左转,右转) → 完成电机正常驱动之后 Track(红外循迹) → Irobstacle(红外避障) --------------------------------------------- */ int8_t Speed; //定义速度变量 int main(void) { //直流电机初始化 Car_Init(); //红外循迹初始化 Track_Init(); Car_Stop(); while (1) { Auto_Tracing(); } }4.红外反射的避障模块Irobstacle.c#include stm32f10x.h // Device header #include Irobstacle.h #include Car.h #include Delay.h void Irobstacle_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_11 | GPIO_Pin_12; //左边PA11,右边PA12 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_InitStructure); } //有黑线返回0 检测 左边 和 右边 是否有遮挡 uint8_t Left_Irobstacle_Get(void) { return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_11); } uint8_t Right_Irobstacle_Get(void) { return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_12); } void Auto_Irobstacle(void) { if(Left_Irobstacle_Get() 1 Right_Irobstacle_Get() 1) { Go_Ahead(40,40); } else if(Left_Irobstacle_Get() 1 Right_Irobstacle_Get() 0) { Self_Left(-40,40); } else if(Left_Irobstacle_Get() 0 Right_Irobstacle_Get() 1) { Self_Right(40,-40); } else if(Left_Irobstacle_Get() 0 Right_Irobstacle_Get() 0) { Go_Back(-40,-40); Delay_ms(1000); Self_Right(40,-40); Delay_ms(300); } }Irobstacle.h#ifndef __IROBSTACLE_H #define __IROBSTACLE_H void Irobstacle_Init(void); uint8_t Left_Irobstacle_Get(void); uint8_t Right_Irobstacle_Get(void); void Auto_Irobstacle(void); #endifMain.c测试对红外反射的避障模块进行测试#include stm32f10x.h // Device header #include OLED.h #include Key.h #include Delay.h #include Car.h #include Track.h #include Irobstacle.h /* --------------------------------------------- 驱动电机 PWM → Motor(电机速度) → Car(小车的前进,后退,左转,右转) → 完成电机正常驱动之后 Track(红外循迹) → Irobstacle(红外避障) --------------------------------------------- */ int8_t Speed; //定义速度变量 int main(void) { //直流电机初始化 Car_Init(); //红外循迹初始化 Track_Init(); //红外避障初始化 Irobstacle_Init(); Car_Stop(); while (1) { Auto_Irobstacle(); } }5-1.超声波测距避障模块——舵机转动Servo.c#include stm32f10x.h // Device header #include PWM.h void Servo_PWM_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period 20000 - 1; TIM_TimeBaseInitStructure.TIM_Prescaler 72 - 1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; TIM_TimeBaseInit(TIM3, TIM_TimeBaseInitStructure); TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 1500; TIM_OC2Init(TIM3, TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3, ENABLE); TIM_Cmd(TIM3, ENABLE); } void Servo_PWM_SetCompare(uint16_t Compare) { TIM_SetCompare2(TIM3, Compare); } /** * 函 数舵机初始化 * 参 数无 * 返 回 值无 */ void Servo_Init(void) { Servo_PWM_Init(); //初始化舵机的底层PWM } /** * 函 数舵机设置角度 * 参 数Angle 要设置的舵机角度范围0~180 * 返 回 值无 */ void Servo_SetAngle(float Angle) { if (Angle 0) Angle 0; if (Angle 180) Angle 180; Servo_PWM_SetCompare(Angle / 180 * 2000 500); //设置占空比 //将角度线性变换对应到舵机要求的占空比范围上 }Servo.h#ifndef __SERVO_H #define __SERVO_H void Servo_PWM_Init(void); void Servo_PWM_SetCompare(uint16_t Compare); void Servo_Init(void); void Servo_SetAngle(float Angle); #endif5-2.超声波测距避障模块——超声波模块测距Ultrasound.c#include stm32f10x.h // Device header #include Delay.h #include Car.h #include Servo.h uint16_t Cnt; uint16_t OverCnt; void Ultrasound_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; // trig GPIO_InitStructure.GPIO_Pin GPIO_Pin_15; 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_14; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); TIM_InternalClockConfig(TIM4); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period 60000 - 1; // arr TIM_TimeBaseInitStructure.TIM_Prescaler 72 - 1; // psc TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; TIM_TimeBaseInit(TIM4, TIM_TimeBaseInitStructure); } float Test_Distance() { GPIO_SetBits(GPIOB, GPIO_Pin_15); Delay_us(20); GPIO_ResetBits(GPIOB, GPIO_Pin_15); while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) RESET){}; TIM_Cmd(TIM4, ENABLE); while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) SET){}; TIM_Cmd(TIM4, DISABLE); Cnt TIM_GetCounter(TIM4); float distance (Cnt * 1.0 / 10 * 0.34) / 2; TIM4-CNT 0; Delay_us(100); return distance; } void Obstacle_Avoidance() { Go_Ahead(60,60); uint16_t Distance Test_Distance(); if(Distance 15){ Car_Stop(); Servo_SetAngle(180); Delay_ms(1000); Distance Test_Distance(); if(Distance 15){ Delay_ms(1000); Turn_Left(0,40); Delay_ms(1500); //向右转1s Servo_SetAngle(90); Go_Ahead(60,60); } else { Servo_SetAngle(0); Delay_ms(1000); Distance Test_Distance(); if(Distance 15){ Delay_ms(1000); Turn_Right(40,0); Delay_ms(1100); Servo_SetAngle(90); Go_Ahead(60,60); } else { Servo_SetAngle(90); Car_Stop(); Delay_ms(1000); Go_Back(-40,-40); Delay_ms(1000); Car_Stop(); Self_Left(-40,40); Delay_ms(1000); Car_Stop(); Delay_ms(1000); return; } } } Delay_ms(100); }Ultrasound.h#ifndef __ULTRASOUND_H #define __ULTRASOUND_H void Ultrasound_Init(void); float Test_Distance(); void Obstacle_Avoidance(void); #endifMain.c测试#include stm32f10x.h // Device header #include OLED.h #include Key.h #include Delay.h #include Car.h #include Track.h #include Irobstacle.h #include Servo.h #include Ultrasound.h #include Serial.h /* --------------------------------------------- 驱动电机 PWM → Motor(电机速度) → Car(小车的前进,后退,左转,右转) → 完成电机正常驱动之后 Track(红外循迹) → Irobstacle(红外避障) --------------------------------------------- */ int8_t Speed; //定义速度变量 uint8_t RxData; int main(void) { /*模块初始化*/ //直流电机初始化 Car_Init(); //红外循迹初始化 Track_Init(); //红外避障初始化 Irobstacle_Init(); //超声波舵机初始化 先恢复到向前 Servo_Init(); Servo_SetAngle(90); //超声波避障初始化 Ultrasound_Init(); Car_Stop(); while (1) { Obstacle_Avoidance(); } }6.蓝牙模块可以直接复制粘贴蓝牙模块的相关内容然后再稍作修改Serial.c#include stm32f10x.h // Device header #include stdio.h // 解决 FILE 未定义 #include stdarg.h // 解决 va_list 和 va_start 未定义 #include OLED.h #include Car.h #include Irobstacle.h #include Servo.h uint8_t Serial_RxData; uint8_t Serial_RxFlag; void Serial_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; // tx GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; // rx 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_Tx | USART_Mode_Rx; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_Init(USART1, USART_InitStructure); // 下面是用中断的方法实现串口发送 // 使能 USART1 的接收中断以便在接收到新的数据时触发中断处理程序。 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启串口接收数据的中断 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置NVIC为分组2 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn; // 选择配置NVIC的USART1线 NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; // 指定NVIC线路使能 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; // 指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; // 将结构体变量交给NVIC_Init配置NVIC外设 NVIC_Init(NVIC_InitStructure); USART_Cmd(USART1, ENABLE); } // 串口发送一个字节 void Serial_SendByte(uint8_t Byte) { USART_SendData(USART1, Byte); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); } // 串口发送一个数组 void Serial_SendArray(uint8_t *Array, uint16_t Length) { uint16_t i; for (i 0; i Length; i ) //遍历数组 { Serial_SendByte(Array[i]); //依次调用Serial_SendByte发送每个字节数据 } } // 串口发送一个字符串 void Serial_SendString(char *String) { uint8_t i; for (i 0; String[i] ! \0; i )//遍历字符数组字符串遇到字符串结束标志位后停止 { Serial_SendByte(String[i]); //依次调用Serial_SendByte发送每个字节数据 } } // 次方函数内部使用 uint32_t Serial_Pow(uint32_t X, uint32_t Y) { uint32_t Result 1; //设置结果初值为1 while (Y --) //执行Y次 { Result * X; //将X累乘到结果 } return Result; } // 串口发送数字 void Serial_SendNumber(uint32_t Number, uint8_t Length) { uint8_t i; for (i 0; i Length; i ) //根据数字长度遍历数字的每一位 { Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 0); //依次调用Serial_SendByte发送每位数字 } } // 使用printf需要重定向的底层函数 int fputc(int ch, FILE *f) { Serial_SendByte(ch); //将printf的底层重定向到自己的发送字节函数 return ch; } // 自己封装的prinf函数 void Serial_Printf(char *format, ...) { char String[100]; //定义字符数组 va_list arg; //定义可变参数列表数据类型的变量arg va_start(arg, format); //从format开始接收参数列表到arg变量 vsprintf(String, format, arg); //使用vsprintf打印格式化字符串和参数列表到字符数组中 va_end(arg); //结束变量arg Serial_SendString(String); //串口发送字符数组字符串 } // 获取串口接收标志位 uint8_t Serial_GetRxFlag(void) { if(Serial_RxFlag 1) { Serial_RxFlag 0; return 1; } return 0; } // 获取串口接收的数据 uint8_t Serial_GetRxData(void) { return Serial_RxData; } // USART1中断函数 void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) SET) // 判断是否是USART1的接收事件触发的中断 { // USART_ReceiveData: 将接收到的数据从 USART1 的接收数据寄存器中读取 Serial_RxData USART_ReceiveData(USART1); OLED_ShowHexNum(1,1, Serial_RxData, 10); if(Serial_RxData 0x30) Car_Stop(); if(Serial_RxData 0x31) Go_Ahead(60,60); if(Serial_RxData 0x32) Go_Back(-60,-60); if(Serial_RxData 0x33) Turn_Left(0,40); if(Serial_RxData 0x34) Turn_Right(40,0); if(Serial_RxData 0x35) Self_Left(-40,40); if(Serial_RxData 0x36) Self_Right(40,-40); if(Serial_RxData 0x37) { Car_Stop(); Servo_SetAngle(0); } if(Serial_RxData 0x38) { Car_Stop(); Servo_SetAngle(90); } if(Serial_RxData 0x39) { Car_Stop(); Servo_SetAngle(180); } USART_ClearITPendingBit(USART1, USART_IT_RXNE); } }Serial.h#ifndef __SERIAL_H #define __SERIAL_H void Serial_Init(void); void Serial_SendByte(uint8_t Byte); void Serial_SendArray(uint8_t *Array, uint16_t Length); void Serial_SendString(char *String); void Serial_SendNumber(uint32_t Number, uint8_t Length); void Serial_Printf(char *format, ...); uint8_t Serial_GetRxFlag(void); uint8_t Serial_GetRxData(void); #endifMain.c测试#include stm32f10x.h // Device header #include OLED.h #include Key.h #include Delay.h #include Car.h #include Track.h #include Irobstacle.h #include Servo.h #include Ultrasound.h #include Serial.h /* --------------------------------------------- 驱动电机 PWM → Motor(电机速度) → Car(小车的前进,后退,左转,右转) → 完成电机正常驱动之后 Track(红外循迹) → Irobstacle(红外避障) --------------------------------------------- */ int8_t Speed; //定义速度变量 uint8_t RxData; uint8_t KeyNum; int8_t Num 0; int main(void) { /*模块初始化*/ //直流电机初始化 Car_Init(); //红外循迹初始化 Track_Init(); //红外避障初始化 Irobstacle_Init(); //超声波舵机初始化 先恢复到向前 Servo_Init(); Servo_SetAngle(90); //超声波避障初始化 Ultrasound_Init(); //蓝牙 Serial_Init(); Car_Stop(); while (1) { //蓝牙模块连接 if(Serial_GetRxFlag() 1) { RxData Serial_GetRxData(); Serial_SendByte(RxData); if(RxData G) //前进 { Go_Ahead(60,70); } else if(RxData B) //后退 { Go_Back(-60,-70); } else if(RxData L) //左转 { Turn_Left(0,60); } else if(RxData R) //右转 { Turn_Right(60,0); } else if(RxData S) //停止 { Car_Stop(); } } }若代码完成后想要连接串口或者app进行测试推荐参考以下这两篇文章app随便哪个蓝牙连接器都可以手把手教你使用--常用模块--HC05蓝牙模块无线蓝牙串口透传模块实例手机蓝牙控制STM32单片机点亮LED灯_hc蓝牙助手-CSDN博客基于stm32与蓝牙模块的使用_stm32蓝牙模块怎么用-CSDN博客都是蓝牙模块HC-05上面有一个按键用于调试指令模式再次提醒这是本人学习记录将所有代码直接展示方便向我这样的小白初学的思路分别对不同部分功能进行单独测试但我也是小白这些代码是在参考众多文章代码和自己的理解完成的我深知代码的不足大家仅供参考希望对你能有所帮助也欢迎大佬指教不足。谢谢