《基于STM32的红外避障小车》

文章目录

  • 前言
  • 1、项目简介
  • 2、硬件准备
  • 3 设计图
  • 4 各个模块介绍
    • 4.1 主控芯片STM32F103VET6介绍
    • 4.2 L298N直流电机驱动模块
      • 模块介绍
      • 模块原理
    • 4.3 红外传感器
  • 5 具体连接
  • 6 效果展示及改进建议
    • 实物展示:
    • 改进建议
  • 7 源码展示
    • `bsp_exti.c`
    • `bsp_exti.h`
    • `bsp_led.c`
    • `bsp_led.h`
    • `bsp_l2980n.c`
    • `bsp_l2980n.h`
    • `stm32f10x_it.c`
    • `main.c`


前言

本文主要讲解基于STM32的红外避障小车的实现(标准库)


1、项目简介

基于stm32实现的一个简单智能避障小车,具有“直行”、转弯、“避障”的功能。
直行、转弯:基于stm32的通用定时器TIM3输出PWM方波信号实现
避障:使用到stm32的外部中断以及通用定时器(使用红外中断),代码编写使用STM32F1的标准库

具体执行流程:红外传感器作为外部输入,它的OUT引脚默认输出高电平,当检测到障碍物时OUT引脚会输出低电平,因此会有一个高电平到低电平的变化,我们称之为“下降沿”。因此我们可以把stm32设置为“下降沿”触发外部中断这样一种机制,因此,当红外对管检测到障碍物时,stm32会被触发中断,由于我们在小车的左右两边都安装了一红外接口,当左边检测到障碍物时候,执行右转直到转弯完成,同理当右边检测到障碍物时候,执行左转直到转弯完成。最终完成避障功能

2、硬件准备

1、stm32f103vet6指南者开发板一块,用作主控芯片(这个学习用的开发板,建议后期用最小系统板子,例如stm32f103c8t6)
2、带编码盘的直流电机两个,编码盘在本项目里面没啥用
3、直流电机驱动模块(L298N直流电机驱动模块)
4、红外避障传感器(两个)
5、转向轮一个、塑料轮子两个
6、18650电池两节和电池盒(用作电源,需要降压到5V)
7、铜柱、螺丝螺母、杜邦线若干
8、电烙铁(用于后续焊接)

3 设计图

在这里插入图片描述

1:电源采用两节18650电池供电,每节4.2V,确保足够驱动两个电机,连接电机驱动模块的12V输入
2:由于STM32的要求输入的电压有限制,只允许输入电压是3.3或者5V的,因此需要采用降压模块LM2596S1将电压降到5V才可以连接到开发板上。但是由于L2980N又5V的电压输出口,因此直接将5V的电压输出口连接开发板即可
3:之后开发板通过通用定时器输出4路PWM方波信号连接电机驱动模块(L2980N),通过编写代码调整占空比对小车实现差速控制

4 各个模块介绍

4.1 主控芯片STM32F103VET6介绍

stm32f103vet6芯片具有功能各异且可复用的100个引脚并集成了如USART(通用同步/异步串行接收/发送器)、I2C (I2C总线) 和SPI (串行外设接口)等常用通信接口,可用于外接各种传感器和执行器以控制其他设备。本项目主要用到外部中断和定时器。
在这里插入图片描述

4.2 L298N直流电机驱动模块

模块介绍

在这里插入图片描述

依次介绍每个部分:从左至右,由上到下,
1、输出A:可以看到输出A和B都有两个螺丝接线,每个正好接马达的正负,如何判断是哪个是正呢?正对输出口的右边是正。
2、板载5v使能:这是个跳线帽,可选项,接上表示不用5v供电,如果拔掉的话就需要5v供电了;
3、12v供电:这个供电是必须的,我用的是两节18650充电电池。
4、供电GND:这个不用说,就是电池的负极,注意这里,单片机的GND也需要连接这个,否则马达不会转动。
5、5v供电:这个也可以作为输出口,为我们的单片机来供电。
6、通道A使能:这个是使能输出A的,也要跳线帽连接,否则A侧马达不转,个人觉得没有什么用。
7、通道B使能:同A
8、逻辑输入:这里的逻辑输入有4个引脚:IN1,IN2.IN3,IN4,由这些引脚的电平状态来控制两个马达的正转,反转,停止。详细的介绍见下表。
9、输出B:同输出A。

在这里插入图片描述
注意:我的l298n黑色部分靠近车头,得出的表格

模块原理

我们想让小车前进,转向,后退,那么就需要单片机控制这四个IN引脚的高低实现我们想要的功能。由于我使用的是通用定时器,使用定时器输出了四路PWM方波信号,分别接在IN1,IN2,IN3和IN4上,通过调节占空比实现车子的前进后退和转弯,例如要实现左转弯,则轮子倒转,右轮子正转,反之则为右转。总之,PWM主要就是用来调速的。

代码如下:

void TIM3_PWM_Init(void) 
{

	GPIO_InitTypeDef 					GPIO_InitStructer; 
	TIM_TimeBaseInitTypeDef 	TIM_TimeBaseStructer;
	TIM_OCInitTypeDef 				TIM_OCInitStructer; 

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);  //
  
	GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);
	//初始化TIM3
	TIM_TimeBaseStructer.TIM_Period = 899;
	TIM_TimeBaseStructer.TIM_Prescaler = 0;
	TIM_TimeBaseStructer.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructer.TIM_ClockDivision = 0;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructer);

	//初始化GPIOC6/GPIOC7  (TIM3_CH1/TIM3_CH2)
	GPIO_InitStructer.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;   
	GPIO_InitStructer.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructer.GPIO_Mode = GPIO_Mode_AF_PP;  
	GPIO_Init(GPIOC, &GPIO_InitStructer);
	
	//初始化GPIOC8/GPIOC9  (TIM3_CH3/TIM3_CH4)
	GPIO_InitStructer.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;  
	GPIO_InitStructer.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructer.GPIO_Mode = GPIO_Mode_AF_PP;  
	GPIO_Init(GPIOC, &GPIO_InitStructer); 

	//PWM通道一
	TIM_OCInitStructer.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructer.TIM_OutputState = TIM_OutputState_Enable;  /*比较输出使能,决定信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。*/
	TIM_OCInitStructer.TIM_OCPolarity = TIM_OCPolarity_High;  /*比较输出极性,决定定时器通道有效电平的极性。*/
	TIM_OCInitStructer.TIM_Pulse = 900;
	TIM_OC1Init(TIM3, &TIM_OCInitStructer);
	//Enables or disables the TIMx peripheral Preload register on CCR1.
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);

	//PWM通道二
	TIM_OCInitStructer.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructer.TIM_OutputState = TIM_OutputState_Enable;  /*比较输出使能,决定信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。*/
	TIM_OCInitStructer.TIM_OCPolarity = TIM_OCPolarity_High;  /*比较输出极性,决定定时器通道有效电平的极性。*/
	TIM_OCInitStructer.TIM_Pulse = 900;
	TIM_OC2Init(TIM3, &TIM_OCInitStructer);
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

	//PWM通道三
	TIM_OCInitStructer.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructer.TIM_OutputState = TIM_OutputState_Enable;  /*比较输出使能,决定信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。*/
	TIM_OCInitStructer.TIM_OCPolarity = TIM_OCPolarity_High;  /*比较输出极性,决定定时器通道有效电平的极性。*/
	TIM_OCInitStructer.TIM_Pulse = 900;
	TIM_OC3Init(TIM3, &TIM_OCInitStructer);
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

	//PWM通道四
	TIM_OCInitStructer.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructer.TIM_OutputState = TIM_OutputState_Enable;  /*比较输出使能,决定信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。*/
	TIM_OCInitStructer.TIM_OCPolarity = TIM_OCPolarity_High;  /*比较输出极性,决定定时器通道有效电平的极性。*/
	TIM_OCInitStructer.TIM_Pulse = 900;
	TIM_OC4Init(TIM3, &TIM_OCInitStructer);
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

	TIM_Cmd(TIM3, ENABLE);
}


//前进
void Go_Forward(void)
{
	//Sets the TIMx Capture Compare1 Register value
	TIM_SetCompare1(TIM3, 300);
	TIM_SetCompare2(TIM3, 900);
	TIM_SetCompare3(TIM3, 300);
	TIM_SetCompare4(TIM3, 900);
}


//停止
void Stop(void) 
{
	TIM_SetCompare1(TIM3, 900);
	TIM_SetCompare2(TIM3, 900);
	TIM_SetCompare3(TIM3, 900);
	TIM_SetCompare4(TIM3, 900);
}


//后退
void Go_Back(void)
{
	TIM_SetCompare1(TIM3, 900);
	TIM_SetCompare2(TIM3, 300);
	TIM_SetCompare3(TIM3, 900);
	TIM_SetCompare4(TIM3, 300);
}


//左转
void Turn_Left(void) 
{
	TIM_SetCompare1(TIM3, 900);
	TIM_SetCompare2(TIM3, 350);
	TIM_SetCompare3(TIM3, 350);
	TIM_SetCompare4(TIM3, 900);
}


//右转
void Turn_Right(void) 
{
	TIM_SetCompare1(TIM3, 350);
	TIM_SetCompare2(TIM3, 900);
	TIM_SetCompare3(TIM3, 900);
	TIM_SetCompare4(TIM3, 350);
}

4.3 红外传感器

模块原理:器件有三个引脚,VCC和GND,OUT就是输出信号用的,其实输出的就是高低电平两种状态(高电平为3.3V,低电平为0V;高电平读出来就是“1”,低电平读出来就是“0”)。
这个器件上电以后,OUT引脚默认输出高电平,当检测到障碍物时OUT引脚会输出低电平,因此会有一个高电平到低电平的变化,我们称之为“下降沿”。我们可以把stm32设置为“下降沿”触发外部中断这样一种机制,因此,当红外对管检测到障碍物时,stm32会被触发中断,从而去执行我们事先编写好的一段程序。
blog.csdnimg.cn/e83916153e954c3f9b415c3e6bf8ee4d.png)
代码如下:

#include "bsp_exti.h"
#include "bsp_usart.h"

static void EXTI_INFRAREDAVOID_NVIC_Config(void)  //static限制这个函数只能被EXTI_Key_Config()调用
{
	NVIC_InitTypeDef		NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//红外1中断
	NVIC_InitStructure.NVIC_IRQChannel=INFRAREDAVOID1_INT_EXTI_IRQ;	
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	//红外2中断
	 NVIC_InitStructure.NVIC_IRQChannel = INFRAREDAVOID2_INT_EXTI_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}


//外部中断红外1配置
void EXTI_INFRAREDAVOID1_Config(void)
{
	GPIO_InitTypeDef		GPIO_InitStructure;		//	定义初始化结构体
	EXTI_InitTypeDef		EXTI_InitStructure;
	
	//配置中断优先级
	EXTI_INFRAREDAVOID_NVIC_Config();
/*配置红外*/	
	//初始化用于中断的GPIO(具体的外设)
	RCC_APB2PeriphClockCmd(INFRAREDAVOID1_INT_GPIO_CLK,ENABLE);  //开红外的时钟
	
	
	GPIO_InitStructure.GPIO_Pin=INFRAREDAVOID1_INT_GPIO_PIN;		//选择GPIO要控制的引脚4
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_Init(INFRAREDAVOID1_INT_GPIO_PORT,&GPIO_InitStructure);	
	
	//初始化EXTI(具体的外设)
	GPIO_EXTILineConfig(INFRAREDAVOID1_INT_EXTI_PortSource,INFRAREDAVOID1_INT_EXTI_PinSource); //配置输入源
	
	EXTI_InitStructure.EXTI_Line=INFRAREDAVOID1_INT_EXTI_LINE;   //因为是PA4,所以是Line4
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;     //配置为下降沿触发中断
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_Init(&EXTI_InitStructure);     //把结构体成员写到相应的寄存器里
}



//外部中断红外2配置
void EXTI_INFRAREDAVOID2_Config(void)
{
	GPIO_InitTypeDef		GPIO_InitStructure;		//	定义初始化结构体
	EXTI_InitTypeDef		EXTI_InitStructure;
	
	//配置中断优先级
	EXTI_INFRAREDAVOID_NVIC_Config();
/*配置红外*/	
	//初始化用于中断的GPIO(具体的外设)
	RCC_APB2PeriphClockCmd(INFRAREDAVOID2_INT_GPIO_CLK,ENABLE);  //开红外的时钟
	
	
	GPIO_InitStructure.GPIO_Pin=INFRAREDAVOID2_INT_GPIO_PIN;		//选择GPIO要控制的引脚4
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;     
	GPIO_Init(INFRAREDAVOID2_INT_GPIO_PORT,&GPIO_InitStructure);	
	
	//初始化EXTI(具体的外设)
	GPIO_EXTILineConfig(INFRAREDAVOID2_INT_EXTI_PortSource,INFRAREDAVOID2_INT_EXTI_PinSource); //配置输入源
	
	EXTI_InitStructure.EXTI_Line=INFRAREDAVOID2_INT_EXTI_LINE;   //因为是PA4,所以是Line4
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;     //配置为下降沿触发中断
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_Init(&EXTI_InitStructure);     //把结构体成员写到相应的寄存器里
}

5 具体连接

  • 1、电源:将两节电池串联,电源的正极连接L2980N的12V输入,电源的负极连接L2980N的GND。

  • 2:接下来是STM32的连接

    • 红外中断:由于我使用的是外部中断3和外部中断4,对应的IO口分别是PA3和PC4,因此将PA3->OUT, 3.3V->VCC, GND->DND。同理PC4->OUT, 3.3V->VCC, GND->DND
    • L2980N:上面有L2980N的介绍,这里就不多赘述了。我使用的是通用定时器TIM3的4个通道。CH1的PC6口,CH2的PC7口,CH3的PC8口,CH4的PC9口。
      • 12V供电->电源正极
      • GND->电源负极和开发板GND
      • 5V供电->开发板的VCC
      • 5V供电->开发板的VCC
      • 逻辑输入分别接开发板的PC6,PC7,PC8,PC9
      • 逻辑输出A连接电机分别连接电机的正负极,正对输出口的右边是正
      • 逻辑输出B连接电机分别连接电机的正负极,正对输出口的右边是正
      • 通道A使能:这个是使能输出A的,也要跳线帽连接,否则A侧马达不转,个人觉得没有什么用一开始就连接好了,不用动。
      • 通道B使能:这个是使能输出B的,也要跳线帽连接否则A侧马达不转,个人觉得没有什么用,,一开始就连接好了,不用动。

连接图如下:
在这里插入图片描述

6 效果展示及改进建议

实物展示:

在这里插入图片描述

改进建议

  • 1:使用超声波模块代替红外模块
    • 由于红外传感器会受到光线和距离的限制,从而会导致最后实验的结果不是特别理想尽管能达到简单避障效果,但是还有待提高。建议后续使用超声波模块hc-sr04代替红外模块。超声波的检测距离长,且稳定性十分好。

超声波模块原理:用声音在空气中传播并且遇到障碍物会反射的效果,再根据声音在空气中的传播速度,计算出前方的距离。

  • 2:使用舵机模块SG90
    • 既然碰到障碍物要转弯,那到底转多少角度呢?使用舵机模块就可以很好的解决这一问题。

舵机模块原理

  1. 其实舵机可以分为两种,一种是模拟舵机,一种是数字舵机,这两者的区别是:模拟舵机需要一直给与要转的角度命令,直到到自己想要的角度,注意这个给定的时间许多不许少,就像是小孩子一样,你要不断的给与鼓励和奖赏,才会达到自己的要求;而数字舵机是只需要给定一次角度命令就行了,就像是长大了的孩子,你可以把事情很放心的交给他,说一次就好。
  2. 说了半天,那么角度命令是什么呢?其实就是我们熟悉的pwm信号,下面给出占空比与旋转的角度之间的关系:
    在这里插入图片描述

注意:
1)、高电平加上低电平等于 20ms,那是因为要求的pwm频率是50hz,1s/50 = 20ms,所以周期就是20ms。
2)、角度怎么看,想象自己站在xy轴的0坐标上,正对着y轴,y轴就是0度。负度在左手边,正度在右手边。
3)、其实低电平的时间并没有那么死板,只要在0.5ms和20ms之间就可以。

7 源码展示

我是使用野火的指南者板子,用的是标准库首先创建文件

bsp_exti.c: 用于保存红外中断函数代码
bsp_exti.h: 用于保存红外中断函数的声明
bsp_led.c: 用于保存开发板上的LED灯函数的代码,我们要实现红外模块遇到障碍物触发中断,亮红灯。
bsp_led.h: 用于保存开发板上的LED灯函数的声明的代码,我们要实现红外模块遇到障碍物触发中断,亮红灯
bsp_l2980n.c: 用于保存电机驱动模块函数的代码(通用定时器输出4路PWM方波信号)
bsp_l2980n.h: 用于保存电机驱动模块函数声明的代码
stm32f10x.c:这个文件库函数本来就有,主要保存我们自己写的中断服务函数(触发中断后要让单片机干啥事情)
main.c:主函数

完整源码如下:可直接跑的哦

bsp_exti.c

#include "bsp_exti.h"

static void EXTI_INFRAREDAVOID_NVIC_Config(void)  //static限制这个函数只能被EXTI_Key_Config()调用
{
	NVIC_InitTypeDef		NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//红外1中断
	NVIC_InitStructure.NVIC_IRQChannel=INFRAREDAVOID1_INT_EXTI_IRQ;	
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	//红外2中断
	 NVIC_InitStructure.NVIC_IRQChannel = INFRAREDAVOID2_INT_EXTI_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}


//外部中断红外1配置
void EXTI_INFRAREDAVOID1_Config(void)
{
	GPIO_InitTypeDef		GPIO_InitStructure;		//	定义初始化结构体
	EXTI_InitTypeDef		EXTI_InitStructure;
	
	//配置中断优先级
	EXTI_INFRAREDAVOID_NVIC_Config();
/*配置红外*/	
	//初始化用于中断的GPIO(具体的外设)
	RCC_APB2PeriphClockCmd(INFRAREDAVOID1_INT_GPIO_CLK,ENABLE);  //开红外的时钟
	
	
	GPIO_InitStructure.GPIO_Pin=INFRAREDAVOID1_INT_GPIO_PIN;		//选择GPIO要控制的引脚4
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_Init(INFRAREDAVOID1_INT_GPIO_PORT,&GPIO_InitStructure);	
	
	//初始化EXTI(具体的外设)
	GPIO_EXTILineConfig(INFRAREDAVOID1_INT_EXTI_PortSource,INFRAREDAVOID1_INT_EXTI_PinSource); //配置输入源
	
	EXTI_InitStructure.EXTI_Line=INFRAREDAVOID1_INT_EXTI_LINE;   //因为是PA4,所以是Line4
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;     //配置为下降沿触发中断
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_Init(&EXTI_InitStructure);     //把结构体成员写到相应的寄存器里
}



//外部中断红外2配置
void EXTI_INFRAREDAVOID2_Config(void)
{
	GPIO_InitTypeDef		GPIO_InitStructure;		//	定义初始化结构体
	EXTI_InitTypeDef		EXTI_InitStructure;
	
	//配置中断优先级
	EXTI_INFRAREDAVOID_NVIC_Config();
/*配置红外*/	
	//初始化用于中断的GPIO(具体的外设)
	RCC_APB2PeriphClockCmd(INFRAREDAVOID2_INT_GPIO_CLK,ENABLE);  //开红外的时钟
	
	
	GPIO_InitStructure.GPIO_Pin=INFRAREDAVOID2_INT_GPIO_PIN;		//选择GPIO要控制的引脚4
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;     
	GPIO_Init(INFRAREDAVOID2_INT_GPIO_PORT,&GPIO_InitStructure);	
	
	//初始化EXTI(具体的外设)
	GPIO_EXTILineConfig(INFRAREDAVOID2_INT_EXTI_PortSource,INFRAREDAVOID2_INT_EXTI_PinSource); //配置输入源
	
	EXTI_InitStructure.EXTI_Line=INFRAREDAVOID2_INT_EXTI_LINE;   //因为是PA4,所以是Line4
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;     //配置为下降沿触发中断
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_Init(&EXTI_InitStructure);     //把结构体成员写到相应的寄存器里
}

bsp_exti.h

#ifndef _BSP_EXTI_H
#define _BSP_EXTI_H

#include "stm32f10x.h"

//infrared红外    obstacle avoidance避障
//红外1
#define		INFRAREDAVOID1_INT_GPIO_PIN											GPIO_Pin_3  
#define		INFRAREDAVOID1_INT_GPIO_PORT											GPIOA
#define		INFRAREDAVOID1_INT_GPIO_CLK											(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO |RCC_APB2Periph_GPIOB)

#define		INFRAREDAVOID1_INT_EXTI_PortSource								GPIO_PortSourceGPIOA
#define		INFRAREDAVOID1_INT_EXTI_PinSource								GPIO_PinSource3
#define		INFRAREDAVOID1_INT_EXTI_LINE											EXTI_Line3
#define		INFRAREDAVOID1_INT_EXTI_IRQ											EXTI3_IRQn

#define		INFRAREDAVOID1_IRQHandler												EXTI3_IRQHandler

//红外2
#define		INFRAREDAVOID2_INT_GPIO_PIN											GPIO_Pin_4 
#define		INFRAREDAVOID2_INT_GPIO_PORT										GPIOC
#define		INFRAREDAVOID2_INT_GPIO_CLK											(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO |RCC_APB2Periph_GPIOB) 

#define		INFRAREDAVOID2_INT_EXTI_PortSource							GPIO_PortSourceGPIOC
#define		INFRAREDAVOID2_INT_EXTI_PinSource								GPIO_PinSource4
#define		INFRAREDAVOID2_INT_EXTI_LINE										EXTI_Line4
#define		INFRAREDAVOID2_INT_EXTI_IRQ											EXTI4_IRQn

#define		INFRAREDAVOID2_IRQHandler												EXTI4_IRQHandler

void EXTI_INFRAREDAVOID1_Config(void);
void EXTI_INFRAREDAVOID2_Config(void);
#endif 

bsp_led.c

#include "bsp_led.h"

void LED_GPIO_Config(void)
{
	GPIO_InitTypeDef		GPIO_InitStructure;		//	定义初始化结构体
	
	
	RCC_APB2PeriphClockCmd(LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK, ENABLE);  //开灯时钟	
	
	GPIO_InitStructure.GPIO_Pin=LED1_GPIO_PIN;		//选择GPIO要控制的引脚0
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;	
	GPIO_Init(LED1_GPIO_PORT,&GPIO_InitStructure);	

	GPIO_InitStructure.GPIO_Pin=LED2_GPIO_PIN;		//选择GPIO要控制的引脚1
	GPIO_Init(LED2_GPIO_PORT,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin=LED3_GPIO_PIN;		//选择GPIO要控制的引脚5
	GPIO_Init(LED3_GPIO_PORT,&GPIO_InitStructure);
	

	
	//熄灭所有灯
	GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN); 
	GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN); 
	GPIO_SetBits(LED3_GPIO_PORT,LED3_GPIO_PIN); 
}


bsp_led.h

#ifndef _BSP_LED_H
#define	_BSP_LED_H

#include	"stm32f10x.h"

/* 定义LED连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
// G-绿色
#define LED1_GPIO_PORT    	GPIOB			              /* GPIO端口 */
#define LED1_GPIO_CLK 	    RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define LED1_GPIO_PIN				GPIO_Pin_0			        /* 连接到SCL时钟线的GPIO */

// B-蓝色
#define LED2_GPIO_PORT    	GPIOB			              /* GPIO端口 */
#define LED2_GPIO_CLK 	    RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define LED2_GPIO_PIN				GPIO_Pin_1			        /* 连接到SCL时钟线的GPIO */

// R-红色
#define LED3_GPIO_PORT    	GPIOB			              /* GPIO端口 */
#define LED3_GPIO_CLK 	    RCC_APB2Periph_GPIOB		/* GPIO端口时钟 */
#define LED3_GPIO_PIN				GPIO_Pin_5			        /* 连接到SCL时钟线的GPIO */

#define		ON		1
#define		OFF		0	

#define		LED1_G(a)		if(a)	\
														GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);\
											else	GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);

#define		LED2_B(a)		if(a)	\
														GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);\
											else	GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);

#define		LED3_R(a)		if(a)	\
														GPIO_ResetBits(LED3_GPIO_PORT,LED3_GPIO_PIN);\
											else	GPIO_SetBits(LED3_GPIO_PORT,LED3_GPIO_PIN);

#define LED1_TOGGLE		{LED1_GPIO_PORT->ODR ^=LED1_GPIO_PIN;} //绿灯状态翻转
#define LED2_TOGGLE		{LED2_GPIO_PORT->ODR ^=LED2_GPIO_PIN;} //蓝灯状态翻转
#define LED3_TOGGLE		{LED3_GPIO_PORT->ODR ^=LED3_GPIO_PIN;} //红灯状态翻转


void LED_GPIO_Config(void);
#endif

bsp_l2980n.c

#include "bsp_l298n.h"
void TIM3_PWM_Init(void) 
{

	GPIO_InitTypeDef 					GPIO_InitStructer; 
	TIM_TimeBaseInitTypeDef 	TIM_TimeBaseStructer;
	TIM_OCInitTypeDef 				TIM_OCInitStructer; 

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);  //
  
	GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);
	//初始化TIM3
	TIM_TimeBaseStructer.TIM_Period = 899;
	TIM_TimeBaseStructer.TIM_Prescaler = 0;
	TIM_TimeBaseStructer.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructer.TIM_ClockDivision = 0;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructer);

	//初始化GPIOC6/GPIOC7  (TIM3_CH1/TIM3_CH2)
	GPIO_InitStructer.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;   
	GPIO_InitStructer.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructer.GPIO_Mode = GPIO_Mode_AF_PP;  
	GPIO_Init(GPIOC, &GPIO_InitStructer);
	
	//初始化GPIOC8/GPIOC9  (TIM3_CH3/TIM3_CH4)
	GPIO_InitStructer.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;  
	GPIO_InitStructer.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructer.GPIO_Mode = GPIO_Mode_AF_PP;  
	GPIO_Init(GPIOC, &GPIO_InitStructer); 

	//PWM通道一
	TIM_OCInitStructer.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructer.TIM_OutputState = TIM_OutputState_Enable;  /*比较输出使能,决定信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。*/
	TIM_OCInitStructer.TIM_OCPolarity = TIM_OCPolarity_High;  /*比较输出极性,决定定时器通道有效电平的极性。*/
	TIM_OCInitStructer.TIM_Pulse = 900;
	TIM_OC1Init(TIM3, &TIM_OCInitStructer);
	//Enables or disables the TIMx peripheral Preload register on CCR1.
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);

	//PWM通道二
	TIM_OCInitStructer.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructer.TIM_OutputState = TIM_OutputState_Enable;  /*比较输出使能,决定信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。*/
	TIM_OCInitStructer.TIM_OCPolarity = TIM_OCPolarity_High;  /*比较输出极性,决定定时器通道有效电平的极性。*/
	TIM_OCInitStructer.TIM_Pulse = 900;
	TIM_OC2Init(TIM3, &TIM_OCInitStructer);
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

	//PWM通道三
	TIM_OCInitStructer.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructer.TIM_OutputState = TIM_OutputState_Enable;  /*比较输出使能,决定信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。*/
	TIM_OCInitStructer.TIM_OCPolarity = TIM_OCPolarity_High;  /*比较输出极性,决定定时器通道有效电平的极性。*/
	TIM_OCInitStructer.TIM_Pulse = 900;
	TIM_OC3Init(TIM3, &TIM_OCInitStructer);
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

	//PWM通道四
	TIM_OCInitStructer.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructer.TIM_OutputState = TIM_OutputState_Enable;  /*比较输出使能,决定信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。*/
	TIM_OCInitStructer.TIM_OCPolarity = TIM_OCPolarity_High;  /*比较输出极性,决定定时器通道有效电平的极性。*/
	TIM_OCInitStructer.TIM_Pulse = 900;
	TIM_OC4Init(TIM3, &TIM_OCInitStructer);
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

	TIM_Cmd(TIM3, ENABLE);
}


//前进
void Go_Forward(void)
{
	//Sets the TIMx Capture Compare1 Register value
	TIM_SetCompare1(TIM3, 300);
	TIM_SetCompare2(TIM3, 900);
	TIM_SetCompare3(TIM3, 300);
	TIM_SetCompare4(TIM3, 900);
}


//停止
void Stop(void) 
{
	TIM_SetCompare1(TIM3, 900);
	TIM_SetCompare2(TIM3, 900);
	TIM_SetCompare3(TIM3, 900);
	TIM_SetCompare4(TIM3, 900);
}


//后退
void Go_Back(void)
{
	TIM_SetCompare1(TIM3, 900);
	TIM_SetCompare2(TIM3, 300);
	TIM_SetCompare3(TIM3, 900);
	TIM_SetCompare4(TIM3, 300);
}


//左转
void Turn_Left(void) 
{
	TIM_SetCompare1(TIM3, 900);
	TIM_SetCompare2(TIM3, 350);
	TIM_SetCompare3(TIM3, 350);
	TIM_SetCompare4(TIM3, 900);
}


//右转
void Turn_Right(void) 
{
	TIM_SetCompare1(TIM3, 350);
	TIM_SetCompare2(TIM3, 900);
	TIM_SetCompare3(TIM3, 900);
	TIM_SetCompare4(TIM3, 350);
}

bsp_l2980n.h

#ifndef __L298N_H
#define __L298N_H

#include	"stm32f10x.h"


void TIM3_PWM_Init(void);
void Go_Forward(void);
void Go_Back(void);
void Stop(void);
void Turn_Left(void);
void Turn_Right(void);

#endif /*__L298N_H*/


stm32f10x_it.c

#include "stm32f10x_it.h"
#include "bsp_led.h"
#include "bsp_exti.h"
#include "bsp_l298n.h"
//EXTI3_IRQHandler中断服务函数
void INFRAREDAVOID1_IRQHandler(void)
{
	if(EXTI_GetITStatus(INFRAREDAVOID1_INT_EXTI_LINE) !=RESET)
	{		 
		LED3_R(1);
	//	Stop() ;
		flag1=1;
		Turn_Right();	
		EXTI_ClearITPendingBit(INFRAREDAVOID1_INT_EXTI_LINE);				
	}	


}


//EXTI4_IRQHandler中断服务函数
void INFRAREDAVOID2_IRQHandler(void)
{
 
	if(EXTI_GetITStatus(INFRAREDAVOID2_INT_EXTI_LINE) !=RESET)
	{		 
		LED3_R(1);
		flag2=1;
	//	Stop() ;
		Turn_Left();		
		EXTI_ClearITPendingBit(INFRAREDAVOID2_INT_EXTI_LINE);	
	}
}

main.c

#include	"stm32f10x.h"
#include 	"bsp_led.h"
#include "bsp_exti.h"
#include "bsp_l298n.h"

extern uint16_t flag1;
extern uint16_t flag2;


int main(void)
{
	LED_GPIO_Config();
	EXTI_INFRAREDAVOID1_Config();
	EXTI_INFRAREDAVOID2_Config();
	TIM3_PWM_Init();
	Go_Forward();
	while(1)
	{
		
			if(flag1 == 1)
			{
				if(GPIO_ReadInputDataBit(INFRAREDAVOID1_INT_GPIO_PORT,INFRAREDAVOID1_INT_GPIO_PIN) == 1) //外部中断跳回0
				{
					flag1 = 0;
					LED3_R(0);
					Go_Forward();
				}
			}
			if(flag2 == 1)
			{
				if(GPIO_ReadInputDataBit(INFRAREDAVOID2_INT_GPIO_PORT,INFRAREDAVOID2_INT_GPIO_PIN) == 1) //外部中断跳回0
				{
					flag2 = 0;
					LED3_R(0);
					Go_Forward();
				}
			}
	}
}



以上就是本项目需要手写的全部代码了,觉得不错的记得一键三连哦!有问题也可私聊我哦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/48265.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单分享婚宴预订小程序怎么做

婚宴预订小程序需要具备一些功能,通过这些功能,新人可以更方便地选择婚宴场地、预订服务,并且更好地规划自己的婚礼。 1. 场地浏览与选择 婚宴预订小程序可以展示多个婚宴场地的照片和详细信息,包括容纳人数、场地设施、价格等。…

three.js入门二:相机的zoom参数

环境: threejs:129 (在浏览器的控制台下输入: window.__THREE__即可查看版本)vscodewindowedge 透视相机或正交相机都有一个zoom参数,它可以用来将相机排到的内容在canvas上缩放显示。 要点:…

ClickHouse(一):ClickHouse介绍及OLAP场景特征

目录 1. ClickHouse与其特性 ​​​​​​​2. 什么是ClickHouse ​​​​​​​3. OLAP场景的特征 进入正文前,感谢宝子们订阅专题、点赞、评论、收藏!关注IT贫道,获取高质量博客内容! ​​​​​​​1. ClickHouse与其特性 …

SpringBoot使用Redis对用户IP进行接口限流

使用接口限流的主要目的在于提高系统的稳定性&#xff0c;防止接口被恶意打击&#xff08;短时间内大量请求&#xff09;。 一、创建限流注解 引入redis依赖 <!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId&g…

【计网】一起聊聊TCP的粘包拆包问题吧

文章目录 1、介绍2、为什么会出现粘包/拆包问题2.1、TCP协议2.2、粘包问题2.3、拆包问题 3、粘包/拆包场景4、解决方案4.1、固定长度的数据包4.2、特殊字符或标记4.3、消息头 5、为什么UDP没有粘包/拆包问题 1、介绍 在TCP中&#xff0c;粘包和拆包问题是十分常见的&#xff0…

Vue3 Radio单选切换展示不同内容

Vue3 Radio单选框切换展示不同内容 环境&#xff1a;vue3tsviteelement plus 技巧&#xff1a;v-if&#xff0c;v-show的使用 实现功能&#xff1a;点击单选框展示不同的输入框 效果实现前的代码&#xff1a; <template><div class"home"><el-row …

【Docker】Consul的容器服务更新与发现

目录 一、Consul二、什么是服务注册与发现1.2什么是consul1.3consul提供的一些关键特性 二、Consul部署2.1环境配置2.2Consul服务器配置1. 建立 Consul 服务2. 查看集群信息3. 通过 http api 获取集群信息 2.3 registrator服务器配置1. 安装 Gliderlabs/Registrator2. 测试服务…

【多模态】17、CORA | 将 CLIP 使用到开集目标检测

文章目录 一、背景二、方法2.1 总体结构2.2 region prompting2.3 anchor pre-matching 三、效果 论文&#xff1a;CORA: Adapting CLIP for Open-Vocabulary Detection with Region Prompting and Anchor Pre-Matching 代码&#xff1a;https://github.com/tgxs002/CORA 出处…

JGJ46-2005施工现场临时用电安全技术规范

为贯彻国家安全生产的法律和法规&#xff0c;保障施工现场用电安全&#xff0c;防止触电和电气火灾事故发生&#xff0c;促进建设事业发展&#xff0c;制定本规范。 本规范适用于新建、改建和扩建的工业与民用建筑和市政基础设施施工现场临时用电工程中的电源中性点直接接地的…

【SpringCloud Alibaba】(四)使用 Feign 实现服务调用的负载均衡

在上一文中&#xff0c;我们实现了服务的自动注册与发现功能。但是还存在一个很明显的问题&#xff1a;如果用户微服务和商品微服务在服务器上部署多份的话&#xff0c;之前的程序无法实现服务调用的负载均衡功能。 本文就带着大家一起实现服务调用的负载均衡功能 1. 负载均衡…

实用便捷!一站式BI系统推荐

在企业数字化转型过程中&#xff0c;BI系统可以建立业务、数据的双驱引擎&#xff0c;形成业务、数据的互补作用&#xff0c;通过建立数字化技术架构&#xff0c;明确企业的战略定位和业务目标&#xff0c;从而支撑实现这个目标。而一站式BI系统&#xff0c;则是指可以轻松从数…

数据结构:顺序表(C实现)

个人主页 水月梦镜花 个人专栏 C语言 &#xff0c;数据结构 文章目录 一、顺序表二、实现思路1.存储结构2.初始化顺序表(SeqListInit)3.销毁顺序表(SeqListDestroty)4.打印顺序表(SeqListPrint)5.顺序表尾插(SeqListPushBack)and检查容量(SeqListCheckCapacity)6.顺序表头插(Se…

Excel 两列数据中相同的数据进行同行显示

一、要求 假设您有两个列&#xff0c;分别是A列和B列&#xff0c;需要在C列中找出A列对应的B列的值。 二、方案 方法1&#xff1a;寻常思路 凸显重复项对A列单独进行筛选–按颜色进行排序&#xff0c;然后升序对B列重复上述操作即可 方法2&#xff1a;两个公式 VLOOKUP 纵向查找…

Python计算统计分析MSE 、RMSE、MAE、R2

1、平均绝对误差 (MAE)Mean Absolute Error&#xff0c;是绝对误差的平均值&#xff0c;能更好地反映预测值误差的实际情况。范围[0,∞)&#xff0c;当预测值与真实值完全吻合时等于0&#xff0c;即完美模型&#xff1b;误差越大&#xff0c;该值越大。 2、均方误差 MSE(mean…

VBA技术资料MF34:检查Excel自动筛选是否打开

【分享成果&#xff0c;随喜正能量】聪明人&#xff0c;抬人不抬杠&#xff1b;傻子&#xff0c;抬杠不抬人。聪明人&#xff0c;把别人抬得很高&#xff0c;别人高兴、舒服了&#xff0c;看你顺眼了&#xff0c;自然就愿意帮你&#xff01;而傻人呢&#xff1f;不分青红皂白&a…

【FAQ】关于无法判断和区分用户与地图交互手势类型的解决办法

一&#xff0e; 问题描述 当用户通过缩放手势、平移手势、倾斜手势和旋转手势与地图交互&#xff0c;控制地图移动改变其可见区域时&#xff0c;华为地图SDK没有提供直接获取用户手势类型的API。 二&#xff0e; 解决方案 华为地图SDK的地图相机有提供CameraPosition类&…

Day_71-76 BP 神经网络

目录 一. 基础概念理解 1. 一点个人理解 2. 神经网络 二. bp神经网络的局部概念 1. 神经元 2. 激活函数 三. bp神经网络的过程 1. 算法流程图 2. 神经网络基础架构 2.1 正向传播过程 2.2 反向传播过程&#xff08;算法核心&#xff09; 四. 基本bp神经网络的代码实现 1. 抽象…

1300*B. T-primes

解析&#xff1a; 有且只有三个因数&#xff0c;当且仅当&#xff0c;完全平方数并且sqrt&#xff08;n&#xff09;为素数 #include<bits/stdc.h> using namespace std; typedef long long ll; const int N1e55; ll t,n; bool prime(ll x){if(x<2) return 0;for(int…

Idea 结合docker-compose 发布项目

Idea 结合docker-compose 发布项目 这里写目录标题 Idea 结合docker-compose 发布项目Docker 开启远程访问功能 添加相应端口配置IDEA 链接Docker配置项目 docker-compose.yml本地还需要安装 dockerwin11 安装本地Docker 可能存在问题 Linux内核不是最新 Docker 开启远程访问功…

使用langchain与你自己的数据对话(二):向量存储与嵌入

之前我以前完成了“使用langchain与你自己的数据对话(一)&#xff1a;文档加载与切割”这篇博客&#xff0c;没有阅读的朋友可以先阅读一下&#xff0c;今天我们来继续讲解deepleaning.AI的在线课程“LangChain: Chat with Your Data”的第三门课&#xff1a;向量存储与嵌入。 …