IMX6UL开发板中断实验(三)

        在上一节我们编写完成了中断驱动文件和中断驱动头文件,那么这一讲我们将继续中断实验

        下面就是GPIO的中断设置,第一步要设置中断GPIO的触发方式,首先我们先看到寄存器,一共有GPIOx_ICR1和ICR2,

        

        图如上,ICR1或者ICR2可以看出图中都是由两个bit来描述一个IO口,相当于一个寄存器控制16个IO口,两个的话也就是32个IO口
        

        可以看出,bit31-bit30的作用翻译过来就是,此寄存器控制GPIO中断15功能的激活条件,然后像寄存器中写入00,01作用就是,中断的触发模式,分别是低电平,高电平,上升沿,下降沿触发
        对于我们需要完成的实验,首先我们看到开发板的原理图,既然是通过中断的方式点亮LED等,那么意思就是当按键按下的时候LED灯亮,那么按键按下是属于哪种形式的触发方式?

        这里需要说明的是,各种触发条件的条件以及特性和大致应用场景,在如下图按键按下额时候,如果产生低电平那么就可以触发条件,那么很明显对应的就是,下降沿触发,然后下面的表格对应的是,低电平触发,高电平触发,上升沿触发,下降沿触发

        KEY0按键对应的IO是,UART1 CTS,需要讲这个引脚设置为下降沿触发

        然后我们看到下一个寄存器,GPIO_IMR 包含了针对每个中断线的屏蔽位(masking bits),具体意思是,用于控制哪些GPIO引脚的中断信号应该被处理器接收和处理,哪些应该被忽略。
        GPIO_IMR寄存器中的每一位都对应一个GPIO引脚的中断屏蔽控制。如果某位被设置为1,则对应的GPIO引脚的中断请求将被屏蔽,即该引脚的中断信号不会被处理器响应;如果某位被设置为0,则对应的GPIO引脚的中断请求是使能的,即该引脚的中断信号可以被处理器接收和处理。

        然后这里继续看下一个我们需要用到的寄存器,GPIOx_ISR这个寄存器的作用就是,每个bit位对应一个IO口,一共也就是对应32的IO,如果某个IO口检测到中断产生,则将对应的bit位置1
        但是在中断产生过后,这个中断标志位肯定是需要被清0的,但是对于这个寄存器而言,这个寄存器是需要通过写1来清0,并不是直接写0

                

        UART1 CTS 按键这个引脚对应的是GPIO1_IO18,现在我们需要找到这个引脚对应的中断ID号,可以看到GPIO16-31是67这个ID号,但是这并不是他实际的ID号,他的实际ID号还需要加上32,因为PPI(私有中断ID)和SGI(软件中断),以及SPI共享外设中断,这些都是在之前提到过的,这里编号全是SPI共享外设中断,而PPI和SGI加起来一共有32个中断,所以需要在这里67的基础上再加上32,所以GPIO16-31引脚对应的中断是99

        然后在我们的代码文件中,也可以看到对应的中断号正好是99

        然后下面就开始编写GPIO的驱动函数,在之前的基础上我们加入中断的相关函数,首先我先会展示之前的代码,首先是头文件

#ifndef _BSP_GPIO_H
#define _BSP_GPIO_H
#define _BSP_KEY_H
#include "imx6ul.h"/* 枚举类型和结构体定义 */
typedef enum _gpio_pin_direction
{kGPIO_DigitalInput = 0U,  		/* 输入 */kGPIO_DigitalOutput = 1U, 		/* 输出 */
} gpio_pin_direction_t;typedef struct _gpio_pin_config
{gpio_pin_direction_t direction; /* GPIO方向:输入还是输出 */uint8_t outputLogic;            /* 如果是输出的话,默认输出电平 */
} gpio_pin_config_t;/* 函数声明 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
int gpio_pinread(GPIO_Type *base, int pin);
void gpio_pinwrite(GPIO_Type *base, int pin, int value);#endif

        然后是.c文件

#include "bsp_gpio.h"
/** @description		: GPIO初始化。* @param - base	: 要初始化的GPIO组。* @param - pin		: 要初始化GPIO在组内的编号。* @param - config	: GPIO配置结构体。* @return 			: 无*/
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{if(config->direction == kGPIO_DigitalInput) /* 输入 */{base->GDIR &= ~( 1 << pin);}else										/* 输出 */{base->GDIR |= 1 << pin;gpio_pinwrite(base,pin, config->outputLogic);/* 设置默认输出电平 */}
}/** @description	 : 读取指定GPIO的电平值 。* @param - base	 : 要读取的GPIO组。* @param - pin	 : 要读取的GPIO脚号。* @return 		 : 无*/int gpio_pinread(GPIO_Type *base, int pin){return (((base->DR) >> pin) & 0x1);}/** @description	 : 指定GPIO输出高或者低电平 。* @param - base	 : 要输出的的GPIO组。* @param - pin	 : 要输出的GPIO脚号。* @param - value	 : 要输出的电平,1 输出高电平, 0 输出低低电平* @return 		 : 无*/
void gpio_pinwrite(GPIO_Type *base, int pin, int value)
{if (value == 0U){base->DR &= ~(1U << pin); /* 输出低电平 */}else{base->DR |= (1U << pin); /* 输出高电平 */}
}

        然后在此基础上加入中断的函数,首先就是使能GPIO中断,前面讲到了GPIO引脚的中断是由引脚GPIOx_IMR寄存器所控制,那么中断使能函数如下

void gpio_enableint(GPIO_Type* base, unsigned int pin)
{ base->IMR |= (1 << pin);
}

         有了使能函数,同样就有禁止函数引脚使用中断的函数

void gpio_disableint(GPIO_Type* base, unsigned int pin)
{ base->IMR &= ~(1 << pin);
}

        以及对应的清楚中断标志位的函数,这些都是上面所提到的寄存器所控制的

void gpio_clearintflags(GPIO_Type* base, unsigned int pin)
{base->ISR |= (1 << pin);
}

        然后揪下来就要开始编写 设置GPIO的中断配置功能的函数,首先代码如下

void gpio_intconfig(GPIO_Type* base, unsigned int pin, gpio_interrupt_mode_t pin_int_mode)
{volatile uint32_t *icr;uint32_t icrShift;icrShift = pin;base->EDGE_SEL &= ~(1U << pin);if(pin < 16) 	/* 低16位 */{icr = &(base->ICR1);}else			/* 高16位 */{icr = &(base->ICR2);icrShift -= 16;}switch(pin_int_mode){case(kGPIO_IntLowLevel):*icr &= ~(3U << (2 * icrShift));break;case(kGPIO_IntHighLevel):*icr = (*icr & (~(3U << (2 * icrShift)))) | (1U << (2 * icrShift));break;case(kGPIO_IntRisingEdge):*icr = (*icr & (~(3U << (2 * icrShift)))) | (2U << (2 * icrShift));break;case(kGPIO_IntFallingEdge):*icr |= (3U << (2 * icrShift));break;case(kGPIO_IntRisingOrFallingEdge):base->EDGE_SEL |= (1U << pin);break;default:break;}
}

        代码看似很长,但其实很好理解,我们分两部分,以swtich语句为分割,首先swtich上面大致原理是这段代码的主要作用是根据pin号配置相应引脚的中断触发方式,并决定修改哪个中断控制寄存器(ICR1或ICR2)以及在该寄存器中的哪个位位置。它首先禁用当前pin号引脚的中断触发方式,然后根据pin号的大小选择合适的ICR寄存器,并计算出在该寄存器中应操作的位位置。
        先定义一个指向uint32_t类型的指针icr,大致作用就是访问中断控制寄存器。volatile关键字确保编译器在每次访问icr时都直接从内存中读取值,而不是使用可能已经缓存在寄存器中的值。
        定义一个无符号32位整数icrShift,用于存储将要操作的位的位置,在ICR寄存器中

        base->EDGE_SEL &= ~(1U << pin);就是如果在之后中断采取高电平或者低电平的方式触发的话,就禁用该引脚当前的边沿触发设置。
        然后ICR在前面也提高过,分别控制16个IO引脚,那么低16位就是ICR1,不低于16位就是ICR2,这里是对ICR寄存器的一个选择

        然后下面看到switch语句的部分,这部分是在完成什么事情呢,也就是选择每个GPIO的中断触发方式,以这个为例,比如我要让某个引脚低电平输出,那么我就将控制触发方式的寄存器(ICR),清零然后写入值,因为低电平触发是00,所以这里就写入00的值,以为着到时候就如果选择模式是低电平,那么就写入值00

case(kGPIO_IntLowLevel):*icr &= ~(3U << (2 * icrShift));break;

        然后下面的代码都是一样的道理,都是先将这个寄存器的值清零,也就是初始化,然后再根据选择的模式然后写入值进去,唯一有区别的地方就是最后一个,因为在前面ICR寄存器中可以看到,他只有低电平,高点平,上边沿和下边沿四种触发方式
        但是在我们的设置当中,却还有一种双边沿触发的方式,那么这个是通过控制哪个寄存器来实现的呢

case(kGPIO_IntRisingOrFallingEdge):base->EDGE_SEL |= (1U << pin);break;

        它的主要作用是,在GPIO(通用输入输出)接口中,GPIO_EDGE_SEL是一个特殊的寄存器,它允许开发者对中断的触发方式进行更细致的控制。通常,中断可以由多种条件触发,如电平变化(高电平、低电平)、边缘变化(上升沿、下降沿)等。GPIO_EDGE_SEL通过其各个位的状态,为GPIO引脚指定了是应该基于上升沿、下降沿还是两者都产生中断。
        所以双边沿以及边沿触发的不产生都是需要这个寄存器的控制

        然后剩下的函数只需要在头文件在中引用一下,然后GPIO的驱动文件我们就编写完成了。然后就开始创建我们的具体实验代码了,首先需要创建一个文件夹存放两个文件,分别名为exti,创建文件bsp_exti.c和bsp_exti.h,下面我会先放出两个文件的代码

#include "bsp_exit.h"
#include "bsp_gpio.h"
#include "bsp_int.h"
#include "bsp_delay.h"
#include "bsp_beep.h"/** @description			: 初始化外部中断* @param				: 无* @return 				: 无*/
void exit_init(void)
{gpio_pin_config_t key_config;/* 1、设置IO复用 */IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);			/* 复用为GPIO1_IO18 */IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);/* 2、初始化GPIO为中断模式 */key_config.direction = kGPIO_DigitalInput;key_config.interruptMode = kGPIO_IntFallingEdge;key_config.outputLogic = 1;gpio_init(GPIO1, 18, &key_config);GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);				/* 使能GIC中对应的中断 */system_register_irqhandler(GPIO1_Combined_16_31_IRQn, (system_irq_handler_t)gpio1_io18_irqhandler, NULL);	/* 注册中断服务函数 */gpio_enableint(GPIO1, 18);								/* 使能GPIO1_IO18的中断功能 */
}/** @description			: GPIO1_IO18最终的中断处理函数* @param				: 无* @return 				: 无*/
void gpio1_io18_irqhandler(void)
{ static unsigned char state = 0;delay(10);if(gpio_pinread(GPIO1, 18) == 0)	/* 按键按下了  */{state = !state;beep_switch(state);}gpio_clearintflags(GPIO1, 18); /* 清除中断标志位 */
}

        首先将按键的GPIO进行复用,然后先初始化GPIO,最后设置GIC的初始化,包括对于中断功能以及对应中断号的初始化,上面其实我们就是在编写自己的库,然后使用自己的库开始编写具体的外部中断函数,就和以前写STM32的标准库是一个道理
        然后下面就是中断处理函数,也就是我们在触发中断后,需要执行的函数,这个就不难理解了,按下 KEY 就会打开蜂鸣器,再次按下就会关闭蜂鸣器,最后再将中断位清除

        主函数里面只需要执行灯一直在闪烁的代码就可以了,然后等待之后我们按下按键产生蜂鸣器鸣叫的中断

#include "bsp_clk.h"#include "bsp_delay.h"#include "bsp_led.h"#include "bsp_beep.h"#include "bsp_key.h"#include "bsp_int.h"#include "bsp_exit.h"/** @description	: main函数* @param 		: 无* @return 		: 无*/int main(void){unsigned char state = OFF;int_init(); 		/* 初始化中断(一定要最先调用!) */imx6u_clkinit();	/* 初始化系统时钟 			*/clk_enable();		/* 使能所有的时钟 			*/led_init();			/* 初始化led 			*/beep_init();		/* 初始化beep	 		*/key_init();			/* 初始化key 			*/exit_init();		/* 初始化按键中断			*/while(1)			{	state = !state;led_switch(LED0, state);delay(500);}return 0;}

        因为添加了文件,最后再修改一下Makefile,确保能找到每个文件的路径

INCDIRS 		:= imx6ul \bsp/clk \bsp/led \bsp/delay  \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/intSRCDIRS			:= project \bsp/clk \bsp/led \bsp/delay \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/int

        然后最后只需要将代码烧写进去,我们的本次实验就算是完成了,这个中断对于imx6ul确实是一个全新的板块,与STM32的NVIC不同,MPU芯片普遍采取的都是GIC的中断模式,也是一个很复杂的适用于多核的中断模式
        博主也是才接触,所以周期有点长,以及在讲解的过程当中可能会有很多地方表述的不够清楚,所以希望各位在阅读的时候保持谅解,如果有问题欢迎讨论或者指针,那么中断的实验到这里就结束了

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【采集软件】小红书评论高级爬取工具

用python开发了一个爬虫采集软件&#xff0c;叫【爬小红书搜索评论软件】&#xff0c;支持2种模式的评论采集&#xff1a; 1. 根据关键词采集评论&#xff0c;爬取思路&#xff1a;笔记关键词->笔记链接->评论 2. 根据笔记链接采集评论&#xff0c;爬取思路&#xff1a;…

每日一练:翻转二叉树

226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; 一、题目要求 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff…

MATLAB Function模块用法案例

Simulink中的MATLAB Function模块是一个非常灵活的工具&#xff0c;允许用户直接在Simulink模型中嵌入MATLAB代码&#xff0c;以实现自定义的算法或功能 MATLAB Function模块的基本用法 添加模块&#xff1a; 在Simulink的模型窗口中&#xff0c;通过搜索“MATLAB Function”…

大数据新视界 --大数据大厂之 Reactjs 在大数据应用开发中的优势与实践

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

2024 年最新前端工程师 Vue3 框架详细教程(更新中)

vue3 前端框架概述 vue 3 是 Vue.js 的最新版本&#xff0c;是一个用于构建用户界面的渐进式 JavaScript 框架。和 vue 2 相比&#xff0c;vue 3 引入了组合式 API&#xff0c;使开发者可以通过函数组织代码逻辑&#xff0c;适合处理复杂功能。vue 3 在性能上也有显著提升&…

AI Agent智能应用从0到1定制开发Langchain+LLM全流程解决方案与落地实战

大模型微调实战&#xff1a;精通、指令微调、开源大模型微调、对齐与垂直领域应用29套AI全栈大模型项目实战&#xff0c;人工智能视频课程-多模态大模型&#xff0c;微调技术训练营&#xff0c;大模型多场景实战&#xff0c;AI图像处理&#xff0c;AI量化投资&#xff0c;OPenC…

使用Charles抓包Android App数据

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 抓包环境准备 1. 下载安装charles charles下载地址&#xff1a;https://www.charlesproxy.com/latest-release/download.do 2. SSL代理设置 3. http代理和…

【计算机网络强化】计网强化笔记

第一章 计算机网络体系结构 1.1 计算机网络概述 1.计算机网络由若干个节点和连接这些节点的链路组成 2. 3.计算机网络的组成 ①硬件、软件、协议 ②边缘部分和核心部分 ③通信子网和资源子网 4.电路交换、报文交换和分组交换 ①电路交换 分为三步&#xff1a;建立连接、…

使用SBP打AssetBundle时脚本引用丢失

1&#xff09;使用SBP打AssetBundle时脚本引用丢失 2&#xff09;在UE 5.3中连接Power节点为何10的3次幂等于1009 3&#xff09;如何在Widget中倾斜一张纹理贴图 4&#xff09;如何在打开关卡蓝图时更改游戏模式 这是第401篇UWA技术知识分享的推送&#xff0c;精选了UWA社区的热…

.NET 6.0 WebAPI 使用JWT生成Token的验证授权

1.引入相关程序包JwtBearer注意版本: 2.配置文件appsettings.json写相关配置参数(也可不写&#xff0c;写在程序里面&#xff0c;数据库读取也是一样的) , //JWT加密"JWTToken": {"SecretKey": "jsaduwqe6asdjewejdue7dfmsdfu0sdfmwmsd8wfsd6",…

Postman导出报告

一、下载node.js 导出测试报告我们需要用到一个工具叫做newman&#xff0c;它是node.js开发的一个插件&#xff0c;要使用他需要先下载node.js&#xff0c;安装包可以去官网下载&#xff0c;这里我分享一个 链接: https://pan.baidu.com/s/179yLzwTtLH3eihYs_yxrZA?pwd7bqt 提…

数据分析:主成分以及贡献变量解析

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍分析目的变量的loadings的含义加载依赖包导入数据数据预处理PCA计算PCA图主成分分布系统信息介绍 PCA分析,即主成分分析(Principal Component Analysis),是一种统计方法,用于…

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)2.3-2.4

目录 第四门课 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;第二周 深度卷积网络&#xff1a;实例探究&#xff08;Deep convolutional models: case studies&#xff09;2.3 残差网络(ResNets)(Residual Networks (ResNets))2.4 残差网络为什么有用&am…

在虚幻引擎中实时显示帧率

引擎自带了显示帧率的功能 但是只能在编辑器中显示 , 在游戏发布后就没有了 , 所以我们要自己做一个 创建一个控件蓝图 创建画布和文本 , 修改文本 文本绑定函数 , 点击创建绑定 添加一个名为 FPS 的变量 格式化文本 用大括号把变量包起来 {FPS Int} FPS 然后转到事件图表…

RHCS认证-Linux(RHel9)-Ansible

文章目录 一、ansible 简介二 、ansible部署三、ansible服务端测试四 、ansible 清单inventory五、Ad-hot 点对点模式六、YAML语言模式七、RHCS-Ansible附&#xff1a;安装CentOS-Stream 9系统7.1 ansible 执行过程7.2 安装ansible&#xff0c;ansible-navigator7.2 部署ansibl…

一看就会!PS2024下载安装教程详解

PS2024下载方法&#xff1a; PS2024安装教程&#xff1a; 1、右击【PS2024.zip】&#xff0c;选择【解压到PS2024】 2、右击【Set-up.exe】&#xff0c;选择【以管理员身份运行】 3、点击右下角灰色的小文件夹图标&#xff0c;选择【更改位置】 4、选择安装路径后&#xff0c;…

策略模式与工厂模式的区别

《策略模式与工厂模式的区别》 策略模式&#xff08;Strategy Pattern&#xff09; 和 工厂模式&#xff08;Factory Pattern&#xff09; 都是常见的设计模式&#xff0c;虽然它们在设计目标上有一些相似之处&#xff0c;如解耦代码、增强扩展性&#xff0c;但它们的应用场景和…

数字化转型中的供应链管理优化

在当今全球化和数字化的浪潮下&#xff0c;企业供应链管理面临着前所未有的挑战和机遇&#xff0c;企业在数字化转型过程中&#xff0c;如何优化供应链管理成为提升竞争力的关键。通过应用先进技术如RPA机器人流程自动化、大数据分析、物联网等&#xff0c;企业可以显著提高物流…

go解决引入私有包报错“Repository owner does not exist“的两种方式

当你写好引入的私有包,执行go mod tidy报错: Gogs: Repository owner does not exist fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. 目前我的两种解决方案: 一、拉群整个…

基于WebServer的工业数据采集系统

一、项目框架及流程 二、http简介 HTTP协议是Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;的缩写&#xff0c;是用于Web Browser&#xff08;浏览器&#xff09;到Web Server&#xff08;服务器&#xff09;进行数据交互的传输协议。 HTTP是应用层协…