历史上的今天
返回首页

历史上的今天

今天是:2024年10月18日(星期五)

正在发生

2021年10月18日 | 【JZ2440笔记】裸机实验使用中断

2021-10-18 来源:eefocus

一、前言

中断是打断当前程序执行,快速响应突发事件的一种机制。中断的触发源有很多种,比如外部引脚电平变化以及外设的各种事件中断等等,当中断发生时,S3C2440 CPU的PC指针跳往固定的中断向量地址处,执行中断处理函数,之后再返回到原先的程序断点处继续执行程序。


二、实验目标

采用按键中断的方式,通过JZ2440开发板上的三个独立按键分别控制开发板上的三个LED的亮灭。


三、硬件连线

独立按键连线如下:

独立按键只用了三个,分别连接到了EINT0,EINT2,EINT11,即S3C2440的外部中断0,2和11。对应的真实物理引脚分别是GPF0,GPF2和GPG3。S3C2440的外部中断引脚不是随便选的,是定死了哪个引脚能用哪个外部中断的。这点上就比不上STM32这类单片机了,STM32是任意普通引脚都可以做外部中断的。而且STM32支持引脚功能的重映射,就是一个引脚上的各种功能可以映射到其他引脚上用,S3C2440貌似没看到有这个功能。又想了想,毕竟S3C2440是定位于跑操作系统的,和单片机的定位着重点不同,术业有专攻。


LED连线如下:

三个LED分别连接在GPF4,GPF5和GPF6上。


四、S3C2440中断体系

1、工作模式


ARM体系的CPU有以下7中工作模式:


(1)用户模式(usr):ARM处理器正常的程序执行状态。


(2)快速中断模式(fiq):用于高速数据传输或通道处理。


(3)中断模式(irq):用于通用的中断处理。


(4)管理模式(svc):操作系统使用的保护模式。


(5)数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储即存储保护。


(6)系统模式(sys):运行具有特权的操作系统任务。


(7)未定义指令终止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。


以上的模式可通过软件来进行切换,发生各类中断的时候CPU也会自动进入相应的模式。除用户模式外,其他6种工作模式都属于特权模式。大多数程序运行于用户模式,进入特权模式是为了处理中断、异常、或者访问被保护的系统资源。

在S3C2440中,这些不同的模式都有一组寄存器(r0到r15,CPSR和SPSR)与各自模式相对应,这些寄存器有些是共用的,有些是某个模式下独有的(如上图带三角阴影的寄存器),虽然寄存器名字都一样,但只是该模式下使用。这些寄存器除r15外都是通用寄存器,其中r13到r15这3个寄存器有特殊的意义,r13称为栈指针寄存器(SP),就是C语言用到的那个堆栈指针;r14称为程序连接寄存器(subroutime Link Register)或连接寄存器,当执行汇编指令BL进行子程序跳转的时候,r14中会得到r15(程序计数器PC)的备份,当子程序返回的时候,r14(lr寄存器)的值会赋给r15(PC指针),这就使得程序能够回到断点位置继续向下执行了。


每个模式都有独立的r13和r14寄存器,即每个模式都有独立的堆栈指针(SP)和连接寄存器(lr)。在不同模式间跳转的时候,需要将上一个模式的所有公用寄存器进行压栈操作,以便在之后可以再出栈恢复这些寄存器的值,达到模式恢复的效果。其中快中断模式fiq从r8到r15都是独立的,在进入该模式的时候需要保存的寄存器数目更少(r0到r7),所以进入该中断会更快。


还有第17个寄存器CPSR,即“当前程序状态寄存器”(Current Program Status Register)。里面保存了各种状态位。如下:

这里挑几个重点说明:


(1)I位和F位:中断禁止位和快中断禁止位,当这些位置位时,CPU不响应这些中断。


(2)T位:置位时CPU处于Thumb状态,运行Thumb指令集;否则为ARM状态,运行ARM指令集,不同指令集这里不详细分析,在JZ2440上做实验基本都是用ARM指令集。


(3)M0到M4:工作模式位,表示CPU处于什么工作模式,修改这些位,可以使CPU进入对应的工作模式。


还有一个寄存器叫SPSR寄存器(Saved Program Status Register),功能是保存前一个工作模式的CPSR值,这样当要返回一个工作模式时,可以将SPSR的值恢复到CPSR中。


2、中断响应流程

有两类中断请求源:


(1)“Request sources(without sub register)”中的中断源被触发之后,SRCPND(中断请求状态)寄存器中相应位被置1,如果INTMSK(中断屏蔽控制)寄存器未将相应中断屏蔽的话,判断该中断是否是快速中断,如果且快速中断模式是使能的,CPU响应快速中断;如果是普通的中断,PRIORITY(IRQ优先级控制)寄存器的相应位被置1表示产生了该中断,之后INTPND(中断请求状态)寄存器挑出PRIORITY寄存器中的优先级最高的中断进行响应,注意,PRIORITY中可能标记了很多的中断需要处理,但是INTPND一次只挑出一个优先级最高的进行处理,在一个中断没处理完的时候,不响应其他的中断,


(2)“Request sources(with sub register)”这个中断源和(1)的区别可以理解为(1)的中断源是那些大模块比如DMA、定时器,串口等等的某个模块的总的中断请求,而串口模块又存在了RX或者TX中断,这些中断称为中断次级源。该类中断源发生时SUBSRCPND的相应位置位,如果SUBMASK的对应位没有置位来屏蔽这个中断,那么会引发SRCPND的对应位置位,之后的处理流程和(1)中一样。


注意点:


(1)S3C2440的快速中断(fiq)可以打断普通中断(irq),反之不行,普通中断不可以打断普通中断,快速中断也不可以打断快速中断,快速中断(fiq)只能设置一个,即不存在中断嵌套。快速中断(fiq)不引起INTPND和INTOFFSET寄存器的变化,所以这两个寄存器只在IRQ中断中有效。


(2)SUBSRCPND、SRCPND、PRIORITY、INTPND中相应位被置位后,CPU响应了对应的中断过后并不会自动清除这些位,所以需要我们在程序中人为清除对应的位,清除的方法是往对应位写1清除,简单的写法是“INTPND = INTPND”不清除的话该次中断结束之后会立刻进入相同的中断。


(3)不论SUBMASK或者MASK寄存器的相应位有没有置位屏蔽,只要产生了对应的中断,中断请求状态寄存器中都会置位表示有这个中断产生了,上不上报是后续流程的事。


(4)fiq中断由于只能设置一个,一旦产生用户就必定知道是哪个中断源引发的。irq中断有很多个,可以通过读取INTPND寄存器或者INTOFFSET寄存器来确定中断源。


3、中断优先级

S3C2440的中断优先级设置是要遵循固定的模式的,并不像STM32那样可以随意设置。这里的中断优先级设置只是对IRQ中断而言的,FIQ中断不受这个规则限制。


中断优先级通过7个仲裁器完成(ARBITER0到ARBITER6),包括6个一级仲裁器(ARBITER0到ARBITER5)和1个二级仲裁器(ARBITER6),说白了就是二级仲裁器从6个一级仲裁器中挑一个优先级最高的中断报上去给CPU处理,其中REQ0和QEQ5的优先级顺序是固定的,一个最高,一个最低,REQ1到REQ4可以通过配置更改它们的内部优先级顺序,下面贴上一张PRIORITY寄存器的定义表来说明:

通过设置ARB_SELn可以设置REQ1到REQ4的内部顺序,通过设置ARB_MODEn可以设置是否需要自动更改排序,就是处理完该组的一次中断请求后,把对应ARB_SELn的值加1取余4,自动轮换一下优先级顺序。为什么要搞个这个功能呢?我猜应该是解决某些中断长期占用导致另外一些中断难以响应的问题,试想一下如果开始优先级是REQ1到REQ4,用户把这四个中断同等重要来看待,然后REQ1有个中断一直都有请求,REQ2这时也来了个中断请求,但是由于REQ1优先级更高霸占了中断请求的处理资源,然后REQ2就一直得不到响应了。


接下来还是要提一点,REQ0到REQ5的优先级顺序只有4种排列方式,个人感觉还是不太灵活,能像STM32那样自由配置就好了。


 


4、中断处理代码


在进入或者退出中断时需要保存和恢复程序的运行环境。示例代码如下:


(1)IRQ中断,进入和退出的代码如下:


sub     lr, lr, #4        @计算返回地址

stmdb   sp!, {r0-r12,lr}  @保存使用到的寄存器

... ...                   @处理中断

ldmia   sp!, {r0-r12,pc}^ @中断返回

                          @^表示将spsr的值赋给cpsr,恢复之前的工作模式

(2)FIQ中断,进入和退出的代码如下:


sub     lr, lr, #4        @计算返回地址

stmdb   sp!, {r0-r7,lr}   @保存使用到的寄存器

... ...                   @处理快速中断

ldmia   sp!, {r0-r7,pc}^  @中断返回

                          @^表示将spsr的值赋给cpsr,恢复之前的工作模式

 


五、代码编写

接下来是具体的实验代码,分为以下几个文件:


head.S:启动文件。


main.c:初始化及各种C函数。


Makefile:编译代码。


各个文件的具体内容如下:


head.S


@*************************************************************************

@ File:head.S

@*************************************************************************       

.text

.global _start

_start:

@******************************************************************************       

@ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用

@******************************************************************************       

    b   Reset

 

@ 0x04: 未定义指令中止模式的向量地址

HandleUndef:

    b   HandleUndef 

 

@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式

HandleSWI:

    b   HandleSWI

 

@ 0x0c: 指令预取终止导致的异常的向量地址

HandlePrefetchAbort:

    b   HandlePrefetchAbort

 

@ 0x10: 数据访问终止导致的异常的向量地址

HandleDataAbort:

    b   HandleDataAbort

 

@ 0x14: 保留

HandleNotUsed:

    b   HandleNotUsed

 

@ 0x18: 中断模式的向量地址

    b   HandleIRQ

 

@ 0x1c: 快中断模式的向量地址

HandleFIQ:

    b   HandleFIQ

    

Reset: 

ldr     sp, =4096                       @设置堆栈,因为要调用C语言函数 

bl     disable_watch_dog               @关WATCH DOG

 

    msr cpsr_c, #0xd2       @ 进入中断模式

    ldr sp, =3072           @ 设置中断模式栈指针

 

    msr cpsr_c, #0xd3       @ 进入管理模式

    ldr sp, =4096           @ 设置管理模式栈指针,

                            @ 其实复位之后,CPU就处于管理模式,

                            @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略

 

    bl  init_led            @ 初始化LED的GPIO管脚

    bl  init_irq            @ 调用中断初始化函数

    msr cpsr_c, #0x53       @ 设置I-bit=0,开IRQ中断

 

    ldr lr, =halt_loop      @ 设置返回地址

    ldr pc, =main           @ 调用main函数

 

halt_loop:

    b   halt_loop

 

HandleIRQ:

    sub lr, lr, #4                  @ 计算返回地址

    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器

                                    @ 注意,此时的sp是中断模式的sp

                                    @ 初始值是上面设置的3072

    

    ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址  

    ldr pc, =EINT_Handle            @ 调用中断服务函数

 

int_return:

    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr

main.c


 

#define BYTE unsigned char

#define WORD unsigned short

#define DWORD unsigned int 

 

/* WOTCH DOG register */

#define REG_WTCON               (*(volatile unsigned long *)0x53000000)

 

 

/* GPIO Configure*/

#define GPFCON (*(volatile unsigned long *)0x56000050)

#define GPFDAT (*(volatile unsigned long *)0x56000054)

#define GPFUP (*(volatile unsigned long *)0x56000058)

 

#define GPGCON (*(volatile unsigned long *)0x56000060)

#define GPGDAT (*(volatile unsigned long *)0x56000064)

#define GPGUP (*(volatile unsigned long *)0x56000068)

 

#define REG_EXTINT0 (*(volatile unsigned long *)0x56000088)

#define REG_EXTINT1 (*(volatile unsigned long *)0x5600008C)

 

/* interrupt Configure */

 

#define REG_EINTMASK (*(volatile unsigned long *)0x560000A4)

#define REG_INTMSK (*(volatile unsigned long *)0X4A000008)

#define REG_INTOFFSET (*(volatile unsigned long *)0x4A000014)

 

#define REG_EINTPEND (*(volatile unsigned long *)0x560000A8)

#define REG_SRCPND (*(volatile unsigned long *)0X4A000000)

#define REG_INTPND (*(volatile unsigned long *)0X4A000010)

 

// #define REG_INTSUBMSK (*(volatile unsigned long *)0x560000A8)

// #define REG_SUBSRCPND (*(volatile unsigned long *)0X4A000018)

 

void disable_watch_dog();

void init_led();

void init_irq();

void EINT_Handle();

 

/*上电后,WATCH DOG默认是开着的,要把它关掉 */

void disable_watch_dog()

{

REG_WTCON = 0;

}

 

void init_led()

{

GPFCON &= ~((DWORD)(3 << (2 * 4)) | (3 << (2 * 5)) | (3 << (2 * 6)));

GPFCON |= ((DWORD)(1 << (2 * 4)) | (1 << (2 * 5)) | (1 << (2 * 6))); //GPF4、GPF5、GPF6输出模式

 

GPFDAT |= (1 << 4) | (1 << 5) | (1 << 6);   //输出高电平,LED全灭

}

 

void init_irq()

{

//配置为外部中断模式

GPFCON &= ~((DWORD)(3 << (2 * 0)) | (3 << (2 * 2)));

GPFCON |= ((DWORD)(2 << (2 * 0)) | (2 << (2 * 2)));

 

GPGCON &= ~((DWORD)(3 << (2 * 3)));

GPGCON |= ((DWORD)(2 << (2 * 3)));

 

//上拉

GPFUP |=  (1 << 0) | (1 << 2);

GPGUP |= (1 << 3);

 

//设置下降沿触发

REG_EXTINT0 &= ~((DWORD)(7 << 0) | (7 << 8));

REG_EXTINT0 |= ((DWORD)(2 << 0) | (2 << 8));

 

REG_EXTINT1 &= ~((DWORD)(7 << 12));

REG_EXTINT1 |= ((DWORD)(2 << 12));

 

//使能EINT11中断

REG_EINTMASK &= ~((DWORD)1<<11);

 

//使能EINT0、EINT2、EINT11中断

REG_INTMSK &= ~((DWORD)(1 << 0) | (1 << 2) |  (1 << 5));

}

 

void EINT_Handle()

{

BYTE bIntOffset = REG_INTOFFSET;

 

switch(bIntOffset)

{

case 0:

GPFDAT ^= ((DWORD)1 << 4); //电平翻转

推荐阅读

史海拾趣

Alpha-Micro Electronics公司的发展小趣事

Alpha-Micro Electronics是一家提供嵌入式系统解决方案的公司,专注于设计和制造微控制器、传感器和其他嵌入式设备。以下是关于Alpha-Micro Electronics公司发展的五个相关故事:

  1. 公司成立:Alpha-Micro Electronics公司成立于1982年,总部位于德国斯图加特。公司的创始人是一群电子工程师,他们致力于为各种应用领域提供高性能的嵌入式系统解决方案。

  2. 技术创新:在公司创立之初,Alpha-Micro Electronics就开始致力于技术创新。他们不断投资于研发,推出了一系列先进的微控制器和传感器产品,以满足客户对功能强大和性能稳定的需求。

  3. 解决方案定制:Alpha-Micro Electronics公司注重与客户的紧密合作,提供定制化的解决方案。他们的工程团队具有丰富的经验和专业知识,能够根据客户的具体要求设计和制造符合其需求的嵌入式系统。

  4. 国际扩展:随着业务的不断发展,Alpha-Micro Electronics逐渐拓展了国际市场。除了在德国的总部和研发中心外,公司还在全球范围内建立了销售和服务网络,包括在美国、亚洲和欧洲的分支机构和合作伙伴。

  5. 行业合作与创新:Alpha-Micro Electronics公司积极参与行业合作和创新项目,与其他企业、研究机构和学术界合作,共同推动嵌入式技术的发展。通过与行业领先者合作,公司不断探索新的技术和应用领域,为客户提供更具竞争力的解决方案。

这些故事展示了Alpha-Micro Electronics公司在技术创新、解决方案定制、国际扩展和行业合作方面的发展历程,以及其在电子行业中的重要地位和影响力。

H&D Wireless公司的发展小趣事

高创始终坚持研发为核心的发展路线,不断加大对新技术、新产品的投入。近年来,其研发投入占营收比例持续保持在较高水平,有时甚至超过15%。这种高强度的研发投入使得高创能够不断推出具有市场竞争力的新产品,如高性能多轴运动控制器、伺服系统等,满足了市场对于高精度、高速度、高性能运动控制解决方案的需求。

德芯半导体(Doeshare)公司的发展小趣事

德芯半导体在成立之初就明确了其市场定位:以高性价比的特色传感器系列产品为目标市场。公司秉承“以德治企,创造精品”的经营理念,通过深入了解市场需求,不断推出符合市场需求的创新产品。在智能家居、智能制造等领域,德芯半导体的产品凭借其高性能、高品质和低价格,赢得了国内外知名公司的认证和合作。

FUJIKURA公司的发展小趣事

德芯半导体深知人才是企业发展的核心。因此,公司注重人才队伍的建设,吸引了一批具有丰富经验和专业知识的资深工程师和管理人员。这些人才不仅为公司带来了先进的技术和管理经验,还为公司的发展提供了源源不断的动力。公司设立技术委员会,布局先进封装项目,推动公司在高端封装技术上的突破。

Dean Technology公司的发展小趣事

近年来,随着电子行业的快速发展和市场竞争的加剧,Dean Technology公司也面临着前所未有的挑战。为了应对这些挑战,公司积极调整战略方向,加强内部管理和团队建设。同时,他们还密切关注行业趋势和市场需求的变化,及时调整产品结构和市场策略。这些努力使得Dean Technology在应对行业变革和挑战时更加从容和自信。

请注意,以上故事是基于对Dean Technology公司一般情况的了解而编写的,可能不完全符合公司的实际发展历程。如需更详细的信息,请查阅相关资料或联系公司官方渠道。

Globaltech Semiconductor Co Ltd公司的发展小趣事

随着技术的不断成熟,Globaltech开始积极拓展国际市场。公司通过与国内外多家系统厂商建立紧密的合作关系,共同开发新产品,进一步提升了其市场竞争力。特别是在亚洲市场,Globaltech凭借其高性价比的产品和快速的响应能力,迅速占据了大量市场份额。同时,公司还积极参与国际展会,与全球客户面对面交流,进一步提升了品牌知名度和影响力。

问答坊 | AI 解惑

新材料可实现超快光电开关,提高光通信速度

作者:崔晓楠  日期:2006-11-1   来自 今日电子 美国华盛顿州立大学物理系研究人员近日设计出一种新型分子,其对光的传播的控制能力比现有的最好材料提高了近20倍,一旦成功用于超快光电开关,将能大大提高互联网与光通信的速度。 研究 ...…

查看全部问答>

请教高手二极管的性能区别问题

2种二极管除了封装不同外,使用中有什么区别吗…

查看全部问答>

通用串行总线(USB)原理及接口设计 I

用串行总线(USB)原理及接口设计 摘要:以USB1.1为基础讨论了USB的基本原理、工作流程、通信协议和相应的关键技术,并介绍了一种USB接口的10M以太网卡的设计方案。已经发布的USB2.0支持480Mbps的高速数据传输,这将使P ...…

查看全部问答>

基于LPC1114的LED控制系统

思路:   1. 通过上位机软件编辑处理效果数据,并存入SD卡中;   2. 通过SSP0读取SD卡中的数据信息到RAM中;   3. 更加效果数据的结构,进行数据转换,转换成LED灯具可以设别的灰度数据;   4. 通过SSP1发送灰度数据到LE ...…

查看全部问答>

达芬奇DSP算法很慢的问题

问题是这样,我用videnc_copy修改,加入我的放大算法代码,在process里面要用到width height pitch等值, 要传参数,我把这些变量放到process里面声明称局部变量并赋初值,程序就跑的很快(20ms),但是我把这几个变量 复制到process的外面(全局 ...…

查看全部问答>

探讨一下关于symbian智能手机操作系统的技术问题

探讨一下关于symbian智能手机操作系统的技术问题 各位从事symbian 智能手机研发或者从事相关电信领域工作的同弟姐妹们,我从事这方面的工作有相当长 的时间,对这个行业,包括有些技术领域有些困惑,想结识一些有同样背景的朋友们,做一下各方 ...…

查看全部问答>

关于CE5的补丁,做个调查,顺便散分!!!

大家做的产品,无论是量产的还是正在开发中的,都打上了CE5的补丁了吗?这些补丁全部打上之后对系统的性能会有改善或者倒退的影响吗? 我现在正在做的产品没有打任何补丁,但是总感觉电源管理有些问题,有时候睡眠唤醒收不到PBT_TRANSITION以及PBT ...…

查看全部问答>

通过TSP利用3700系列系统开关/万用表和2600系列SourceMeter®

在2005年,随着2600系列第一款一体化源表的引进,吉时利推出了安装有板上测试脚本处理器(Test Script Processors,TSP™)的测试测量仪器。TSP执行基于文本的程序,非常类似于SCPI,具有增强的测试序列/流程控制、决策判断以及利用用户自定义 ...…

查看全部问答>

hercules菜鸟步步新建工程点亮LED(HET0)

一、环境说明:HALCoGen :版本03.05.02CCS        :版本Code Composer Studio Version:5.4.0.00091硬件        :TMS570LS031USB 二、工作目录:     CCS工作 ...…

查看全部问答>

汽车雨刮器的设计 单片机课设

1. 设计要求单臂式雨刮器,步进电机驱动,摆角+70o, 周期范围为0.5S~20S。需根据雨量传感器数据调整摆动周期。需要使用显示器显示当前雨刮器的摆动周期。雨刮器说明:为防损坏雨刮,雨刮器每步摆动角必需小于1o ,请根据这个需要选择步距角合适的步 ...…

查看全部问答>