GEC210(S5PV210)裸机驱动之中断系统

本文介绍了GEC210(S5PV210)开发板上中断系统的详细流程,包括从中断发生到服务程序执行的整个过程。文章探讨了中断驱动的编写思路,提供了一个基本框架,并强调了编写外设中断服务程序的关键步骤。内容涵盖了全局中断开启、异常向量表设置、中断使能和模式配置等,旨在帮助开发者理解并实现ARM平台的中断驱动。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主机平台:Linux CentOS 6.5

arm平台:粤嵌GEC210开发板(S5PV210)


中断是arm里面很重要的一个部分,但S5PV210数据手册中中断那一章里讲的基本是一些概述性的内容,没有具体的中断实现流程,如果没有参考例程的话,完全不知到如何入手写一个中断程序。(或者其它地方有,但是我没有找到,哪位知道的麻烦告诉我一下)


下面把一些我认为比较重要的归纳下。因为是个人归纳的,而且有些地方涉及到芯片内部的操作,所以在某些细节方面可能会有不严谨的地方,这些地方我会加一些注释的,但在整体的流程上应该是没有问题的。

此外,下面所有的内容都以IRQ中断为例。


首先是:

中断系统接收到一个由外设来的中断信号时,会执行:

1.检测全局的IRQ中断是否打开。(注:这里也有可能是:若全局的IRQ中断没有打开,中断系统根本不会接收到中断信号)

2.若全局IRQ中断以打开,检查对应外设的中断在中断系统对应寄存器是否使能(注:同上,也可能并非检测)

3.若使能,将对应中断入口地址加载到其所在中断组的VIC0ADDRESS寄存器中,并置位对应的中断状态标志位。(注:这一步的位置不太确定)

4.跳转到异常向量表中,查找IRQ中断的对应入口地址,并进入此地址中执行通用中断服务程序,这个中断服务程序是所有IRQ中断共用的。

5.建立中断所需的环境(注:这里不太清楚具体做了那些工作,至少在我调试过程中,会自动将SP设置为IRQ模式下的对应值)

5.5.这步与中断系统无关,是在程序中通过中断系统的相关寄存器,从通用的中断服务程序跳转到与外设相关的特定中断服务程序。

6.当从特定中断服务程序返回时,会再次进入通用的中断服务程序,

7.从中断服务程序返回,恢复环境,此次中断结束


下面说一下当要写一个外设的中断驱动时的具体步骤:

1.关闭IRQ全局中断

2.编写通用服务程序

3.设置异常向量表

4.清除中断系统中所有中断的使能位(置位VICxINTENCLEAR)

5.设置所有中断的模式为IRQ(默认就是IRQ模式,可省略)

6清除VICxADDR(置0)

7.使能IRQ全局中断

上面是系统复位后,或者需要重置中断系统的状态时必须的步骤,下面就是针对某个特定中断的步骤

8.编写外设对应的中断服务程序(第8、9步是中断驱动的重点)

9.设置外设寄存器初始化外设(包括清除中断标志位,使能中断等操作)

10.根据外设查找中断源表获得其中断号

11.根据中断号设置对应的中断服务程序入口地址寄存器(VICxVECTADDRx)

12.根据中断号使能对应中断




只是讲也不太清除要怎么做,下面就贴一下代码,看一下具体的实现过程(代码是参考网上的一段中断程序修改的,但不知到出处,总之感谢一下原作者)

main.c文件

/******************************************
*main.c
******************************************/
extern void IRQ_handle();
extern void irq_handler();

void exceptionundef(void)
{
    //printf("undefined instruction exception.\n");

    while(1);
}

void exceptionswi(void)
{
    //printf("swi exception.\n");

	while(1);
}

void exceptionpabort(void)
{
    //printf("pabort exception.\n");

	while(1);
}

void exceptiondabort(void)
{

    //printf("dabort exception.\n");
    while(1);
}


void system_vector_init( void)  //第3步:初始化异常向量表
{  
    pExceptionUNDEF   = (unsigned long)exceptionundef;  
    pExceptionSWI     = (unsigned long)exceptionswi;  
    pExceptionPABORT  = (unsigned long)exceptionpabort;  
    pExceptionDABORT  = (unsigned long)exceptiondabort;  
    pExceptionIRQ     = (unsigned long)IRQ_handle;  //IRQ通用中断服务程序入口
    pExceptionFIQ     = (unsigned long)IRQ_handle; 
}


void clear_vicaddress()
{     
   //清除VECTADDRx寄存器       
       <span style="font-size:14px;"> </span>VIC0ADDRESS=0X00000000;
	VIC1ADDRESS=0X00000000;
	VIC2ADDRESS=0X00000000;
	VIC3ADDRESS=0X00000000;
}

void interrupt_init()
{
	//关闭所有中断
	VIC0INTENCLEAR=0xFFFFFFFF;
	VIC1INTENCLEAR=0xFFFFFFFF;
	VIC2INTENCLEAR=0xFFFFFFFF;
	VIC3INTENCLEAR=0xFFFFFFFF;

	//设置中断模式为IRQ模式
	VIC0INTMOD=0x00000000;
	VIC1INTMOD=0x00000000;
	VIC2INTMOD=0x00000000;
	VIC3INTMOD=0x00000000;

	//清除VECTADDRx寄存器
	clear_vicaddress();
}

void interrupt_enable(int int_num)   //使能特定中断号对应的中断
{
	unsigned long temp;
	if(int_num<32)
	{
		temp = VIC0INTENABLE;
		temp |= (1<<int_num);
		VIC0INTENABLE=temp;
	}
	else if(int_num<64)
	{
		temp = VIC1INTENABLE;
		temp |= (1<<(int_num-32));
		VIC1INTENABLE=temp;
	}
	else if(int_num<96)
	{
		temp = VIC2INTENABLE;
		temp |= (1<<(int_num-64));
		VIC2INTENABLE=temp;
	}
	else if(int_num<128)
	{
		temp = VIC0INTENABLE;
		temp |= (1<<(int_num-96));
		VIC3INTENABLE=temp;
	}
}


void interrupt_disable(int int_num)  //除能特定中断号对应的中断
{
	unsigned long temp;
	if(int_num<32)
	{
		temp = VIC0INTENCLEAR;
		temp |= (1<<int_num);
		VIC0INTENCLEAR=temp;
	}
	else if(int_num<64)
	{
		temp = VIC1INTENCLEAR;
		temp |= (1<<(int_num-32));
		VIC1INTENCLEAR=temp;
	}
	else if(int_num<96)
	{
		temp = VIC2INTENCLEAR;
		temp |= (1<<(int_num-64));
		VIC2INTENCLEAR=temp;
	}
	else if(int_num<128)
	{
		temp = VIC3INTENCLEAR;
		temp |= (1<<(int_num-96));
		VIC3INTENCLEAR=temp;
	}
}


void set_int_vectaddr(unsigned long int_num,void(*func)(void)) //设置特定中断号的服务程序入口地址
{
	if(int_num<32)
		*(volatile unsigned long *)(VIC0VECTADDR+int_num*4)=(unsigned long)func;
	else if(int_num<64)
		*(volatile unsigned long *)(VIC1VECTADDR+(int_num-32)*4)=(unsigned long)func;
	else if(int_num<96)
		*(volatile unsigned long *)(VIC2VECTADDR+(int_num-64)*4)=(unsigned long)func;
	else if(int_num<128)
		*(volatile unsigned long *)(VIC3VECTADDR+(int_num-96)*4)=(unsigned long)func;
}


void enable_global_IRQ()  //打开全局IRQ中断
{
	asm("stmfd sp!,{r0}\n"
		"mrs r0, cpsr\n"
		"bic r0,r0,#0x80\n"
		"msr cpsr, r0\n"
		"ldmfd sp!,{r0}\n");
}

void disable_global_IRQ()   //关闭全局IRQ中断
{
	asm("stmfd sp!,{r0}\n"
		"mrs r0, cpsr\n"
		"and r0,r0,#0x80\n"
		"msr cpsr, r0\n"
		"ldmfd sp!,{r0}\n");
}


unsigned long get_irq_status(unsigned long vic_num)  //检测那个中断发生了,在irq_handler中调用
{
	unsigned long irq_status_addr[4]={VIC0IRQSTATUS,VIC1IRQSTATUS,VIC2IRQSTATUS,VIC3IRQSTATUS};
	if(vic_num<4)
		return irq_status_addr[vic_num];
	return 0;
}


void irq_handler()//第2步:编写通用中断服务程序 汇编部分
<span style="font-size:14px;">{
    unsigned long vicaddr[4]={VIC0ADDRESS,VIC1ADDRESS,VIC2ADDRESS,VIC3ADDRESS};
    unsigned long i=0,temp=0,status=0;
    void (*isr)(void)=(void (*)(void))0;



    //下面的for循环就是选出真正发生中断的入口地址,将其赋值给isr   
    for(i=0;i<4;i++)
    {
        status=get_irq_status(i);
        if(status)
        {

            isr=(void (*)(void))vicaddr[i];

            break;
        }
    }

    
    //如果isr为0则出错,否则进入特定的中断服务程序
    if((unsigned long)isr==0)
    {
        disable_global_IRQ();
        error();
    }
    (*isr)();
        
 
    
}


void timer0_int_func() //第9步:编写外设对应的中断服务程序
{
    TINT_CSTAT |= 0x3e0;
    clear_vicaddress();
    enable_global_IRQ();
    mputs("in timer0\r\n");
}

void timer0_init()   
{
    //第8步:初始化外设寄存器
    TCFG0_REG = 0xffff;
    TCFG1_REG = 0x4;
    TCNTB0_REG = 16113;
    TCON_REG |= 0x2;
    TCON_REG &=~0x2;
    TCON_REG |=  0x8;
    TCON_REG |= 0x1;
    TINT_CSTAT |= 0x3e1;
    

    set_int_vectaddr(21,timer0_int_func); //第11步:设置中断入口地址
    interrupt_enable(21);                 //第12步:使能中断系统中的中断
}

int main()
{
    disable_global_IRQ()关闭全局中断;      
    system_vector_init(); //第3步:初始化异常向量表  
    interrupt_init();     //第4、5、6步:初始化中断寄存器
    enable_global_IRQ();  //第7步:打开IRQ全局中断
   //下面是根据相应的外设初始化各个寄存器
    timer0_init();
  while(1);
}


  

start.S文件

/******************************************
*start.S
******************************************/
IRQ_handle:
    ldr sp,=0xD0037F80
    sub lr,lr,#4
    stmfd sp!,{r0-r12,lr}
    bl irq_handler
    ldmfd sp!,{r0-r12,PC}





好了,这次主要写的是裸机中断驱动的编写思路,也可以说是一个框架,以后写某个中断驱动时,最主要修改的是上面所讲的第8、9步,其它地方基本上是不需要修改的。


后面会针对各个外设编写出相应的驱动的,这次就到这里了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值