中断在裸机开发中是非常重要的一项学习内容,之前学习过STM32的中断实
现,是参考正点原子的代码,通过库函数的方式实现的,为了进一步深度理解
其中的道理,此次通过天嵌的TQ210开发板实现中断方式。
具体功能为,LED正常闪烁,当按键被按下的时候,通过串口打印信息,提
示按键被按下,此过程不影响LED正常闪烁。
中断执行过程具体如下图:
根据示意图,可知道当有中断请求时,首先进行保护现场,然后跳到中断处理函数,执行中断处理函数当中的程序,最后进行恢复现场,程序继续运行。
通过查询开发板原理图得知,按键连接的为外部中断XEINT0,连接的芯片引脚为GPH0,具体细节如下图。
知道了硬件连接就可以进行代码编写了,具体流程如下:
- 配置GPH0引脚为为外部中断模式
- 配置中断触发方式(此处设置为下降沿触发)
- 中断屏蔽配置
- 使能外部中断
通过以上流程中断基本已经完成初始化准备工作。
其中提到了中断屏蔽,此处引用百度百科进行简单的介绍下:
什么叫屏蔽中断?允许中断?怎样实现?
按照是否可以被屏蔽,可将中断分为两大类:不可屏蔽中断(又叫非屏蔽中断)和可屏蔽中断。不可屏蔽中断源一旦提出请求,CPU必须无条件响应,而对可屏蔽中断源的请求,CPU可以响应,也可以不响应。CPU一般设置两根中断请求输入线:可屏蔽中断请求INTR(Interrupt Require)和不可屏蔽中断请求NMI(NonMaskable Interrupt)。对于可屏蔽中断,除了受本身的屏蔽位控制外,还都要受一个总的控制,即CPU标志寄存器中的中断允许标志位IF(Interrupt Flag)的控制,IF位为1,可以得到CPU的响应,否则,得不到响应。IF位可以由用户控制,指令STI或Turbo c的Enable()函数,将IF位置1(开中断),指令CLI或Turbo_c 的Disable()函数,将IF位清0(关中断)。
此外,还需配置中断向量表、优先级、控制等具体配置过程中涉及的中断控制器如下图所示:
具体程序文件由key.h、key.c main.c start.S等文件组成,具体目录结构如下图:
gpio.h 内容如下图
#ifndef _ASM_ARCH_GPIO_H
#define _ASM_ARCH_GPIO_H
struct s5pc1xx_gpio_bank{
unsigned int con;
unsigned int dat;
unsigned int pull;
unsigned int drv;
unsigned int pdn_con;
unsigned int pdn_pull;
unsigned char res1[8];
};
struct s5pv210_gpio{
struct s5pc1xx_gpio_bank gpio_a0;
struct s5pc1xx_gpio_bank gpio_a1;
struct s5pc1xx_gpio_bank gpio_b;
struct s5pc1xx_gpio_bank gpio_c0;
struct s5pc1xx_gpio_bank gpio_c1;
struct s5pc1xx_gpio_bank gpio_d0;
struct s5pc1xx_gpio_bank gpio_d1;
struct s5pc1xx_gpio_bank gpio_e0;
struct s5pc1xx_gpio_bank gpio_e1;
struct s5pc1xx_gpio_bank gpio_f0;
struct s5pc1xx_gpio_bank gpio_f1;
struct s5pc1xx_gpio_bank gpio_f2;
struct s5pc1xx_gpio_bank gpio_f3;
struct s5pc1xx_gpio_bank gpio_g0;
struct s5pc1xx_gpio_bank gpio_g1;
struct s5pc1xx_gpio_bank gpio_g2;
struct s5pc1xx_gpio_bank gpio_g3;
struct s5pc1xx_gpio_bank gpio_i;
struct s5pc1xx_gpio_bank gpio_j0;
struct s5pc1xx_gpio_bank gpio_j1;
struct s5pc1xx_gpio_bank gpio_j2;
struct s5pc1xx_gpio_bank gpio_j3;
struct s5pc1xx_gpio_bank gpio_j4;
};
#define S5PV210_GPIO_BASE (0xE0200000)
struct s5pv210_gph_bank{
unsigned int con;
unsigned int dat;
unsigned int pull;
unsigned int drv;
};
#define S5PV210_GPH0_BASE (0xE0200C00) //GPH0 基地址
#endif
uart.h内容如下图
#ifndef UART_H
#define UART_H
struct s5pv2xx_uart
{
unsigned int ulcon;
unsigned int ucon;
unsigned int ufcon;
unsigned int umcon;
unsigned int utrstat;
unsigned int uerstat;
unsigned int ufstat;
unsigned int umstat;
unsigned char utxh;
unsigned char resl[3];
unsigned char urxh;
unsigned char res2[3];
unsigned int ubrdiv;
unsigned short udivslot;
unsigned char res3[2];
unsigned char res4[0x3d0];
};
#define S5PV210_UART_BASE (0xE2900000)
void uart_init(void);
void myputc(char c);
void myputs(const char *str);
#endif
关键的是key.h 其中的地址一定要正确
#ifndef KEY_H
#define KEY_H
#define pEXECPTION_IRQ (*(volatile unsigned int*)(0xD0037400+0X18))
#define EXT_INT_0_CON (*(volatile unsigned int*)(0xE0200E00)) //外部中断0 控制寄存器
#define EXT_INT_0_MASK (*(volatile unsigned int*)(0xE0200F00)) //外部中断0 mask(屏蔽控制)寄存器
#define EXT_INT_0_PEND (*(volatile unsigned int*)(0xE0200F40)) //外部中断0 pend 阻塞 挂起控制器
#define VIC0INTENABLE (*(volatile unsigned int*)(0xF2000010))//VICO 中断使能控制器
#define VIC0ADDRESS (*(volatile unsigned int*)(0xF2000F00))//VICO 中断地址控制器
#define VIC0VECTADDR0 (*(volatile unsigned int*)(0xF2000100))//VICO 地址O清除寄存器
void key_init(void);
void irq_init(void);
void key1_handler(void);
void irp_c_handler(void);
#endif
key.c文件内容如下图:
#include "cpu_io.h"
#include "gpio.h"
#include "key.h"
#include "uart.h"
extern void asm_irq(void);
void irq_init()
{
pEXECPTION_IRQ=(unsigned int )asm_irq;
}
void irq_c_handler(void)
{
void (*handler)(void)=0x0;
myputs("in irq c handler....\n");
handler =(void (*) (void)) VIC0ADDRESS;
if(handler) handler();
}
void key1_handler(void)
{
myputs("key1 down!!!\n");
EXT_INT_0_PEND |= (0x1<<0);
VIC0ADDRESS=0X0;
}
void key_init(void)
{
struct s5pv210_gph_bank *gph0_base=(struct s5pv210_gph_bank*) S5PV210_GPH0_BASE;
unsigned int var;
/**外部引脚进行功能选择,EINT***/
var =readl(&gph0_base->con);
var|=0xf<<0; //功能选择 0xf 表示配置GPHO引脚为外部中断0
writel(var,&gph0_base->con);
/****配置ENIT功能***************/
EXT_INT_0_CON &= ~(0X7<<0); //低三位清零 为以后配置中断触发方式做准备
EXT_INT_0_CON |=(0x2<<0); //低三位配置为010 即为下降沿触发方式
EXT_INT_0_MASK &= ~(0x1<<0);//使能中断屏蔽
/*****配置主中断控制器**********/
VIC0INTENABLE |= (0X1<<0); //使能中断
VIC0VECTADDR0=(unsigned int)key1_handler;
}
uart.c 文件内容如下图:
#include "cpu_io.h"
#include "uart.h"
#include "gpio.h"
void uart_init(void)
{
struct s5pv210_gpio *gpio_base=(struct s5pv210_gpio *)S5PV210_GPIO_BASE;
struct s5pv2xx_uart *uart_base=(struct s5pv2xx_uart *)S5PV210_UART_BASE;
unsigned int var;
var=readl(&gpio_base->gpio_a0.con);
var &= ~(0xff<<0);
var |= (0x22<<0);
writel(var,&gpio_base->gpio_a0.con);
_REG(&uart_base->ulcon)=0x3; //8 bit 1stop non-even
_REG(&uart_base->ucon) =0x5; //
_REG(&uart_base->ubrdiv)=35;
_REG(&uart_base->udivslot)=0x80;
}
void myputc(char c)
{
struct s5pv2xx_uart *uart_base=(struct s5pv2xx_uart *)S5PV210_UART_BASE;
//判断缓冲区是否为空,再发送数据
while(!(_REG(&uart_base->utrstat)&(0x1<<2)));
_REG(&uart_base->utxh)=c;
}
void myputs(const char *str)
{
while(*str)
{
myputc(*str);
str++;
}
}
main.c函数内容比较少 ,完成闪烁LED和初始化工作:
//main.c
#include "led.h"
#include "cpu_io.h"
#include "uart.h"
#include "key.h"
static void mydelay()
{
volatile unsigned int i=0xfffff;
while(i--);
}
void led_test()
{
led_init();
while(1)
{
led_blink(1);
mydelay();
led_blink(0);
mydelay();
}
}
int main()
{
irq_init();
uart_init();
key_init();
myputs("hello world!\n");
led_test();
return 0;
}
start.S内容如下 ,主要完成中断跳转和保护现场等;
.global _start
.global main
.global irq_c_handler
.global asm_irq
_start :
bl main
loop:
b loop
irq_handler:
stmfd sp!,{r0-r3,lr}
bl irq_c_handler
ldmfd sp!,{r0-r3,lr}
subs pc,lr,#4
asm_irq:
b irq_handler
.end
最后还需要注意的是,下载的时候应该选择SD卡下载,不要选择Dram下载。以上内容仅供参考,如有问题还请指出。