stm32教程:EXTI外部中断应用

早上好啊大佬们,上一期我们讲了EXTI外部中断的原理以及基础代码的书写,这一期就来尝试一下用它来写一些有实际效能的工程吧。

这一期里,我用两个案例代码来让大家感受一下外部中断的作用和使用价值。

旋转编码器计数

整体思路讲解

这里,我们需要使用到 OLED 和 旋转编码器。

先来说一下旋转编码器 ——

我们使用的是EC11旋转编码器,这是一种增量式旋转编码器,拥有A、B、C三个输出通道,其中A、B两相输出正交信号,相位差为90°,C相输出零脉冲信号,用于标识位置。

当编码器正转时,A相的输出信号超前B相90°;当编码器反转时,A相滞后B相90°。我们在程序中可以根据A、B两相信号输出的先后顺序,来判断旋转编码器是正转还是反转

波形图 显示结果就是:

然后对于我们的代码实现思路就很清晰了,

检测A相的下降沿或上升沿,然后判断B相的电平高低,用来区分正转还是反转。

接线

OLEDSTM32
VCC3.3V
GNDGND
SCLPB8
SDAPB9
旋转编码器STM32
VCC3.3V
GNDGND
APA0
BPA1

代码

main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "count.h"

int main()
{
	OLED_Init();
	External_Init();
	
	OLED_ShowString(0, 0, "cnt:", OLED_8X16);
	OLED_Update();
	
	while(1)
	{
		OLED_ShowNum(32, 0, Count_Get(), 3, OLED_8X16);
		OLED_Update();
	}
	
}

count.c

#include "stm32f10x.h"                  // Device header

int16_t cnt = 0;

void External_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //打开AFIO的时钟
	//然后打开 EXTI 和 NVIC 的外设时钟,但是出于它们是一直打开的,所以就不需要了。
	
	GPIO_InitTypeDef GPIO_InitStructure;
	/*配置GPIO*/
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/*重映射GPIO*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //将外部中断的0号线映射到GPIOB
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1); //将外部中断的1号线映射到GPIOB
	
	/*配置EXTI*/
	EXTI_InitTypeDef EXTI_InitStructure;
	
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //检测下降沿
	
	EXTI_Init(&EXTI_InitStructure); //EXTI中断初始化
	
	/*配置NVIC*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//中断分组, 2抢占,2响应
	//这个分组函数一个工程只选择一次,多次调用会覆盖之前的配置
	
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;			//选择配置NVIC的EXTI0线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;			//选择配置NVIC的EXTI1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//指定NVIC线路的响应优先级为2
	NVIC_Init(&NVIC_InitStructure);
	//这里就是两个中断线的写法,但是这里的中断配置比较随意,因为两种情况不会同时发生
}

int16_t Count_Get(void)
{
	return cnt;
}

void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET) //判断返回值是否为 SET
	{
		// 如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动
		if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0)		//PA0的下降沿触发中断,此时检测另一相PA1的电平,目的是判断旋转方向
			{
				cnt --;					//此方向定义为反转,计数变量自减
			}
		}
		
		EXTI_ClearITPendingBit(EXTI_Line0); //重置中断判断位,否则会卡死在中断函数中
	}
}

void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET) //判断返回值是否为 SET
	{
		// 如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动
		if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 0)
		{
			if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)		//PA1的下降沿触发中断,此时检测另一相PA0的电平,目的是判断旋转方向
			{
				cnt ++;					//此方向定义为反转,计数变量自减
			}
		}
		
		EXTI_ClearITPendingBit(EXTI_Line1); //重置中断判断位,否则会卡死在中断函数中
	}
}


count.h

#ifndef __COUNT_H
#define __COUNT_H


void External_Init(void);
int16_t Count_Get(void);


#endif

红外避障实现

当车辆行驶时,前方遇到障碍物被我们的红外探测到,就需要停下当前的所有行为,进行避障操作。

对于红外部分内容,大家可以看看之前的一篇文章。

stm32教程:红外循迹模块 & 红外避障模块 & 光电门模块-CSDN博客

整体功能讲解

我们这里主要是来演示一下EXTI外部中断,所以这里就简单模拟一下。

首先是红外避障模块,当它没有探测到物品时,车辆正常行驶。

当它探测到物品时,就需要进行紧急避障。

然后,我们用一个LED灯来模拟车辆状态,车辆正常行驶时,LED灯灭。

车辆进行避障时,LED灯亮

接线

红外STM32
VCC3.3V
GNDGND
OUTPB7

LED我们使用STM32系统板的板载LED

代码

bizhang.c

#include "stm32f10x.h"                  // Device header

void LED_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //PC13是板载的LED
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOC, GPIO_Pin_13);
}

void LED_Turn(void)
{
	if (GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13) == 0)		//获取输出寄存器的状态,如果当前引脚输出低电平
	{
		GPIO_SetBits(GPIOC, GPIO_Pin_13);					//则设置PC13引脚为高电平,高电平灭
	}
	else													//否则,即当前引脚输出高电平
	{
		GPIO_ResetBits(GPIOC, GPIO_Pin_13);					//则设置PC13引脚为低电平,低电平亮
	}
}


void External_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //打开AFIO的时钟
	//然后打开 EXTI 和 NVIC 的外设时钟,但是出于它们是一直打开的,所以就不需要了。
	
	GPIO_InitTypeDef GPIO_InitStructure;
	/*配置GPIO*/
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	/*重映射GPIO*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource7); //将外部中断的7号线映射到GPIOB
	
	/*配置EXTI*/
	EXTI_InitTypeDef EXTI_InitStructure;
	
	EXTI_InitStructure.EXTI_Line = EXTI_Line7;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //改成双向,上升沿切换为避障,下降沿切换为正常行驶。
	
	EXTI_Init(&EXTI_InitStructure); //EXTI中断初始化
	
	/*配置NVIC*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//中断分组, 2抢占,2响应
	//这个分组函数一个工程只选择一次,多次调用会覆盖之前的配置
	
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;			//选择配置NVIC的EXTI9_5线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);
}

void EXTI9_5_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line7) == SET) //判断返回值是否为 SET
	{
		LED_Turn();
		
		EXTI_ClearITPendingBit(EXTI_Line7); //重置中断判断位,否则会卡死在中断函数中
	}
}

bizhang.h

#ifndef __BIZHANG_H
#define __BIZHANG_H

void LED_Init(void);
void LED_Turn(void);
void External_Init(void);



#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "bizhang.h"

int main()
{
	LED_Init();
	External_Init();
	
	while(1)
	{
		
	}
	
}

尾声

OK,那么这一期就到这里了,如果需要这期的代码,可以私信我。

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

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

相关文章

数据分析系列--⑦RapidMiner模型评价(基于泰坦尼克号案例含数据集)

一、前提 二、模型评估 1.改造⑥ 2.Cross Validation算子说明 2.1Cross Validation 的作用 2.1.1 模型评估 2.1.2 减少过拟合 2.1.3 数据利用 2.2 Cross Validation 的工作原理 2.2.1 数据分割 2.2.2 迭代训练与测试 ​​​​​​​ 2.2.3 结果汇总 ​​​​​​​ …

WPS mathtype间距太大、显示不全、公式一键改格式/大小

1、间距太大 用mathtype后行距变大的原因 mathtype行距变大到底怎么解决-MathType中文网 段落设置固定值 2、显示不全 设置格式: 打开MathType编辑器点击菜单栏中的"格式(Format)"选择"间距(Spacing)"在弹出的对话框中调整"分数间距(F…

【Postman接口测试】Postman的安装和使用

在软件测试领域,接口测试是保障软件质量的关键环节之一,而Postman作为一款功能强大且广受欢迎的接口测试工具,能够帮助测试人员高效地进行接口测试工作。本文将详细介绍Postman的安装和使用方法,让你快速上手这款工具。 一、Pos…

因果推断与机器学习—用机器学习解决因果推断问题

Judea Pearl 将当前备受瞩目的机器学习研究戏谑地称为“仅限于曲线拟合”,然而,曲线拟合的实现绝非易事。机器学习模型在图像识别、语音识别、自然语言处理、蛋白质分子结构预测以及搜索推荐等多个领域均展现出显著的应用效果。 在因果推断任务中,在完成因果效应识别之后,需…

python算法和数据结构刷题[2]:链表、队列、栈

链表 链表的节点定义: class Node():def __init__(self,item,nextNone):self.itemitemself.nextNone 删除节点: 删除节点前的节点的next指针指向删除节点的后一个节点 添加节点: 单链表 class Node():"""单链表的结点&quo…

AJAX案例——图片上传个人信息操作

黑马程序员视频地址&#xff1a; AJAX-Day02-11.图片上传https://www.bilibili.com/video/BV1MN411y7pw?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes&p26 图片上传 <!-- 文件选择元素 --><input type"file"…

deepseek大模型本机部署

2024年1月20日晚&#xff0c;中国DeepSeek发布了最新推理模型DeepSeek-R1&#xff0c;引发广泛关注。这款模型不仅在性能上与OpenAI的GPT-4相媲美&#xff0c;更以开源和创新训练方法&#xff0c;为AI发展带来了新的可能性。 本文讲解如何在本地部署deepseek r1模型。deepseek官…

使用 Ollama 和 Kibana 在本地为 RAG 测试 DeepSeek R1

作者&#xff1a;来自 Elastic Dave Erickson 及 Jakob Reiter 每个人都在谈论 DeepSeek R1&#xff0c;这是中国对冲基金 High-Flyer 的新大型语言模型。现在他们推出了一款功能强大、具有开放权重的思想链推理 LLM&#xff0c;这则新闻充满了对行业意味着什么的猜测。对于那些…

Greenplum临时表未清除导致库龄过高处理

1.问题 Greenplum集群segment后台日志报错 2.回收库龄 master上执行 vacuumdb -F -d cxy vacuumdb -F -d template1 vacuumdb -F -d rptdb 3.回收完成后检查 仍然发现segment还是有库龄报警警告信息发出 4.检查 4.1 在master上检查库年龄 SELECT datname, datfrozen…

栈和队列特别篇:栈和队列的经典算法问题

图均为手绘,代码基于vs2022实现 系列文章目录 数据结构初探: 顺序表 数据结构初探:链表之单链表篇 数据结构初探:链表之双向链表篇 链表特别篇:链表经典算法问题 数据结构:栈篇 数据结构:队列篇 文章目录 系列文章目录前言一.有效的括号(leetcode 20)二.用队列实现栈(leetcode…

记录一次,PyQT的报错,多线程Udp失效,使用工具如netstat来检查端口使用情况。

1.问题 报错Exception in thread Thread-1: Traceback (most recent call last): File "threading.py", line 932, in _bootstrap_inner File "threading.py", line 870, in run File "main.py", line 456, in udp_recv IndexError: list…

论文阅读(十):用可分解图模型模拟连锁不平衡

1.论文链接&#xff1a;Modeling Linkage Disequilibrium with Decomposable Graphical Models 摘要&#xff1a; 本章介绍了使用可分解的图形模型&#xff08;DGMs&#xff09;表示遗传数据&#xff0c;或连锁不平衡&#xff08;LD&#xff09;&#xff0c;各种下游应用程序之…

本地部署DeepSeek开源多模态大模型Janus-Pro-7B实操

本地部署DeepSeek开源多模态大模型Janus-Pro-7B实操 Janus-Pro-7B介绍 Janus-Pro-7B 是由 DeepSeek 开发的多模态 AI 模型&#xff0c;它在理解和生成方面取得了显著的进步。这意味着它不仅可以处理文本&#xff0c;还可以处理图像等其他模态的信息。 模型主要特点:Permalink…

从 UTC 日期时间字符串获取 Unix 时间戳:C 和 C++ 中的挑战与解决方案

在编程世界里&#xff0c;从 UTC 日期时间字符串获取 Unix 时间戳&#xff0c;看似简单&#xff0c;实则暗藏玄机。你以为输入一个像 “Fri, 17 Jan 2025 06:07:07” 这样的 UTC 时间&#xff0c;然后轻松得到 1737094027&#xff08;从 1970 年 1 月 1 日 00:00:00 UTC 开始经…

Linux——网络(tcp)

文章目录 目录 文章目录 前言 一、TCP逻辑 1. 面向连接 三次握手&#xff08;建立连接&#xff09; 四次挥手&#xff08;关闭连接&#xff09; 2. 可靠性 3. 流量控制 4. 拥塞控制 5. 基于字节流 6. 全双工通信 7. 状态机 8. TCP头部结构 9. TCP的应用场景 二、编写tcp代码函数…

51单片机(STC89C52)开发:点亮一个小灯

软件安装&#xff1a; 安装开发板CH340驱动。 安装KEILC51开发软件&#xff1a;C51V901.exe。 下载软件&#xff1a;PZ-ISP.exe 创建项目&#xff1a; 新建main.c 将main.c加入至项目中&#xff1a; main.c:点亮一个小灯 #include "reg52.h"sbit LED1P2^0; //P2的…

力扣116. 填充每个节点的下一个右侧节点指针

Problem: 116. 填充每个节点的下一个右侧节点指针 文章目录 题目描述思路复杂度Code 题目描述 思路 遍历思想(利用二叉树的先序遍历) 本题目的难点在于对于不同父节点的邻接问题因此我们可以抽象将两两节点为一组&#xff08;不同父节点的两个孩子节点也抽象为一组&#xff09…

k8s简介,k8s环境搭建

目录 K8s简介环境搭建和准备工作修改主机名&#xff08;所有节点&#xff09;配置静态IP&#xff08;所有节点&#xff09;关闭防火墙和seLinux&#xff0c;清除iptables规则&#xff08;所有节点&#xff09;关闭交换分区&#xff08;所有节点&#xff09;修改/etc/hosts文件&…

苯乙醇苷类化合物的从头生物合成-文献精读108

Complete pathway elucidation of echinacoside in Cistanche tubulosa and de novo biosynthesis of phenylethanoid glycosides 管花肉苁蓉中松果菊苷全生物合成途径解析及苯乙醇苷类化合物的从头生物合成 摘要 松果菊苷&#xff08;ECH&#xff09;是最具代表性的苯乙醇苷…

C++ 新特性实现 ThreadPool

序言 在之前我们实现过线程池&#xff0c;但是非常基础。答题思路就是实现一个安全的队列&#xff0c;再通过 ThreadPool 来管理队列和线程&#xff0c;对外提供一个接口放入需要执行的函数&#xff0c;但是这个函数是无参无返回值的。  参数的问题我们可以使用 bind 来封装&a…