240602_-1_进度搬运_第2个校园团队项目_STM32F103ZET6四轮智能红外循迹避障小车

实践报告格式:

《嵌入式系统创新设计实践——STM32小车实践报告》

  1. 实践目的

内容一:

在STM32F103ZET6的小车平台上点亮一个LED灯,掌握如何控制GPIO引脚输出。

内容二:

实现小车的前进、后退以及转向功能,并调整小车的运行速度

掌握电机控制基本原理,并分析电机模块与STM32单片机的接口电路。

掌握STM32单片机驱动电机控制小车行走程序设计的基本方法。

内容三:

掌握智能车检测传感器的基本工作原理,包括红外循迹传感器、红外避障传感器和超声波模块,掌握多功能智能车程序设计的基本方法。

实现智能小车循迹程序的设计,设计T型或十字型路线,实现循迹程序的设计。

设计程序能够让小车在行动过程中进行自主避障

  1. 实践环境与器材

硬件环境:STM32F103ZET6小车开发板、LED灯、电阻、导线、烧录线等。

软件环境:Keil 5 MDK-ARM集成开发环境、STM32F103ZET6芯片手册、ST-LINK调试器等。

三、实践方案(设计内容原理分析,设计方案,硬件结构与接口电路图,软件流程图)

内容一实践方案:

内容分析:

本设计旨在通过应用STM32F103ZET6芯片的通用输入/输出(GPIO)功能,实现对LED灯点亮及熄灭的控制。通过将GPIO引脚配置为输出模式,并据此调整引脚电平至高或低,从而控制LED的工作状态。
设计方案:

  1. 将LED的正极与GPIO引脚相连,负极接地,并在二者之间串接一只适当的电阻以限制电流。
  2. 启用相应的GPIO端口时钟,并将GPIO引脚设置为输出模式。
  3. 编制程序代码,通过控制GPIO引脚输出高电平或低电平,以实现LED的点亮与熄灭。

硬件结构与接口电路图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment.png

软件流程图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 1.png

内容二实践方案:

2-1电机驱动部分:

设计内容原理分析:
电机驱动小车运动的基本原理在于,通过控制左右电机引脚的高低电平输出。对于左电机而言,当芯片输出引脚AIN1和AIN2均为低电平时,小车保持静止状态;若AIN1为低电平而AIN2为高电平,则小车向前行驶;反之,若AIN1为高电平而AIN2为低电平,则小车后退。右电机的控制逻辑与左电机相同,即BIN1和BIN2的电平组合决定了小车的停止、前进或后退。此外,通过调整左右电机的运动状态,可以实现小车的左转、右转及停止等操作。

设计方案:
在本设计方案中,电机速度的调控是通过两路PWM信号实现的,这两路PWM信号分别连接至STM32的PB6和PB9引脚,对应于TIM4的第一通道和第四通道。然而,鉴于本设计采用的STM32型号为STM32F103ZET6,其外设并不包含TIM4,因此采用了通过改变定时器周期来生成PWM信号的方法。理论上,可以将TB6612的PWMA和PWMB直接接至高电平,使电机在给定电压下以最高功率运行,但这并不利于精确控制。在采用定时器周期变化生成PWM信号之前,首先需要初始化TB6612与STM32连接的所有GPIO引脚,并对STM32的定时器进行初始化,包括使能相应的总线时钟、设置定时器的定时时间及工作模式、启动定时器,并编写定时器中断服务程序。

硬件结构与接口电路图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 3.png

软件流程图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 6.png

2-2调速部分:

原理分析:脉宽调制(PWM)的基本原理在于通过对逆变电路中开关器件的通断控制,以产生一系列幅值恒定的脉冲,代替正弦波或其他所需波形。具体而言,是在输出波形的半周期内生成多个脉冲,通过调整这些脉冲的宽度,使其等效电压呈现正弦波形态,从而获得平滑的输出波形,且低次谐波较少。将正弦半波波形等分为N份,视之为由N个相互连接的脉冲组成的波形,这些脉冲的宽度相等,但幅值不同,并且脉冲顶部呈曲线状,按正弦规律变化。通过使用数量相同但宽度不等的矩形脉冲序列替代,使得矩形脉冲的中点与相应正弦等分点重合,并保持矩形脉冲与相应正弦部分面积相等,便可得到PWM波形。在PWM波形中,各脉冲的幅值相等,通过调整脉冲宽度的比例系数,可改变等效输出正弦波的幅值。因此,在交-直-交变频器中,PWM逆变电路输出的脉冲电压即为直流侧电压的幅值。根据给定的正弦波频率、幅值及半周期内的脉冲数,可准确计算出PWM波形各脉冲的宽度和间隔,并据此控制电路中各开关器件的通断,以产生所需的PWM波形。

设计方案:通过预设小车芯片中的调速代码,控制电机电压,进而调节电机转速。在实际控制中,通过H桥控制直流电机的正反转,当T1和T4二极管导通,形成粉色通路;当T2和T3二极管导通,形成蓝色通路,实现弱电控制强电。通过控制二极管的通断时间,即PWM控制,可自由调节Us的电压值,从而控制电机转速。D1-D4二极管起到续流作用,因电机中含绕组,断电后电感电流不能瞬时归零,故断电后电流通过棕色和绿色通路放电。在一个周期内,我们通过控制通电的时间就可以调控平均电压,而平均电压的高低直接控制电机的转速,通电时间/周期,就可以得到占空比,我们也就是通过控制电机的占空比来控制电机的转速的。

硬件结构与接口电路图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 13.png

软件流程图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 14.png

内容三实践方案:

3-1红外寻迹部分:

设计内容原理分析:循迹模块利用红外传感器检测黑线,通过红外发射管发出的光线,遇白色反射、遇黑色吸收的原理,实现对黑线的识别。红外传感器遇黑线输出高电平,否则输出低电平。因此,左侧传感器遇黑线时小车左拐,右侧传感器遇黑线时小车右拐。

设计方案:STM32智能小车主要由STM32F103C8T6微控制器、L298N电机驱动模块、超声波模块和舵机云台组成。L298N驱动两路电机,超声波模块控制舵机角度,STM32通过GPIO和TIMER控制电机转向、测距和避障。系统使用12V锂电池供电,配备循迹模块和下载器,底盘可选三轮或四轮。

硬件结构与接口电路图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 15.png

软件流程图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 16.png

3-2红外避障部分:

硬件设计原理:红外对管模块通过发射红外线并接收其反射信号,转换为电信号输出,用于测定障碍物距离和方位。

软件设计原理:

初始化GPIO引脚,将红外发射管引脚设为输出模式,红外接收管引脚设为输入模式。

控制红外发射管通过GPIO引脚高电平发射红外线。

通过读取红外接收管GPIO引脚电平,判断是否接收到反射红外线,以便识别障碍物。

根据障碍物检测结果,控制小车避障或前进。

设计方案:

正确安装并调试传感器,确保传感器在有无障碍物时能正确反应。

使用空程序进行调试,调整传感器直至在5-10cm范围内检测到障碍物时亮灯。

硬件结构与接口电路图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 17.png

软件流程图:

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 18.png

四、实践过程及实现(包括实践要求中需要分析的问题及主要实现程序)

内容一:

具体分析:

具体落实到实践中应该怎么进行硬件软件初次操作?

硬件连接

将LED的正极引脚连接到小车板上的一个GPIO引脚。

将LED的负极引脚连接到地线(GND)。

最好在LED的正极还串联一个适当的电阻,以限制电流。

软件配置

使用Keil或其他IDE打开STM32F103VET6的工程项目。

在工程中找到与GPIO相关的头文件和源文件,通常在"stm32f10x_gpio.h"和"stm32f10x_gpio.c"中。

使能对应GPIO端口的时钟。

配置将要用于LED的GPIO引脚为输出模式。

编写控制代码

在main.c或其他合适的文件中编写控制LED的代码。

使用库函数GPIO_WriteBit或者GPIO_Write将对应引脚设置为高电平(打开LED)或低电平(关闭LED)。

如果需要LED闪烁,可以在循环中切换GPIO引脚状态,并使用延时函数控制闪烁频率。

主要实现程序:

#include "stm32f10x.h"

#include "interface.h"

int main()

{

GPIOCLKInit(); //调用IO口使能时钟函数

UserLEDInit(); //调用LED灯函数

while(1)

{

LED_RESET; //低电平点高LED灯

}

}

内容二:

2-1电机驱动部分:

问题及分析:

如何通过人工操作实现对小车的控制(包括红外遥控小车的前进、后退及转向):

通过实施红外遥控技术,实现对小车的人为操控。

主要实现程序:

#include "stm32f10x.h"

#include "interface.h" //连接IO口定义头文件

#include "LCD1602.h"

#include "IRCtrol.h"

#include "motor.h"

//全局变量定义

unsigned int speed_count=0;//占空比计数器 50次一周期

char front_left_speed_duty=SPEED_DUTY;

char front_right_speed_duty=SPEED_DUTY;

char behind_left_speed_duty=SPEED_DUTY;

char behind_right_speed_duty=SPEED_DUTY;

unsigned char tick_5ms = 0;//5ms计数器,作为主函数的基本周期

unsigned char tick_1ms = 0;//1ms计数器,作为电机的基本计数器

unsigned char tick_200ms = 0;//刷新显示

char ctrl_comm = COMM_STOP;//控制指令

unsigned char continue_time=0;

int main(void)

{

delay_init();

GPIOCLKInit();

UserLEDInit();

LCD1602Init();

IRCtrolInit();

TIM2_Init();

MotorInit();

// ServoInit();

while(1)

{

if(tick_5ms >= 5)//5ms

{

tick_5ms = 0;

tick_200ms++;

if(tick_200ms >= 40)

{

tick_200ms = 0;

LEDToggle(LED_PIN);

}

continue_time--;//200ms 无接收指令就停车

if(continue_time == 0)

{

continue_time = 1;

CarStop();//关闭本指令之后,将变成红外遥控小车时要按下停止按钮才能停,为节省遥控器电量建议关闭

}

//do something

if(ir_rec_flag == 1)//接收到红外信号

{

ir_rec_flag = 0;

switch(ctrl_comm)

{

case COMM_UP: CarGo();break;

case COMM_DOWN: CarBack();break;

case COMM_LEFT: CarLeft();break;

case COMM_RIGHT: CarRight();break;

case COMM_STOP: CarStop();break;

default : break;

}

LCD1602WriteCommand(ctrl_comm);

2-2调速部分:

问题分析:

如何调节电机速度?

芯片的通用输入输出(GPIO)接口内置振荡源,并且该寄存器支持多种输出模式,包括推挽输出和脉冲宽度调制(PWM)输出等。通过编程将寄存器配置为PWM输出模式,可以直接从GPIO接口输出PWM波形。此PWM波形可直接连接至舵机引脚,用以控制小车的转速。

主要实现程序:

htim7.Instance = TIM7;

htim7.Init.Prescaler = 72-1;

htim7.Init.CounterMode = TIM_COUNTERMODE_UP;

htim7.Init.Period = 100-1;

htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

if (HAL_TIM_Base_Init(&htim7) != HAL_OK)

{

Error_Handler();

}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;

sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)

{

Error_Handler();

电机正转反转部分:

#include "stm32f10x.h"

#include "interface.h"

#include "LCD1602.h"

#include "IRCtrol.h"

#include "motor.h"

#include "uart.h"

#include "redvoid.h"

//È«¾Ö±äÁ¿¶¨Òå

unsigned int speed_count=0;//Õ¼¿Õ±È¼ÆÊýÆ÷ 50´ÎÒ»ÖÜÆÚ

char front_left_speed_duty=SPEED_DUTY;

char front_right_speed_duty=SPEED_DUTY;

char behind_left_speed_duty=SPEED_DUTY;

char behind_right_speed_duty=SPEED_DUTY;

unsigned char tick_5ms = 0;//5ms¼ÆÊýÆ÷£¬×÷ΪÖ÷º¯ÊýµÄ»ù±¾ÖÜÆÚ

unsigned char tick_1ms = 0;//1ms¼ÆÊýÆ÷£¬×÷Ϊµç»úµÄ»ù±¾¼ÆÊýÆ÷

unsigned char tick_200ms = 0;//Ë¢ÐÂÏÔʾ

char ctrl_comm = COMM_STOP;//¿ØÖÆÖ¸Áî

char ctrl_comm_last = COMM_STOP;//ÉÏÒ»´ÎµÄÖ¸Áî

unsigned char continue_time=0;

unsigned char bt_rec_flag=0;//À¶ÑÀ¿ØÖƱê־λ

// ¼ì²â×ó±ß´«¸ÐÆ÷º¯Êý£¬ÓÐÎïÌåµç»úǰ½ø£¬ÎÞÎïÌåµç»úºóÍË

//IN1=1 ×óºó

//IN2=1 ×óǰ

//IN3=1 ÓÒºó

//IN4=1 ÓÒǰ

void VoidRun2(void) //ÓÐÎïʱºóÍË£¬Ã»ÓÐÎïÌåʱǰ½ø

{

//L×ó±ß¼ì²âµ½ÎïÌå

if(VOID_L_IO == BARRIER_Y)//ǰ·½ÓÐÎïÌå0

{

LED_RESET;//¿ªµÆ µÍµçƽ

//IN1 ¸ßµçƽ×óºóÍË

//IN2 µÍµçƽ

//IN1=1 IN2=0 ×óºóÍ˺¯Êý

FRONT_RIGHT_F_RESET; //IN1 ¸ßµçƽ

FRONT_RIGHT_B_SET; //IN2 µÍµçƽ

}

if(VOID_L_IO == BARRIER_N)//ǰ·½ÎÞÎïÌå1

{

LED_SET; //¹ØµÆ ¸ßµçƽ

//IN1 µÍµçƽ

//IN2 ¸ßµçƽǰ½ø

//IN1=0 IN2=1 ×óǰ½øº¯Êý

FRONT_RIGHT_F_SET; //IN1 µÍµçƽ

FRONT_RIGHT_B_RESET; //IN2 ¸ßµçƽ

}

}

int main(void)

{

delay_init();

GPIOCLKInit();

UserLEDInit();

LCD1602Init();

//IRCtrolInit();

TIM2_Init();

MotorInit();

ServoInit();

RedRayInit();

//USART3Conf(9600);

while(1)

{

VoidRun2();

//LED_RESET;//¿ªµÆ µÍµçƽ

//LED_SET; //¹ØµÆ ¸ßµçƽ

BEHIND_RIGHT_EN;

//BEHIND_LEFT_GO; //×óǰ½øº¯Êý

//BEHIND_LEFT_BACK; //ǰºóÍ˺¯Êý

//FRONT_RIGHT_GO; //ÓÒǰ½øº¯Êý

//FRONT_RIGHT_BACK; //ÓÒºóÍ˺¯Êý

}

内容三:

3-1红外寻迹部分:

问题及分析:当小车拐弯时,如何实现让其减速拐弯?

分析:检测寻迹后提前调整转速。

主要实现程序:

void CarGo(void)

{

front_right_speed_duty=20; //右边前进,改变电机PWM值可实现速度调节

behind_left_speed_duty=20; //左边前进,改变电机PWM值可实现速度调节

}

//后退

void CarBack(void)

{

front_right_speed_duty=-SPEED_DUTY; //右边后退,全速

behind_left_speed_duty=-SPEED_DUTY; //左边后退,全速

}

//向左

void CarLeft(void)

{

front_right_speed_duty=25; //右边前进,实现左转

behind_left_speed_duty=-20; //左边后退

}

//向右

void CarRight(void)

{

front_right_speed_duty=-20; //右边后退

behind_left_speed_duty=25;//左前进,实现右转

}

//停止

void CarStop(void)

{

front_right_speed_duty=0;

behind_left_speed_duty=0;

}

void MotorInit(void)

{

MotorGPIO_Configuration();

CarStop();

//全局变量定义

unsigned int speed_count=0;//占空比计数器 50次一周期

//char front_left_speed_duty=SPEED_DUTY;

char front_right_speed_duty=SPEED_DUTY;

char behind_left_speed_duty=SPEED_DUTY;

//char behind_right_speed_duty=SPEED_DUTY;

unsigned char tick_5ms = 0;//5ms计数器,作为主函数的基本周期

unsigned char tick_1ms = 0;//1ms计数器,作为电机的基本计数器

unsigned char tick_200ms = 0;//刷新显示

char ctrl_comm = COMM_STOP;//控制指令

char ctrl_comm_last = COMM_STOP;//上一次的指令

unsigned char continue_time=0;

unsigned char bt_rec_flag=0;//

3-2红外避障部分:

问题描述:如何实现小车的自主左转、右转、前进及后退操作?

分析过程:首先,需要编写一套控制程序,该程序包含了控制小车左转、右转、前进及后退的相关函数。随后,将此程序通过编程工具烧录至小车的控制单元中,以赋予小车上述的自主移动能力。

主要实现程序:

#include "redvoid.h"

#include "interface.h"

#include "motor.h"

extern char ctrl_comm;

//»ñÈ¡ºìÍâ±ÜÕÏÄ£¿é״̬

char GetVoidStatus(void)

{

char left=0,right=0;

char count;

if(VOID_L_IO == BARRIER_Y)

{

count = 2;

while(--count)//10ms ²É¼¯2´Î¾ùÒª²É¼¯µ½Ç°ÃæÕϰ­ÎïÐÅÏ¢£¬Â˲¨

{

if(VOID_L_IO == BARRIER_N)

break;

Delayms(1);

}

if(count == 0) left = 1;

}

if(VOID_R_IO == BARRIER_Y)

{

count = 2;

while(--count)//10ms ²É¼¯2´Î¾ùÒª²É¼¯µ½Ç°ÃæÕϰ­ÎïÐÅÏ¢£¬Â˲¨

{

if(VOID_R_IO == BARRIER_N)

break;

Delayms(1);

}

if(count == 0) right = 2;

}

return left + right;

}

//ÑÓʱµÄͬʱ¼ì²âºìÍ⣬һµ©·¢ÉúÕϰ­Î¾ÍÍ£Ö¹²¢Ìø³öÑÓʱ

void DelayCheck(int ms)

{

while(ms--)

{

Delayms(1);

if(VOID_NONE != GetVoidStatus())

{

CarStop();

return;

}

}

}

//ºìÍâ±ÜÕÏ´¦Àí

//´¦Àí·½Ê½£º×ó±ß¼ì²âµ½ ºóÍË500ms ÓÒת500ms

// Óұ߼ì²âµ½ ºóÍË500ms ×óת500ms

// Á½±ß¼ì²âµ½ ºóÍË700ms ÓÒת500ms

// û¼ì²âµ½ Ö±ÐÐ

void VoidRun(void)

{

char status;

status = GetVoidStatus();

switch(status)

{

case VOID_LEFT:

ctrl_comm = COMM_RIGHT;CarBack(); Delayms(500); CarRight(); DelayCheck(500);

break;

case VOID_RIGHT:

ctrl_comm = COMM_LEFT;CarBack(); Delayms(500); CarLeft(); DelayCheck(500);

break;

case VOID_BOTH:

ctrl_comm = COMM_RIGHT;CarBack(); Delayms(700); CarRight(); DelayCheck(500);

break;

case VOID_NONE:

ctrl_comm = COMM_UP;CarGo();

break;

default: break;

}

}

五、结果与分析(结果描述,实践中遇到的问题及处理方法)

内容一:

问题描述:在启动小车后,观察到指示灯未能点亮。

分析与排查过程:经过仔细检查,确认硬件与软件配置均无异常,且通过USB接口供电时设备运行正常,因此推断问题可能源于电池电量耗尽。随后对电池进行充电,问题得到有效解决。

解决结果:通过程序控制,小车成功执行点灯操作,LED灯顺利点亮。

结果描述:小车LED灯亮起

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 19.png

内容二:

2-1电机驱动部分

遇到的问题及分析:

问题描述:小车在刚开始运行时出现无法控制的旋转现象。

解决措施:经检查发现,问题源于函数代码输入不完整,导致程序运行错误。重新完整 输入函数代码后,小车恢复了正常运行状态。

结果描述:通过红外遥控技术,小车能够完成前进、后退以及旋转等一系列动作。

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 20.png

2-2调速部分

遇到的问题及分析:

在小车运行过程中出现间歇性停顿的问题。

解决方案:

将GPIO结构体配置为全局变量,以解决局部变量与全局变量在作用域和周期上的差异。局部变量仅在函数内有效,其周期限于函数执行期间,函数执行完毕后,其占用的内存将被回收。相反,全局变量在整个文件中有效,其周期贯穿程序运行全程,内存不会被回收。此外,全局变量在定义时会被初始化为零,而局部变量则不会自动清零,其初始值是不确定的。这导致PWM波形可能不是标准的方波。通过初始化所有定义的对象,确保变量的赋值与函数输出一致,从而使小车能够正常运行。

结果描述:小车配备了自动调速功能,能够无缝实现速度从快到慢的渐变过程。

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 21.png

内容三:

3-1红外寻迹部分:

遇到的问题及分析:

问题描述:小车在执行转向动作时出现振动现象。

解决策略:通过调整程序,降低小车的基础运行速度,并在速度变化过程中加入防抖动算法,以减轻小车在转向时的振动情况。

结果描述:小车成功实现了T字形循迹、十字形循迹以及绕圈循迹,并且在执行循迹功能时表现出了平稳的性能。

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 23.png

3-2红外避障部分:

遇到的问题:第一次运行小车时避障会出现避障动作进行一半停滞现象。

处理方法:固定小车的硬件,尽量不让硬件松动,重新烧入程序,使小车能够进行完全避障。

结果描述:把小车放在地上,小车能够自主的移动,自主的进行前进后退左转和右转,并且在运动中,如果前方会碰到障碍的话,小车会进行自主避障。

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 24.png

红外寻迹+红外避障

05publishedweb/W002-学习进度/DocxerAttachments/Attachment 25.png