IMX6UL开发板中断实验(三)

news2024/9/27 10:22:08

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

        下面就是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/int

				   			   

SRCDIRS			:= project \

				   bsp/clk \

				   bsp/led \

				   bsp/delay \

				   bsp/beep \

				   bsp/gpio \

				   bsp/key \

				   bsp/exit \

				   bsp/int

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

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

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

相关文章

TortoiseGit 下载和安装

下载 1&#xff0c;下载路径 Download – TortoiseGit – Windows Shell Interface to Git 2&#xff0c;选择windows64的&#xff0c; 3&#xff0c;下载完成后 安装 1&#xff0c;双击运行&#xff0c;点击next 2&#xff0c;点击next 3&#xff0c;点击next 4&#xff0…

unity_Occlusion_Culling遮挡剔除学习

unity_Occlusion_Culling遮挡剔除学习 文档&#xff1a; https://docs.unity.cn/cn/2019.4/Manual/occlusion-culling-getting-started.html没彻底搞明白&#xff0c;但是会用&#xff0c;虽然也不熟练 设置遮挡剔除 打开遮挡剔除面板 设置场景物体。设置为静态 设置场景 烘…

ZYNQ:开发环境搭建

资料下载 http://47.111.11.73/docs/boards/fpga/zdyz_qimxing(V2).html Vivado软件是什么&#xff1f; Vivado软件是Xilinx&#xff08;赛灵思&#xff09;公司推出的一款集成设计环境&#xff08;IDE&#xff09;&#xff0c;主要用于FPGA&#xff08;现场可编程门阵列&am…

【Spring】Spring Aop基础入门

一、AOP(Aspect-Oriented Programming: 面向切面编程) 将那些与业务无关&#xff0c;却为业务模块所共同调用的逻辑&#xff08;例如事务处理、日志管理、权限控制等&#xff09;封装抽取成一个可重用的模块&#xff0c;这个模块被命名为“切面”&#xff08;Aspect&#xff09…

【Linux系统编程】第二十四弹---从零到一:掌握进程替换的奥秘

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、进程程序替换 1.1、替换原理 1.2、替换函数 1.3、函数解释 1.4、命名理解 1.5、代码演示 1.5.1、execl调用举例 1.5…

线上环境排故思路与方法GC优化策略

前言 这是针对于我之前[博客]的一次整理&#xff0c;因为公司需要一些技术文档的定期整理与分享&#xff0c;我就整理了一下。(https://blog.csdn.net/TT_4419/article/details/141997617?spm1001.2014.3001.5501) 其实&#xff0c;nginx配置 服务故障转移与自动恢复也是可以…

九、子查询

文章目录 0. 引入1. 需求分析与问题解决1.1 实际问题1.2 子查询的基本使用1.3 子查询的分类1.3.1 分类方式11.3.2 分类方式2 2. 单行子查询2.1 单行比较操作符2.2 代码实例2.3 HAVING 中的子查询2.4 CASE中的子查询2.5 子查询中的空值问题2.6 非法使用子查询 3. 多行子查询3.1 …

FPGA实现PCIE图片采集转HDMI输出,基于XDMA中断架构,提供3套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的PCIE方案 3、PCIE基础知识扫描4、工程详细设计方案工程设计原理框图测试图片QT上位机XDMA配置及使用XDMA中断模块FDMA图像缓存Native视频时序生成RGB转HDMI输出模块Windows版本XDMA驱动安装Linux版本XDMA驱动安装工程源码…

awvs工具测试输出漏洞报告教程

1.登录进awvs 2.添加要扫描的网站url 3.这里选择生成新的报告 4.开始扫描 5.生成报告

Spring Boot 核心概念

1. 起步依赖 Spring Boot 提供了一系列以 spring-boot-starter- 为前缀的起步依赖&#xff0c;通过这些依赖&#xff0c;可以快速引入 Web、JPA、Security、Redis 等功能。例如&#xff1a; spring-boot-starter-web&#xff1a;包含 Web 开发常见的依赖&#xff0c;如 Sprin…

TypeScript 设计模式之【备忘录模式】

文章目录 备忘录模式&#xff1a;时光机器的魔法备忘录模式的奥秘备忘录模式有什么利与弊?如何使用备忘录模式来优化你的系统代码实现案例备忘录模式的主要优点备忘录模式的主要缺点备忘录模式的适用场景总结 备忘录模式&#xff1a;时光机器的魔法 想象一下&#xff0c;如果…

调用飞书接口导入供应商bug

1、业务背景 财务这边大部分系统都是供应商项目&#xff0c;由于供应商的研发人员没有飞书项目的权限&#xff0c;涉及到供应商系统需求 财务这边都是通过多维表格进行bug的生命周期管理如图&#xff1a; 但多维表格没有跟飞书项目直接关联&#xff0c;测试组做bug统计的时候无…

【Docker】01-Docker常见指令

1. Docker Docker会下载镜像&#xff0c;运行的时候&#xff0c;创建一个隔离的环境&#xff0c;称为容器。 docker run -d \ # 创建并运行一个容器&#xff0c;-d表示后台运行 --name mysql \ # 容器名称-p 3307:3306 \ # 端口映射&#xff0c;宿主机端口映射到容器端口-e TZ…

打造同城O2O平台:外卖跑腿APP的架构与功能设计详解

今天&#xff0c;小编将于大家共同讨论外卖跑腿APP的架构设计及其核心功能&#xff0c;旨在为开发者提供一份详尽的参考。 一、外卖跑腿APP的架构设计 1.整体架构概述 通常包括前端、后端和数据库。 2.前端设计 用户端提供直观的界面&#xff0c;方便用户下单、查询订单状态…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《基于自适应时段划分的含氢微能网中长期变分辨率调度》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

Rust gRPC---Tonic实战

API 一个API做了两件事 客户端发起请求Request服务端作出响应Response REST是什么 REST&#xff08;Representational State Transfer&#xff09;&#xff1a;表现层状态传输&#xff0c;是一种设计风格&#xff0c;通常将 HTTP API 称为 RESTful API、RESTful 服务或 RES…

AI大模型生态暨算力大会今日举行,20位大咖领衔共探「AI NATIVE,生成未来」

出品&#xff5c;AI大模型工场 9月25日消息&#xff0c;由AI大模型工场主办AI大模型生态暨算力大会今日举行。作为国内最具影响力与最懂大模型的AI生态大会&#xff0c;大会讨论了AI大模型的最新进展和未来发展趋势。 2024年被业内称为大模型应用落地元年&#xff0c;大模型产…

【入门01】arcgis api 4.x 创建地图、添加图层、添加指北针、比例尺、图例、卷帘、图层控制、家控件(附完整源码)

1.效果 2.代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title></title><link rel"s…

JSP+Servlet+Mybatis实现列表显示和批量删除等功能

前言 使用JSP回显用户列表&#xff0c;可以进行批量删除&#xff08;有删除确认步骤&#xff09;&#xff0c;和修改用户数据&#xff08;用户数据回显步骤&#xff09;使用servlet处理传递进来的请求参数&#xff0c;并调用dao处理数据并返回使用mybatis&#xff0c;书写dao层…

828华为云征文|华为云Flexus云服务器X实例的网络性能测试

828华为云征文&#xff5c;华为云Flexus云服务器X实例的网络性能测试 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、iperf3工具介绍2.1 iperf3简介2.2 iperf3特点 三、本次实践介绍3.1 本次实践…