方舟计划——智能夹取小车(电控)

目录

一、准备

1、整体思路

2、物料

二、电控部分具体操作

1、蓝牙模块

(1)、蓝牙模块调试

(2)、蓝牙模块与手机通信

 (3)、蓝牙模块控制板上模块(蓝牙使用串口与开发板通信)

2、L298N电机驱动模块

(1)、基础知识

(2)、麦克纳姆轮控制

3、电推杆控制

(1)、基础知识

4、同步带

5、启动/停止按钮

(1)、设计

(2)、中断基础知识

6、LCD显示屏

7、视觉识别

8、整体系统

(1)、GPIO

(2)、底盘

(3)、整体


一、准备

1、整体思路

正点原子精英板做主控板,树莓派进行视觉识别,并通过串口与主控板通信

使用正点原子的LCD屏幕显示树莓派发过来的结果

车上要有一个启动停止按钮,使用按钮中断控制一个按钮

底盘由四个L298N驱动四个直流减速电机,四个直流减速电机驱动四个麦克纳姆轮,通过PWM调速,进行运动

车子使用同步带进行升降,同步带由一个直流减速电机加上联轴器带动,直流减速电机还是用L298N驱动

车子夹取用一个横着的电推杆与一个固定的贴片,电推杆内部也是一个直流减速电机,直接用L298N驱动即可

车子的操控使用一个蓝牙模块,蓝牙模块使用串口与板子通信,进行信息传输,把我使用串口调试助手发送的信息发给主控板

2、物料

正点原子精英板、蓝牙模块、l298n电机驱动模块,12v电源、直流减速电机、电推杆、杜邦线、按钮模块、树莓派、安装工具.......

二、电控部分具体操作

1、蓝牙模块

(1)、蓝牙模块调试

hc05蓝牙模块要使用到USB转TTL模块进行调试(该模块需要使用到ch340串口驱动),

而正点原子的战舰和精英开发板的该模块都与使用跳帽与usart1的PA9、PA10相连,所以使用USB转TTL模块时,需要将跳帽拔下,将蓝牙的TXD、RXD与板子上的TXD、RXD交错相接。

注意不是与PA9、PA10相接

模块接5v

问题:

  • 但是不知道为什么我的蓝牙模块与正点原子精英板按照这个方式连接后,电脑无法识别到我的蓝牙,但是直接与USB转TTL模块相接后却可以识别到,可能是精英板我有什么东西不了解

解决:

  • 精英板上的USB转TTL模块的USB不是j-link,要自己额外连接USB,把USB转TTL与电脑接起来

(2)、蓝牙模块与手机通信

将蓝牙模块与USB转TTL模块正确连接后,可以使用串口调试助手与电脑通信,蓝牙模块具有俩种工作模式:9透传模式(下分主模式、从模式、回环模式)、AT模式(俩种方法进入AT模式,效果不同)

蓝牙与手机连接前AT操作配置:

AT+ROLE=0(设置为从机模式)

AT+CMODE=1(设置为任意地址连接)

AT+NAME?(查询名称)

AT+PSWD?(查询密码)

AT+RESET(重启蓝牙,进入透传)

AT模式蓝牙默认的波特率为38400,透传为9600,注意通信时要设置对波特率,否者传输的数据会乱码

问题

1、连接后可以使用AT指令与蓝牙模块进行交互,但是手机上找不到蓝牙模块

初步设想是有没有可能是购买的蓝牙模块是坏的、是不是蓝牙模式还需要调试什么参数手机才能找的到(比如说波特率、主从模式)

换了个蓝牙,手机可以配对到,但是无法连接

要进入蓝牙调试软件才能链接,但是我的软件好像有问题,换了软件(名称:蓝牙调试器)后连接成功,成功通信

 (3)、蓝牙模块控制板上模块(蓝牙使用串口与开发板通信)

串口GPIO及总线分布

 串口常用库函数

 串口使用步骤

 如果要开发板随时接收到串口发来的信息并且及时处理,一般要使用串口中断而不是轮询

蓝牙串口初始化

#include "bule.h"

 /**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
	/* 提示 NVIC_PriorityGroupConfig() 在整个工程只需要调用一次来配置优先级分组*/
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = 
	USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}


串口中断函数

// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{

	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE) != RESET)//获取状态标志位
	{
		rx_data = USART_ReceiveData(DEBUG_USARTx);   //接收到的数据放入rx_data
        rx_end = 1;
	}
	USART_ClearITPendingBit(DEBUG_USARTx,USART_IT_RXNE);   //清除中断待处理位

}

问题:

写好蓝牙串口中断程序后,手机成功链接蓝牙,并先蓝牙发送信息,板子没有进入串口中断

但是串口调试助手收到了信息,板子没有检测到中断

想着蓝牙连接板子上的串口(USB转TTL),串口连接着电脑,如果串口调试助手即电脑可以收到信息,证明硬件连接没错

发现思路错误,蓝牙通过板子上的USB转TTL模块连到电脑,但是这个USB转TTL模块是没有与板子有连接的(原本是通过跳帽连接PA9、PA10),但是为了使用USB转TLL模块,我拔了跳帽,此时的连接相当于我只是用一个单独的USB转TTL模块去调试蓝牙,跟第一步一样

正确连接的方式是把蓝牙的RX、TX与板子上的PA9、PA10相连,做到了蓝牙的输入输出与板子的GPIO相连,进行通信,而不只是与USB转TTL相连。

控制逻辑及软件

2、L298N电机驱动模块

注意L298N要接隔离电路,不然容易产生反向电流把板子烧坏(我就是因为没有加隔离电路,烧了一块板子)

L298N芯片可以驱动两个二相电机,也可以驱动一个四相电机,输出电压最高可达50V,可以直接通过电源来调节输出电压;可以直接用单片机的IO口提供信号;而且电路简单,使用比较方便。

L298N可接受标准TTL逻辑电平信号VSS,VSS可接4.5~7V电压。4脚VS接电源电压,VS电压范围VIH为+2.5~46V。输出电流可达2A,可驱动电感性负载。1脚和15脚下管的发射极分别单独引出以便接入电流采样电阻,形成电流传感信号。

(1)、基础知识

1、l298n供电部分由12V供电、供地GND、5V供电、和板载5V使能(接了一个跳帽)组成。

如果板载5V使能那个跳线帽安上,那么就不需要给驱动模块单独5V供电,只需要12V供电就行。一般来说会使用这种方式。同时在跳帽按上后,5V端子将成为5V输出,可以给单片机供电,但是不建议。(注意:如果您提供的电压超过12V,则需要卸下跳线,并向+ 5V端子提供5V的电压。这以保护模块)

如果把板载5V使能的那个跳线帽去掉,5v端子为5v输入,需要给12V、5V端子都给电源。

(注意L298N供电的5V如果是用另外电源供电(比如说电池)的话,( 即不是用单片机供电,接单片机的地),那么必须要将单片机的GND和模块上的GND连接,只有这样单片机上过来的逻辑信号才有个参考0点。板载5V稳压芯片的输入引脚和电机供电驱动接线端导通。

2、控制部分由通道A使能、逻辑输入、通道B使能组成。左边的3排控制左边的输出A通道,右边的三排控制右边的输出B通道。

以左边三排为例,右边三排和左边是对称的。先看通道A使能的那个跳线帽,使用时将那个跳线帽拔掉,然后把单片机的PWM输出引脚接到靠近板边缘的排针上面。(注意,最好通过光耦或者其他方式将单片机引脚和使能排针做一下隔离。如果不做隔离,尽量不要使用外力让电机转动,不然电机会发电,有可能烧坏单片机等电路。)

逻辑输入靠近通道A使能的那两个排针是用来控制通道A的电压正负的,给这两个排针1 0,通道A会输出一个电压,给这两个排针0 1,通道A会输出和1 0 相反的电压,从而让电机反转。这两个逻辑控制可以直接接到单片机的IO口,用IO口控制。当然,如果不需要正反转,可以直接给固定的电压。

3、L298N控制直流减速电机可以使用PWM控制速度

 STM32F103ZET6 的高级/通用定时器的 IO 分配表,包括高级控制和通用定时器通道引脚分布 。
定时器的 IO 很多已经复用它途,故表中的 IO 只有部分可用于定时器的实验。

问题

 TIM3CH3应该是初始化PC8,但是初始化PC8不能用,初始化PC9,实际是PC8能用

很诡异,但是先用着

(2)、麦克纳姆轮控制

#include "wheel.h"

/*===================================初始化motor1的GPIO===================================*/
void GPIO_WHEEL_Init(void){
	
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIO外设时钟

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	//MOTOR1_GPIO_IN1初始化
	GPIO_InitStructure.GPIO_Pin = MOTOR1_GPIO_IN1;
	GPIO_Init(MOTOR1_GPIO_PORT, &GPIO_InitStructure);
	GPIO_ResetBits(MOTOR1_GPIO_PORT,MOTOR1_GPIO_IN1);       
	//MOTOR1_GPIO_IN2初始化
	GPIO_InitStructure.GPIO_Pin = MOTOR1_GPIO_IN2;
	GPIO_Init(MOTOR1_GPIO_PORT, &GPIO_InitStructure);
	GPIO_ResetBits(MOTOR1_GPIO_PORT,MOTOR1_GPIO_IN2);
	
	//MOTOR2_GPIO_IN1初始化
	GPIO_InitStructure.GPIO_Pin = MOTOR2_GPIO_IN1;
	GPIO_Init(MOTOR2_GPIO_PORT, &GPIO_InitStructure);
	GPIO_ResetBits(MOTOR2_GPIO_PORT,MOTOR2_GPIO_IN1);       
	//MOTOR2_GPIO_IN2初始化
	GPIO_InitStructure.GPIO_Pin = MOTOR2_GPIO_IN2;
	GPIO_Init(MOTOR2_GPIO_PORT, &GPIO_InitStructure);
	GPIO_ResetBits(MOTOR2_GPIO_PORT,MOTOR2_GPIO_IN2);
	
	//MOTOR3_GPIO_IN1初始化
	GPIO_InitStructure.GPIO_Pin = MOTOR3_GPIO_IN1;
	GPIO_Init(MOTOR3_GPIO_PORT, &GPIO_InitStructure);
	GPIO_ResetBits(MOTOR3_GPIO_PORT,MOTOR3_GPIO_IN1);       
	//MOTOR3_GPIO_IN2初始化
	GPIO_InitStructure.GPIO_Pin = MOTOR3_GPIO_IN2;
	GPIO_Init(MOTOR3_GPIO_PORT, &GPIO_InitStructure);
	GPIO_ResetBits(MOTOR3_GPIO_PORT,MOTOR3_GPIO_IN2);
	
	//MOTOR4_GPIO_IN1初始化
	GPIO_InitStructure.GPIO_Pin = MOTOR4_GPIO_IN1;
	GPIO_Init(MOTOR4_GPIO_PORT, &GPIO_InitStructure);
	GPIO_ResetBits(MOTOR4_GPIO_PORT,MOTOR4_GPIO_IN1);       
	//MOTOR4_GPIO_IN2初始化
	GPIO_InitStructure.GPIO_Pin = MOTOR4_GPIO_IN2;
	GPIO_Init(MOTOR4_GPIO_PORT, &GPIO_InitStructure);
	GPIO_ResetBits(MOTOR4_GPIO_PORT,MOTOR4_GPIO_IN2);  
	
}

/*===================================四个电机正反转控制,0为不转,1为正转,2为反转===================================*/
void motor1(int i){
	//TIM_SetCompare2(TIM3,900);//pwm调速900就是百分之90
	if(i==1){
  GPIO_SetBits(MOTOR1_GPIO_PORT,MOTOR1_GPIO_IN1); 
  GPIO_ResetBits(MOTOR1_GPIO_PORT,MOTOR1_GPIO_IN2);
	}else if(i==2){
  GPIO_SetBits(MOTOR1_GPIO_PORT,MOTOR1_GPIO_IN2); 
  GPIO_ResetBits(MOTOR1_GPIO_PORT,MOTOR1_GPIO_IN1);	
	}else if(i==0){
	GPIO_ResetBits(MOTOR1_GPIO_PORT,MOTOR1_GPIO_IN1); 
  GPIO_ResetBits(MOTOR1_GPIO_PORT,MOTOR1_GPIO_IN2);
	}  
}
void motor2(int i){
	//TIM_SetCompare2(TIM3,900);//pwm调速
	if(i==1){
  GPIO_SetBits(MOTOR2_GPIO_PORT,MOTOR2_GPIO_IN1); 
  GPIO_ResetBits(MOTOR2_GPIO_PORT,MOTOR2_GPIO_IN2);
	}else if(i==2){
  GPIO_SetBits(MOTOR2_GPIO_PORT,MOTOR2_GPIO_IN2); 
  GPIO_ResetBits(MOTOR2_GPIO_PORT,MOTOR2_GPIO_IN1);	
	}else if(i==0){
	GPIO_ResetBits(MOTOR2_GPIO_PORT,MOTOR2_GPIO_IN1); 
  GPIO_ResetBits(MOTOR2_GPIO_PORT,MOTOR2_GPIO_IN2);
	}  
}
void motor3(int i){
	//TIM_SetCompare2(TIM3,900);//pwm调速
	if(i==1){
  GPIO_SetBits(MOTOR3_GPIO_PORT,MOTOR3_GPIO_IN1); 
  GPIO_ResetBits(MOTOR3_GPIO_PORT,MOTOR3_GPIO_IN2);
	}else if(i==2){
  GPIO_SetBits(MOTOR3_GPIO_PORT,MOTOR3_GPIO_IN2); 
  GPIO_ResetBits(MOTOR3_GPIO_PORT,MOTOR3_GPIO_IN1);	
	}else if(i==0){
	GPIO_ResetBits(MOTOR3_GPIO_PORT,MOTOR3_GPIO_IN1); 
  GPIO_ResetBits(MOTOR3_GPIO_PORT,MOTOR3_GPIO_IN2);
	}
}
void motor4(int i){
	//TIM_SetCompare2(TIM3,900);//pwm调速
	if(i==1){
  GPIO_SetBits(MOTOR4_GPIO_PORT,MOTOR4_GPIO_IN1); 
  GPIO_ResetBits(MOTOR4_GPIO_PORT,MOTOR4_GPIO_IN2);
	}else if(i==2){
  GPIO_SetBits(MOTOR4_GPIO_PORT,MOTOR4_GPIO_IN2); 
  GPIO_ResetBits(MOTOR4_GPIO_PORT,MOTOR4_GPIO_IN1);	
	}else if(i==0){
	GPIO_ResetBits(MOTOR4_GPIO_PORT,MOTOR4_GPIO_IN1); 
  GPIO_ResetBits(MOTOR4_GPIO_PORT,MOTOR4_GPIO_IN2);
	} 	
}


/*===================================麦克纳姆轮前后左右函数===================================*/
void STOP(void){
 motor1(RELEASE); 
 motor2(RELEASE); 
 motor3(RELEASE); 
 motor4(RELEASE); 
}//停止函数
 
void RUN(void){
 motor1(FORWARD); 
 motor2(FORWARD); 
 motor3(FORWARD); 
 motor4(FORWARD); 
}//可调速的前进函数
 
void LEFT(void){
 motor1(FORWARD); 
 motor2(BACKWARD); 
 motor3(BACKWARD); 
 motor4(FORWARD); 
}//可调速的左转函数
 
void RIGHT(void){
 motor1(BACKWARD); 
 motor2(FORWARD); 
 motor3(FORWARD); 
 motor4(BACKWARD); 
}//可调速的右转函数
 
void BACK(void){
  motor1(BACKWARD); 
 motor2(BACKWARD); 
 motor3(BACKWARD); 
 motor4(BACKWARD); 
}//可调速的后退函数
 
void zishun(void){
  motor1(FORWARD); 
 motor2(BACKWARD); 
 motor3(FORWARD); 
 motor4(BACKWARD); 
}//顺时针自转
 
void zini(void){
  motor1(BACKWARD); 
 motor2(FORWARD); 
 motor3(BACKWARD); 
 motor4(FORWARD); 
}//逆时针自转


使用面包板来实现L298N四个电机用同一个PWM输出控制

3、电推杆控制

小车的夹取依靠电推杆的伸缩,本来打算用丝杠,但是比较难调

(1)、基础知识

 电推杆有俩个电源线,输入正负极控制推拉,正反极互换,推拉互换

我看网络上有使用继电器来输出正反俩种电压控制电推杆的,但是比较复杂

刚刚好L298N也可以实现输出电压,并且反转输出电压的,所以决定用L298N控制

L298N输出的电压与输入电压差不多,我使用12v电源给L298N供电,刚好输出与12v相近的电压来控制需要12v的电推杆

控制程序与底盘轮子程序基本相似,还可以使用PWM控制速度(实际上PWM控制速度就是依靠控制输出电压的高低实现的,比如说PWM占空比为90%,那L298N就输出12v(输入电压)90%的电压,电机速度也就是原来正常速度的90%)

代码编写过程问题

1、程序写完后,电推杆不运动

通过检测发现PWM输出正常,可能是GPIO有问题,检查电推杆GPIO的初始化函数没发现问题

将电推杆控制GPIO引脚从PB3、4改成PB5、6

不知道为什么电推杆可以运动,推的程序正常,但是拉的的时候会先推一会再拉

怀疑是板子GPIO有什么我不懂的东西,将电推杆控制引脚从PB5、6改成了电机控制使用正常的PA4、5,发现电推杆使用正常

证明有些引脚存在一些我不太懂的知识点,不能正常使用,比如说正点原子精英板PE5原本连的是板载LED灯,如果我把它用到其他模块应该要怎么样

正常来说,可能会有跳帽,拔了跳帽之后,再接即可(没有跳帽直接接?)

2、不用GPIOB5、6的时候将一根杜邦线接上,电推杆会运动,证明有些引脚是在高电平状态的,不明白原因

(下面关于GPIO的会解答)

4、同步带

因为同步带用的也是直流减速电机,与电推杆一致,所以程序也与电推杆程序差不多,只是换了名称和GPIO

特别注意自己程序每个部分所使用的GPIO口是不是重复了,或者没有用到,程序不会报错,但是会有让机器出现莫名其妙的错误

5、启动/停止按钮

(1)、设计

设计思路1:使用启动按键和停止按键设置中断,检测到停止按钮就进入停止中断函数,进行死循环如果按钮二按下,就进入启动中断函数,启动中断函数执行后就会返回主函数。

思路错误:我把问题想复杂和错误了,首先是即使是进入停止中断的死循环后,主程序设置的各个外设的状态不会改变,其实只要进入中断后执行轮子、电推杆等模块的停止函数即可,返回主函数后因为有while(1)的循环,又可以重修启动这些模块

设计思路2:使用启动按钮和停止按钮设置中断,检测到停止按钮按下后就进入中断函数,中断函数中,执行轮子、电推等模块的停止函数,执行结束后返回主函数,进入while(1)循环,等待检测其他操作,启动按钮按下后,进人中断函数就给轮子和电推杆设置一个初始的PWM值,可以直接进行操控(但是我觉得这个思路没有做到真正意义上的软件层面上的启动停止按钮,因为即使按下了停止按钮,手机蓝牙也可以控制车子,只是做到了一时的停止,即使没有按下启动按钮,车子也可以通过蓝牙操控运动,说白了这俩个按钮不是最高优先级,只是做到了暂时控制和完成比赛要求的目的

思路2想法:要想真的做到最高优先级的效果,应该在思路1的基础上,按下停止按钮,进入停止中断函数设置各个模块停止,同时进入死循环,等待启动按钮按下,进入启动中断函数,简单执行后,返回主函数,进入while(1)循环,重新对车子进行控制(但是这个就要搞清楚,一进入停止中断函数后,还能不能检测到其他中断,二会不会产生其他问题)

中断执行后不会影响别的程序的逻辑,就是别的程序执行到某个位置时,被一个中断抢夺的CPU资源,中断执行结束后,程序还会从被中断走的位置继续执行,而且栈变量不变。(中断和主函数属于俩个不同的层)

(2)、中断基础知识

1、中断系统

F103 在内核水平上搭载了一个异常响应系统,支持为数众多的系统异常和外部中断。其中系统
异常有 8 个(如果把 Reset 和 HardFault 也算上的话就是 10 个),外部中断有 60 个。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。有关具体的系统异常和外部中断可在标
准库文件 stm32f10x.h 这个头文件查询到,在 IRQn_Type 这个结构体里面包含了 F103 系列全部
的异常声明
 

2、外部中断可以分为电平触发和边缘触发两种(但是stm32只有边缘触发

从低电平到高电平, 叫做上升
从高电平到低电平, 叫做下降

·上升沿触发
数字电平从低电平(数字“0”)变为高电平(数字“1”)的那一瞬间叫作上升沿。 上升沿触发是当信号有上升沿时的开关动作,当电位由低变高而触发输出变化的就叫上升沿触发。也就是当测到的信号电位是从低到高也就是上升时就触发,叫做上升沿触发。

·下降沿触发
数字电路中,数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间叫作下降沿。 下降沿触发是当信号有下降沿时的开关动作,当电位由高变低而触发输出变化的就叫下降沿触发。也就是当测到的信号电位是从高到低也就是下降时就触发,叫做下降沿触发。

那么我们可以很好的理解两种触发:

  • 上升沿触发 就是当电压从低变高时触发中断
  • 下降沿触发 就是当电压从高变低时触发中断

当然,上升沿与下降沿检测的是电平变化的一瞬间,就会产生中断,这个时间是us级别的,但是如果中断引脚检测到一直保持低/高电平,则无法产生下次中断,也就是中断只会触发一次,只有在下次电平发生变化时才会重新触发中断

注意事项:

  1. 边沿触发就是单片机在上一次机器周期内,检测到中断引脚口为高电平,这一次机器周期内检测到为低电平,则会申请产生中断,所以为us级别
  2. 下降沿触发是锁存中断信号的,由D触发器记忆,意即:即使当时CPU来不及响应中断,外部中断信号撤消后,由于D触发器的记忆作用,消失的中断信号仍然有效,直到中断被响应并进入中断ISR,记忆的中断信号才会由硬件清除。 这也是为什么边沿触发只能触发一次的原因
  3. 对于单片机的中断引脚,如果你另一端接的是VCC 则需要设置成上升沿或者高电平触发 如果你接的是GND 就可以设置成下降沿或者低电平触发

3、 EXTI 有 20 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0 至 EXTI15,还有另外七根用于特定的外设事件

其中中断线0-4有单独的中断向量,即单独的中断服务函数。

EXTI0_IRQHandler

EXTI1_IRQHandler

EXTI2_IRQHandler

EXTI3_IRQHandler

EXTI4_IRQHandler

中断线5-9共用一个中断服务函数

EXTI9_5_IRQHandler

中断线10-15共同一个中断服务函数

EXTI15_10_IRQHandler

EXTI15_10_IRQn用来配置外部中断线,共用情况和中断服务函数一样。

代码编写过程问题

1、编写好中断和初始化好板载按钮GPIO后,只有按下KEY_UP会进入中断,KEY0不行

想到这俩类按钮的有效电平是不同的,KEY_UP是高电平有效,初始为低电平,KEY0是低电平有效,初始为高电平,我中断全部配置的为下降沿触发,按下按钮后只能检查到高电平转为低电平,按理来说只能检查到KEY0,检测不到KEY1。

但把中断全部设置为上升沿触发,还是只有按下KEY_UP会进入中断,KEY0不行,可能其他东西出错,与这个边缘触发无关,但是不知道为什么这个边缘触发不起效果

思考后发现,KEY_UP是初始状态为低电平,按下按钮后为高电平,再松开又会变回低电平,在这个过程中,上升下降都有实现,所以无论是上升沿触发还是下降沿触发都可以进入中断函数,只是进入中断的时间不同,上升沿触发,按下按钮的那一刻进入中断函数,下降沿触发,按下后松开的时候进入中断函数。KEY0也同理,所以KEY0无效的原因应该不是边沿触发配置的问题。

会不会是KEY0的GPIO配置错误,根本不是PE3或者EXTI配置错误

检测EXTI发现自己没有打开AFIO时钟,但是打开和不打开效果一样,不明白

检测配置后把EXTI15_10_IRQHandler换成EXTI3_IRQHandler,把EXTI15_10_IRQn换成EXTI3_IRQn后,按钮KEY0使用正常,但是移植原来程序的时候用的PC13也是用EXTI15_10_IRQHandler和EXTI15_10_IRQn,还以为是15有什么特殊的,可以哪个引脚都能用

还要思考明白EXTI15_10_IRQHandler和EXTI15_10_IRQn到底是什么意思,在配置什么,是否有特殊性

EXTI15_10_IRQHandler这个是配置中断服务函数的,GPIOx就用中断线X就用EXTIx

比较特殊的是EXTI15_10_IRQHandler是中断线10-15共同一个中断服务函数,就是GPIO10_15都用这个中断服务函数,所以移植的代码PC13也用这个,但是我改成了PE3,就要用EXTI3_IRQHandler,图中的宏的定义只是把这个中断函数换了个名

EXTI15_10_IRQn包含EXTI10 ---EXTI15共6条外部中断线,也是共用一个中断入口,需要在中断里面判断是哪一个中断。

2、为什么要初始化俩次GPIO

就是程序移植的时候没有移植对,把普通按钮的程序和按钮中断的程序弄混了

按钮中断相关的GPIO在EXTI初始化时就初始化好了,且只与它有关

 下面是普通按钮程序的GPIO初始化

3、已经连接器件的GPIO怎么用?

有些有跳帽,有些是复用

4为什么使用别的GPIO不行,肯定是我哪里疏漏了

GPIO初始化有问题

5使用自锁按钮,按下后进入中断打开led灯的反应不是很正常

怀疑是按钮问题

会不会是杜邦线链接不稳,按住杜邦线链接处,加强链接稳定性,还是一样 

想起来可能是之前按钮程序一直有说的抖动问题,这个按钮比较劣质,板载按钮的时候才正常

按键的抖动会导致一次按键动作被当成多次按键,为确保MCU对按键的一次闭合仅作一次处理,必须消除按键的抖动,在按键处于稳定状态时读取按键的状态。

按键的去抖动,通常有硬件消抖和软件消抖两种方式。

(1)硬件消抖

在按键数目较少时,可以采用硬件的方法消除按键抖动。比较简单的硬件电路是利用电阻和电容构成的RC低通滤波器来实现,电路结构如图所示。

采用RC低通滤波器实现硬件去抖的关键是选择适当的电阻值和电容值。由于人按键的频率一般低于1kHz,而按键抖动的频率一般是几十到几百kHz。因此,可以将低通滤波器的截止频率选择为10kHz。根据截止频率计算式fLP=1/2πRC可以选取160 Ω的电阻以及0.1 pF的电容来构成RC低通滤波器。

(2)软件消抖

如果按键数目较多,可以采用软件的方法消除按键抖动。处理流程如下:当检测到按键按下后执行一个延时程序,延时时间为5~10 ms。当前沿抖动消失后,再次检测按键的状态,如果仍然保持按下状态,则确认按键真正按下,并执行按键处理任务。同样,对于按键的释放,也需要采用延时函数去掉后沿的抖动。

按键抖动其实好像就是相当于多按几次,进入中断后多开几次灯好像没有什么影响,我的问题是有时候按下按钮后灯会开不了,有时候会按了复位后又重新亮

在中断中加了延时函数之后,相当于消抖动了,但是还是不正常

看看是不是自锁按钮的问题,把自锁按钮换成非自锁按钮

还是一个鸟样

直接使用电机控制试一下,如果控制正常就不纠结了

中断中写入电机控制之后,按钮模块一接上,电机就不动了,似乎是直接进入了中断,但是使用开发板自带的按钮还算正常

怀疑是按钮不太好

放弃!!(可能是GPIO初始化问题,但是后面没去再研究了)

6、LCD显示屏

问题

不明白LCD屏幕例程移植后,屏幕必须初始化串口才显示

因为在void LCD_Init(void)函数中使用了 printf 来打印 LCD ID,所以,如果你在主函数里面没有初始化串口,那么将导致程序死在 printf 里面!!如果不想用 printf,那么请注释掉它。

 不知道为什么,只要用蓝牙控制电机后,屏幕就会异常,开始时正常显示,一但按下控制键,只有在控制的时候屏幕才显示,本来以为是串口的原因,把LCD相关的函数中串口的部分去掉后,还是这样

异常后,再按前进和左移键的时候会显示,按后退和右移键不显示

是不是电源因为板子连太多东西了

把俩个模块都接上电池/或者把板子上俩个模块的连接线先拆了

还是这样

我突然想明白,可能是因为我在控制电机的时候进入了中断,在中断中主函数相关的功能就停止了,而我的屏幕显示函数在主函数中,所以屏幕显示异常

使能了一个按钮中断,与串口中断不同,点击按钮进入中断后,屏幕也熄灭,确定是中断的原因,不是串口的原因

 把按钮中断中的电机控制函数去掉后,好像按钮中断也不会引起屏幕异常,这说明欧美异常不是中断的原因,是电机控制函数的原因,是不是因为电机控制的GPIO用到屏幕相关的GPIO了

底盘电机用的GPIO

 LCD用的GPIO

 把重复使用的GPIOE改了之后,异常解除

7、视觉识别

视觉识别主要通过树莓派来实现,树莓派再将得到的信息通过串口发送给单片机

以下是操作方法:树莓派3识别二维码并与stm32通信_ChapterQ的博客-CSDN博客

8、整体系统

(1)、GPIO

问题

不知道为什么PA12、PA13(14、15)的GPIO一直是高电平,有些GPIO用不了

STM32F10xxx 微控制器的不同封装有不同的有效引脚数。因此,某些与引脚相关的功能可能随
封装而不同。

GPIO复用概述

STM32F4 有很多的内置外设,这些外设的外部引脚都是与 GPIO 复用的。也就是说,一个 GPIO如果可以复用为内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用

 一般都是复用为内置外设,有一些可以复用为普通输入输出GPIO,但是一般都是有用的,不太好用,比如下面这个

(原本是swj调试端口引脚) 

(2)、底盘

四个底盘电机的速度不一样,导致运动有些不正常

应该要使用PID算法的,但是买的电机没有带编码器,所以只能通过调每个电机的PWM来让速度尽量相同

但是不知道为什么好像PWM调速没什么用,就是不够明显,装上轮子后

(3)、整体

最终整个车子因为机械结构没有装好导致车子虽然能动,各个模块也能控制,但是达不到使用的效果,所以电控程序和思路应该是正确的

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值