草庐IT

基于STM32f103c8t6的简单红外巡迹避障小车制作

在人间· 2023-08-20 原文

目录

一、硬件部分

1、模块选择

(1)电源

电源模块选用的是18650锂电池(充电器及电池底座)、3.3v稳压模块。

(2)车模

淘宝最常见的智能车底。

(3)电机

买的智能车带有四个电机,选用L298N电机驱动板对电机进行驱动。

(4)巡迹及避障

巡迹选用四路红外模块实现,避障选用超声波模块HC-SR04实现。


(5)其它

热熔胶枪,焊笔,杜邦线,主控制器STM32f103c8t6核心板,烧录工具(usb转ttl模块、ST-Link或J-Link)等。

2、硬件连接

(1)电源

将电池组引出线的正极接L298N模块的+12V(Vcc)端,负极接L298N模块的GND端,再将L298N的+5V端和GND端与3.3V稳压模块的5V与GND分别相连。最后把稳压模块的3.3V和GND与STM32核心板上的3.3V与GND相连。

(3)电机

将一侧电机的两极分别与L298N模块的Out1和Out2相连,另一侧的电机与Out3和Out4相连(写程序时测试正转与反转如何控制)。将L298N上的IN1,IN2,IN3,IN4分别与STM32核心板上的四个不同的IO口连接(本人连接的是PA7,PA6,PB1,PB0)。

(4)红外

四个红外模块与主控板上对应的端口相连即可。主控板上的3.3V与GND与STM32核心板上的3.3V和GND相连,IN1,IN2,IN3,IN4分别与STM32核心板上的四个不同的IO口连接(本人连接的是PB4,PB5,PB6,PB7)。

(4)超声波

HC-SR04上的5V与GND与STM32核心板上的5V和GND相连,TRIG与ECHO与STM32核心板上的两个不同的IO口连接(本人连接的是PB11,PB10)。

二、软件部分

1、电机

编程思路:控制核心板IO口的电平(即电机驱动模块的IN1,IN2,IN3,IN4)从而控制Out1、Out2、Out3、Out4的电平高低,进而控制电机使其正转、反转、不转。由于单纯地拉高拉低电平只能让电机在转与不转的状态切换,故决定利用TIM3的四个通道分别产生四路PWM信号来控制电机速度。

/*Head File*/
#ifndef _PWM_H
#define _PWM_H
#include <stm32f10x.h>
#include <sys.h>

#define PWMA1   TIM3->CCR1  
#define PWMA2   TIM3->CCR2 
#define PWMB1   TIM3->CCR3  
#define PWMB2   TIM3->CCR4

void PWM_Init(uint16_t Arr,uint16_t Psc);
void Set_LeftMotor(int speed);
void Set_RightMotor(int speed);

#endif
#include <PWM.h>

void PWM_Init(uint16_t Arr,uint16_t Psc){
	GPIO_InitTypeDef GPIO_InitStruct;         
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_OCInitTypeDef TIM_OCInitStruct;   
	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);        
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;      
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;      
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
 
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);     
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;       
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;     
	TIM_TimeBaseInitStruct.TIM_Period=Arr;         
	TIM_TimeBaseInitStruct.TIM_Prescaler=Psc;      	
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
 
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;          
	TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse=0;
	
	TIM_OC1Init(TIM3,&TIM_OCInitStruct );          
	TIM_OC2Init(TIM3,&TIM_OCInitStruct );
	TIM_OC3Init(TIM3,&TIM_OCInitStruct );          
	TIM_OC4Init(TIM3,&TIM_OCInitStruct);
	
	TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);
	TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
	TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);      
	TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);		
	
	TIM_ARRPreloadConfig(TIM3,ENABLE);      
	
	TIM_Cmd(TIM3,ENABLE);    
}

void Set_RightMotor(int speed){
	if(speed<0)			PWMA1=2000,PWMA2=2000+speed;
	else 	          PWMA2=2000,PWMA1=2000-speed;
}
	
void Set_LeftMotor(int speed){
	if(speed<0)			PWMB1=2000,PWMB2=2000+speed;
	else 	          PWMB2=2000,PWMB1=2000-speed;
}

2、红外

编程思路:检测到黑线时红外模块将会把返回高电平,否则返回低电平。
故只需要读取对应的IO端口电平用以控制小车转向从而实现巡线。(由于在自己铺的简易赛道上四路红外巡线效果和两路巡线效果差不多,所以只用了中间两个红外模块进行巡线,外侧两个用来辅助超声波模块实现避障)

#ifndef _SENSOR_H
#define _SENSOR_H
#include <stm32f10x.h>

void Sensor_Init(void);
unsigned char Sensor_Detect(void);

#endif

#include <sensor.h>
#include <delay.h>

#define STRAIGHT 0
#define LEFTS 1
#define LEFTL 2
#define RIGHTS 3
#define RIGHTL 4
#define STOP 5
#define BACK 6

uint8_t left1;
uint8_t left0;
uint8_t right1;
uint8_t right0;
uint8_t counter=0;
float length;


void Sensor_Init(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}

unsigned char Sensor_Detect(void){
	left1=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
	left0=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8);
	right0=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7);
	right1=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6);
	if(left0==1&&right0==0)return LEFTS;
	else if(left1==0&&right1==1)return LEFTL;
	else if(left0==0&&right0==1)return RIGHTS;
	else if(left1==1&&right1==0)return RIGHTL;
	else if(left1==1&&left0==1&&right0==1&&right1==1)return STOP;
	else if(left1==0&&right1==0)return BACK;
	else return STRAIGHT;
}

3、超声波

/*Head File*/
#ifndef __HC_H
#define __HC_H	
#include "sys.h"


void hcsr04_NVIC(void);

void Hcsr04Init(void);

static void OpenTimerForHc(void);

static void CloseTimerForHc(void);

void TIM4_IRQHandler(void);

u32 GetEchoTimer(void);

float Hcsr04GetLength(void);

#endif
#include "hc.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"	
#include "led.h"
#define TRIG_Send  PBout(11)
#define ECHO_Reci  PBin(10)

#define HCSR04_PORT     GPIOB
#define HCSR04_CLK      RCC_APB2Periph_GPIOB
#define HCSR04_TRIG     GPIO_Pin_11
#define HCSR04_ECHO     GPIO_Pin_10

u16 msHcCount = 0;

void hcsr04_NVIC()
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
			
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;             
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;       
	NVIC_Init(&NVIC_InitStructure);
}

void Hcsr04Init()
{  
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;   
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE);
   
    GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG;      
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
    GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
     
    GPIO_InitStructure.GPIO_Pin =   HCSR04_ECHO;     
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);  
    GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);    
     
          
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);   
     
    TIM_DeInit(TIM2);
    TIM_TimeBaseStructure.TIM_Period = (1000-1); 
    TIM_TimeBaseStructure.TIM_Prescaler =(72-1); 
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);          
        
    TIM_ClearFlag(TIM4, TIM_FLAG_Update);  
    TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);    
    hcsr04_NVIC();
    TIM_Cmd(TIM4,DISABLE);     
}


static void OpenTimerForHc()  
{
   TIM_SetCounter(TIM4,0);
   msHcCount = 0;
   TIM_Cmd(TIM4, ENABLE); 
}

static void CloseTimerForHc()    
{
   TIM_Cmd(TIM4, DISABLE); 
}

void TIM4_IRQHandler(void)  
{
   if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)  
   {
       TIM_ClearITPendingBit(TIM4, TIM_IT_Update  ); 
       msHcCount++;
   }
}
 

u32 GetEchoTimer(void)
{
   u32 t = 0;
   t = msHcCount*1000;
   t += TIM_GetCounter(TIM4);
   TIM4->CNT = 0;  
   Delay_ms(50);
   return t;
}
 
float Hcsr04GetLength(void )
{
   u32 t = 0;
   int i = 0;
   float lengthTemp = 0;
   float sum = 0;
   while(i!=5)
   {
      TRIG_Send = 1;      
      Delay_us(20);
      TRIG_Send = 0;
      while(ECHO_Reci == 0);      
      OpenTimerForHc();        
      i = i + 1;
      while(ECHO_Reci == 1);
      CloseTimerForHc();        
      t = GetEchoTimer();        
      lengthTemp = ((float)t/58.0);//cm
      sum = lengthTemp + sum ;
        
    }
    lengthTemp = sum/5.0;
    return lengthTemp;
}


4、控制

控制小车的代码主要在Speed_Control()函数中编写。如果不使用超声波模块的话,也可以在定时器1的中断服务函数中编写。

/*Head File*/
#ifndef _CONTROL_H
#define	_CONTROL_H

void TIM1_UP_IRQHandler(void);
void Speed_Control(unsigned char Flag,float length);
#endif
#include <control.h>
#include <Sensor.h>
#include <PWM.h>
#include <delay.h>
#include <usart.h>
#include <stdio.h>
#define STRAIGHT 0
#define LEFTS 1
#define LEFTL 2
#define RIGHTS 3
#define RIGHTL 4
#define STOP 5
#define BACK 6

extern uint8_t left0;
extern uint8_t left1;
extern uint8_t right0;
extern uint8_t right1;
extern float length;

void TIM1_UP_IRQHandler(void){
	if(TIM_GetITStatus(TIM1,TIM_IT_Update)==SET){
		
	}
	TIM_ClearITPendingBit(TIM1,TIM_IT_Update); 	
}

void Speed_Control(unsigned char Flag,float length){
	if(Flag==STOP){
		Set_LeftMotor(0);
		Set_RightMotor(0);
	}	
	
	if(length<18.0){
		if(Flag==RIGHTL){
			Set_LeftMotor(-2000);
			Set_RightMotor(2000);
		}	
		else if(Flag==LEFTL){
			Set_LeftMotor(2000);
			Set_RightMotor(-2000);
		}
		else{
			Set_LeftMotor(-2000);
			Set_RightMotor(2000);
		}
	}
}

5、主函数

#include <stm32f10x.h>
#include <timer.h>
#include <usart.h>
#include <PWM.h>
#include <sensor.h>
#include <led.h>
#include <delay.h>
#include <stdio.h>
#include <hc.h>
#include <control.h>
#include <OLED.h>
#define STRAIGHT 0
#define LEFTS 1
#define LEFTL 2
#define RIGHTS 3
#define RIGHTL 4
#define STOP 5
#define BACK 6
extern float length;

int main(){
	Led_Init();	
	Delay_Init();
	Sensor_Init();
	Hcsr04Init();
	PWM_Init(1999,71);
	TIM1_Init(1999,71);
	TIM2_Init(1999,71);
	uart_init(115200);
	OLED_Init();
	while(1){
		int flag=Sensor_Detect();
		Speed_Control(flag,Hcsr04GetLength());
	}
}

三、总结

整个工程参考了许多不同的代码(简称究极缝合怪),故代码风格不统一。由于调试过程中把红外模块撞坏了,所以控制代码没有写得很完善,但是各个模块工作正常。在加入超声波模块之前,对巡迹功能进行了测试,虽然速度比较慢,但还是能完成巡迹工作。加入超声波模块后,也能实现较高精度的障碍检测(可以多用几个超声波模块,这样避障功能更完善)。
工程链接:https://pan.baidu.com/s/1CslspOudO7mCGkAVzEmbaQ?pwd=14j9
提取码:14j9
视频演示:循迹功能展示

有关基于STM32f103c8t6的简单红外巡迹避障小车制作的更多相关文章

  1. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  2. STM32的HAL和LL库区别和性能对比 - 2

    LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L

  3. ESP32学习入门:WiFi连接网络 - 2

    目录一、ESP32简单介绍二、ESP32Wi-Fi模块介绍三、ESP32Wi-Fi编程模型四、ESP32Wi-Fi事件处理流程 五、ESP32Wi-Fi开发环境六、ESP32Wi-Fi具体代码七、ESP32Wi-Fi代码解读6.1主程序app_main7.2自定义代码wifi_init_sta()八、ESP32Wi-Fi连接验证8.1测试方法8.2服务器模拟工具sscom58.3测试代码8.4测试结果前言为了开发一款亚马逊物联网产品,开始入手ESP32模块。为了能够记录自己的学习过程,特记录如下操作过程。一、ESP32简单介绍ESP32是一套Wi-Fi(2.4GHz)和蓝牙(4.2)双模解决方

  4. Spring Security 6.0系列【32】授权服务器篇之默认过滤器 - 2

    有道无术,术尚可求,有术无道,止于术。本系列SpringBoot版本3.0.4本系列SpringSecurity版本6.0.2本系列SpringAuthorizationServer版本1.0.2源码地址:https://gitee.com/pearl-organization/study-spring-security-demo文章目录前言1.OAuth2AuthorizationServerMetadataEndpointFilter2.OAuth2AuthorizationEndpointFilter3.OidcProviderConfigurationEndpointFilter4.N

  5. ruby - 摘要::CRC32 与 Zlib - 2

    在我的代码中,我需要使用各种算法(包括CRC32)对文件进行哈希处理。因为我还在Digest系列中使用其他加密哈希函数,所以我认为为它们维护一个一致的接口(interface)会很好。为了记录,我确实找到了digest-crc,一颗完全符合我要求的gem。问题是,Zlib是标准库的一部分,并且有一个我想重用的CRC32工作实现。此外,它是用C编写的,因此它应该提供与digest-crc相关的卓越性能,后者是纯ruby​​实现。实现Digest::CRC32一开始看起来非常简单:%w(digestzlib).each{|f|requiref}classDigest::CRC32一切正常:

  6. ruby - 安装gem : Couldn't reserve space for cygwin's heap, Win32错误487错误 - 2

    我正在尝试在我的机器上安装win32-apigem,但在构建native扩展时我遇到了一些问题:$geminstallwin32-api--no-ri--rdocTemporarilyenhancingPATHtoincludeDevKit...Buildingnativeextensions.Thiscouldtakeawhile...C:\Programs\dev_kit\bin\make.exe:***Couldn'treservespaceforcygwin'sheap,Win32error0ERROR:Errorinstallingwin32-api:ERROR:Failed

  7. Ruby 1.9 - 没有这样的文件可以加载 'win32/open3' - 2

    我在Windows上运行ruby​​1.9.2并试图移植在Ruby1.8中工作的代码。该代码使用以前运行良好的Open4.popen4。对于1.9.2,我做了以下事情:通过geminstallPOpen4安装了POpen4需要POpen4通过require'popen4'尝试像这样使用POpen4:Open4.popen4("cmd"){|io_in,io_out,io_er|...}当我这样做时,我得到了错误:nosuchfiletoload--win32/open3如果我尝试安装win32-open3(geminstallwin32-open3),我会收到错误消息:win32-op

  8. Dell Inspiron 5488加内存32G - 2

    DellInspiron5488加内存32G 原装内置内存仅仅8G,目前看,真的太小了! 1.内存型号Dell5488内存型号:DDR42666。笔记本有两个内存插槽,原装占了一个,还能扩展一个。 2.买内存如果买Dell原装笔记本内存,8G就得500块左右。 我咨询了一下,三星的笔记本内存,可以兼容。16G,299块(2023年2月23日,京东价) Dell5488内存组合,最多只能插两根16G内存。 我于是买了两根三星16G内存。装上,很爽😄 跑国产系统统信UOS,再也看不到用交换区了,32G内存,爽!  

  9. 蓝桥杯 stm32 MCP4017 - 2

    本文代码使用HAL库。文章目录前言一、MCP4017的重要特性二、MCP4017计算RBW阻值三、MCP4017地址四、MCP4017读写函数五、CubeMX创建工程(利用ADC测量MCP4017电压)、对应代码:总结前言一、MCP4017的重要特性蓝桥杯板子上的是MCP4017T-104ELT,如图1。MCP4017是一个可编程电阻,通过写入的数值可以改变电阻的大小。重点在于6引脚(W),5引脚(B&#

  10. STM32 OTA应用开发——通过USB实现OTA升级 - 2

    STM32OTA应用开发——通过USB实现OTA升级目录STM32OTA应用开发——通过USB实现OTA升级前言1环境搭建2功能描述3BootLoader的制作4APP的制作5烧录下载配置6运行测试结束语前言什么是OTA?百度百科:空中下载技术(Over-the-AirTechnology;OTA),是通过移动通信的空中接口实现对移动终端设备及SIM卡数据进行远程管理的技术。经过公网多年的应用与发展,已十分成熟,网络运营商通过OTA技术实现SIM卡远程管理,还能提供移动化的新业务下载功能。实际上,现在我们所说的OTA比百度百科的定义还要更广泛,OTA的形式已经不再局限于手机和SIM卡,只要涉及

随机推荐