博客
关于我
【ARM自学笔记】ARM Cortex -A中断系统(程序篇)
阅读量:577 次
发布时间:2019-03-11

本文共 12089 字,大约阅读时间需要 40 分钟。

原理说明

点击此文章:

功能描述

当按下按键后,蜂鸣器工作,再次按下后关闭蜂鸣器。按键采用中断方式处理。

流程

  1. 移植SDK中中断相关文件
  2. 编写start.s文件
  3. 编写中断驱动文件
  4. 编写GPIO驱动文件
  5. 编写按键驱动文件
  6. 编写main文件
  7. 编译烧录

移植core_ca7.h文件

重要API函数:

函数 描述
GIC_Init 初始化 GIC
GIC_EnableIRQ 使能指定的外设中断
GIC_DisableIRQ 关闭指定的外设中断
GIC_AcknowledgeIRQ 返回中断号
GIC_DeactivateIRQ 无效化指定中断
GIC_GetRunningPriority 获取当前正在运行的中断优先级
GIC_SetPriorityGrouping 设置抢占优先级位数
GIC_GetPriorityGrouping 获取抢占优先级位数
GIC_SetPriority 设置指定中断的优先级
GIC_GetPriority 获取指定中断的优先级

编写start.s文件

该文件主要完成启动设置,以及配置C语言环境

.global _start  				/* * 中断向量表的创建 */_start:	ldr pc, =Reset_Handler		/* 复位中断 					*/		ldr pc, =Undefined_Handler	/* 未定义中断 					*/	ldr pc, =SVC_Handler		/* SVC(Supervisor)中断 		*/	ldr pc, =PrefAbort_Handler	/* 预取终止中断 					*/	ldr pc, =DataAbort_Handler	/* 数据终止中断 					*/	ldr	pc, =NotUsed_Handler	/* 未使用中断					*/	ldr pc, =IRQ_Handler		/* IRQ中断 					*/	ldr pc, =FIQ_Handler		/* FIQ(快速中断)未定义中断 			*//* 复位中断 */	Reset_Handler:	cpsid i						/* 关闭全局中断 */	/*  	 * 关闭I,DCache和MMU。	 */	mrc     p15, 0, r0, c1, c0, 0     /* 读取CP15的C1寄存器到R0中       		        	*/    bic     r0,  r0, #(0x1 << 12)     /* 清除C1寄存器的bit12位(I位),关闭I Cache            	*/    bic     r0,  r0, #(0x1 <<  2)     /* 清除C1寄存器的bit2(C位),关闭D Cache    				*/    bic     r0,  r0, #0x2             /* 清除C1寄存器的bit1(A位),关闭对齐						*/    bic     r0,  r0, #(0x1 << 11)     /* 清除C1寄存器的bit11(Z位),关闭分支预测					*/    bic     r0,  r0, #0x1             /* 清除C1寄存器的bit0(M位),关闭MMU				       	*/    mcr     p15, 0, r0, c1, c0, 0     /* 将r0寄存器中的值写入到CP15的C1寄存器中	 				*/	#if 0	/* 设置中断向量表偏移 */	ldr r0, =0X87800000	dsb	isb	mcr p15, 0, r0, c12, c0, 0	dsb	isb#endif    	/* 	 * 设置各个模式下的栈指针,	 */	/* 进入IRQ模式 */	mrs r0, cpsr	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/	orr r0, r0, #0x12 	/* r0或上0x13,表示使用IRQ模式					*/	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/	ldr sp, =0x80600000	/* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB */	/* 进入SYS模式 */	mrs r0, cpsr	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/	orr r0, r0, #0x1f 	/* r0或上0x13,表示使用SYS模式					*/	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/	ldr sp, =0x80400000	/* 设置SYS模式下的栈首地址为0X80400000,大小为2MB */	/* 进入SVC模式 */	mrs r0, cpsr	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/	orr r0, r0, #0x13 	/* r0或上0x13,表示使用SVC模式					*/	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/	ldr sp, =0X80200000	/* 设置SVC模式下的栈首地址为0X80200000,大小为2MB */	cpsie i				/* 打开全局中断 */#if 0	/* 使能IRQ中断 */	mrs r0, cpsr		/* 读取cpsr寄存器值到r0中 			*/	bic r0, r0, #0x80	/* 将r0寄存器中bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断 */	msr cpsr, r0		/* 将r0重新写入到cpsr中 			*/#endif	b main				/* 跳转到main函数 			 	*//* 未定义中断 */Undefined_Handler:	ldr r0, =Undefined_Handler	bx r0/* SVC中断 */SVC_Handler:	ldr r0, =SVC_Handler	bx r0/* 预取终止中断 */PrefAbort_Handler:	ldr r0, =PrefAbort_Handler		bx r0/* 数据终止中断 */DataAbort_Handler:	ldr r0, =DataAbort_Handler	bx r0/* 未使用的中断 */NotUsed_Handler:	ldr r0, =NotUsed_Handler	bx r0/* IRQ中断 */IRQ_Handler:	push {   lr}					/* 保存lr地址 */	push {   r0-r3, r12}			/* 保存r0-r3,r12寄存器 */	mrs r0, spsr				/* 读取spsr寄存器 */	push {   r0}					/* 保存spsr寄存器 */	mrc p15, 4, r1, c15, c0, 0 /* 	                            * 从CP15的C0寄存器内的值到R1寄存器中								*/								add r1, r1, #0X2000			/* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */	ldr r0, [r1, #0XC]			/* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,								 * GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据								 * 这个中断号来绝对调用哪个中断服务函数								 */	push {   r0, r1}				/* 保存r0,r1 */		cps #0x13					/* 进入SVC模式,允许其他中断再次进去 */		push {   lr}					/* 保存SVC模式的lr寄存器 */	ldr r2, =system_irqhandler	/* 加载C语言中断处理函数到r2寄存器中*/	blx r2						/* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */	pop {   lr}					/* 执行完C语言中断服务函数,lr出栈 */	cps #0x12					/* 进入IRQ模式 */	pop {   r0, r1}					str r0, [r1, #0X10]			/* 中断执行完成,写EOIR */	pop {   r0}							msr spsr_cxsf, r0			/* 恢复spsr */	pop {   r0-r3, r12}			/* r0-r3,r12出栈 */	pop {   lr}					/* lr出栈 */	subs pc, lr, #4				/* 将lr-4赋给pc */		/* FIQ中断 */FIQ_Handler:	ldr r0, =FIQ_Handler		bx r0

这里主要用到IRQ中断,其它中断都写成了死循环。

  1. 编写中断驱动文件
    头文件:
#ifndef _BSP_INT_H#define _BSP_INT_H#include "imx6ul.h"/* 中断服务函数形式 */ typedef void (*system_irq_handler_t) (unsigned int giccIar, void *param); /* 中断服务函数结构体*/typedef struct _sys_irq_handle{       system_irq_handler_t irqHandler; /* 中断服务函数 */    void *userParam;                 /* 中断服务函数参数 */} sys_irq_handle_t;void int_init(void);void system_irqtable_init(void);void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam);void system_irqhandler(unsigned int giccIar); void default_irqhandler(unsigned int giccIar, void *userParam); #endif

驱动文件:

#include "bsp_int.h"/* 中断嵌套计数器 */static unsigned int irqNesting;/* 中断服务函数表 */static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS];/* * @description	: 中断初始化函数 * @param		: 无 * @return 		: 无 */void int_init(void){   	GIC_Init(); 						/* 初始化GIC 	*/	system_irqtable_init();				/* 初始化中断表 		*/	__set_VBAR((uint32_t)0x87800000); 	/* 中断向量表偏移,偏移到起始地址   	*/}/* * @description	: 初始化中断服务函数表  * @param		: 无 * @return 		: 无 */void system_irqtable_init(void){   	unsigned int i = 0;	irqNesting = 0;		/* 先将所有的中断服务函数设置为默认值 */	for(i = 0; i < NUMBER_OF_INT_VECTORS; i++){   		system_register_irqhandler((IRQn_Type)i,default_irqhandler, NULL);	}}/* * @description			: 给指定的中断号注册中断服务函数  * @param - irq			: 要注册的中断号 * @param - handler		: 要注册的中断处理函数 * @param - usrParam	: 中断服务处理函数参数 * @return 				: 无 */void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam) {   	irqTable[irq].irqHandler = handler;  	irqTable[irq].userParam = userParam;}/* * @description			: C语言中断服务函数,irq汇编中断服务函数会 						  调用此函数,此函数通过在中断服务列表中查 						  找指定中断号所对应的中断处理函数并执行。 * @param - giccIar		: 中断号 * @return 				: 无 */void system_irqhandler(unsigned int giccIar) {      uint32_t intNum = giccIar & 0x3FFUL;      /* 检查中断号是否符合要求 */   if ((intNum == 1023) || (intNum >= NUMBER_OF_INT_VECTORS)) {   	 	return;   }    irqNesting++;	/* 中断嵌套计数器加一 */   /* 根据传递进来的中断号,在irqTable中调用确定的中断服务函数*/   irqTable[intNum].irqHandler(intNum, irqTable[intNum].userParam);    irqNesting--;	/* 中断执行完成,中断嵌套寄存器减一 */}/* * @description			: 默认中断服务函数 * @param - giccIar		: 中断号 * @param - usrParam	: 中断服务处理函数参数 * @return 				: 无 */void default_irqhandler(unsigned int giccIar, void *userParam) {   	while(1) {      	}}

编写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;/* * GPIO中断触发类型枚举 */typedef enum _gpio_interrupt_mode{       kGPIO_NoIntmode = 0U, 				/* 无中断功能 */    kGPIO_IntLowLevel = 1U, 			/* 低电平触发	*/    kGPIO_IntHighLevel = 2U, 			/* 高电平触发 */    kGPIO_IntRisingEdge = 3U, 			/* 上升沿触发	*/    kGPIO_IntFallingEdge = 4U, 			/* 下降沿触发 */    kGPIO_IntRisingOrFallingEdge = 5U, 	/* 上升沿和下降沿都触发 */} gpio_interrupt_mode_t;	/* * GPIO配置结构体 */	typedef struct _gpio_pin_config{       gpio_pin_direction_t direction; 		/* GPIO方向:输入还是输出 */    uint8_t outputLogic;            		/* 如果是输出的话,默认输出电平 */	gpio_interrupt_mode_t interruptMode;	/* 中断方式 */} 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);void gpio_intconfig(GPIO_Type* base, unsigned int pin, gpio_interrupt_mode_t pinInterruptMode);void gpio_enableint(GPIO_Type* base, unsigned int pin);void gpio_disableint(GPIO_Type* base, unsigned int pin);void gpio_clearintflags(GPIO_Type* base, unsigned int pin);#endif

GPIO文件:

#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){   	base->IMR &= ~(1U << pin);		if(config->direction == kGPIO_DigitalInput) /* GPIO作为输入 */	{   		base->GDIR &= ~( 1 << pin);	}	else										/* 输出 */	{   		base->GDIR |= 1 << pin;		gpio_pinwrite(base,pin, config->outputLogic);	/* 设置默认输出电平 */	}	gpio_intconfig(base, pin, config->interruptMode);	/* 中断功能配置 */}/* * @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); /* 输出高电平 */	 }}/* * @description  			: 设置GPIO的中断配置功能 * @param - base 			: 要配置的IO所在的GPIO组。 * @param - pin  			: 要配置的GPIO脚号。 * @param - pinInterruptMode: 中断模式,参考枚举类型gpio_interrupt_mode_t * @return		 			: 无 */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;	}}/* * @description  			: 使能GPIO的中断功能 * @param - base 			: 要使能的IO所在的GPIO组。 * @param - pin  			: 要使能的GPIO在组内的编号。 * @return		 			: 无 */void gpio_enableint(GPIO_Type* base, unsigned int pin){        base->IMR |= (1 << pin);}/* * @description  			: 禁止GPIO的中断功能 * @param - base 			: 要禁止的IO所在的GPIO组。 * @param - pin  			: 要禁止的GPIO在组内的编号。 * @return		 			: 无 */void gpio_disableint(GPIO_Type* base, unsigned int pin){        base->IMR &= ~(1 << pin);}/* * @description  			: 清除中断标志位(写1清除) * @param - base 			: 要清除的IO所在的GPIO组。 * @param - pin  			: 要清除的GPIO掩码。 * @return		 			: 无 */void gpio_clearintflags(GPIO_Type* base, unsigned int pin){       base->ISR |= (1 << pin);}

按键驱动文件

#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); /* 清除中断标志位 */}

编写main文件

#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;}

编译下载

在这里插入图片描述

结果

符合预期功能。

最后

首先,感谢正点原子的课程以及资料,让我能深入学习ARM+Linux。裸机编程很复杂(相对于学习stm32来说是真很难),ARM7这块底层东西比较多,而且不再像以前学习一样有着成堆好用又方便的工具。这段时间里,我甚至认为裸机操作复杂且毫无意义。但是回过头来想一想,之前学习STM32的过程存在者一些漏洞,屏蔽掉了底层的原理(ARM-M内核掌握不深),注重应用,导致我经常遇到瓶颈,然后去补基础知识,然后再拐回来,反反复复,学习过程就很不流畅,比如RTOS系统,我理解的就不是很透彻(以后还需要进一步学习)。我想,这些裸机操作存在必有它的道理,是为了嵌入式Linux系统打下扎实的基础。不能再像以前,着急出结果,却把最重要最基础的东西没掌握透彻。道阻且长,继续努力吧!

转载地址:http://shntz.baihongyu.com/

你可能感兴趣的文章
解决微信小程序中 calc 失效问题
查看>>
web中的本地存储和小程序中的本地存储的区别
查看>>
JS数组去重的方法
查看>>
Python 之变量
查看>>
线性代数 笔记1
查看>>
堆的应用_topK算法和堆排序
查看>>
双向链表
查看>>
并查集(求连通块数量)
查看>>
蓝桥训练 分考场
查看>>
最大半连通子图
查看>>
牛客多校签到
查看>>
Remove Extra one 维护前缀最大最小值
查看>>
树套树板子
查看>>
Linux命令 - pwd命令
查看>>
Mysql学习笔记
查看>>
最大的和
查看>>
跳台阶
查看>>
另类加法,走方格的方案数,最近公共祖先
查看>>
线程学习5
查看>>
给LaTeX表格加脚注时的奇怪问题(使用PDFLaTeX)
查看>>