历史上的今天
返回首页

历史上的今天

今天是:2025年10月12日(星期日)

正在发生

2022年10月12日 | arm-linux-gcc裸机程序开发(三)

2022-10-12 来源:csdn

一. 中断问题

       中断对编写程序非常的重要,所以程序对中断处理的好坏将直接影响程序的优劣,对实时性要求较高的系统更是如此。对于ADS2.0,在编写中断处理程序的时候,只需要在程序前面加上"_irq"这个关键字,ADS就会自动为我们保存中断现场,等程序返回的时候自动恢复现场,细节无须我们关心。当然,也可以不加这个关键字,如果这样就得自己保存与恢复中断现场,考虑的问题就多了。arm-linux-gcc开发环境下,目前我还没有发现类似“_irq”这样的关键字可以通知编译器自动处理中断过程。因此,如果使用arm-linux-gcc编译带有中断的程序,就必须自己处理中断现场。除了保护中断现场外,程序还需要做的就是设置正确的中断向量表,使得每个中断发生时都能找到合适的跳转地址。这里我主要参考了2440init.S中的方法。有如下几步:

(1)首先定义宏

.macro Handler Addr

sub     sp, sp, #4      

stmfd   sp!, {r0}    

ldr     r0, =Addr    

ldr     r0, [r0]          

str     r0, [sp,#4]    

ldmfd   sp!, {r0,pc}    

.endm

    这个宏的名称是Handler,作用是取地址Addr处存放的值,赋值给PC。


(2)宏展开

HandlerUndef:

Handler HandleUndef

HandlerSWI:

Handler HandleSWI

HandlerPabort:

Handler HandlePabort

HandlerDabort:

Handler HandleDabort

HandlerIRQ:

Handler HandleIRQ

HandlerFIQ:

Handler HandleFIQ

    这样一来,发生中断后,PC就会跳转到HandleIRQ存放的地址处执行(假设是普通中断)


(3)在HandlerIRQ处有这样的代码

HandleIRQ:

.word IsrIRQ

    HandleIRQ处存放的地址是IsrIRQ,所以发生中断后,PC跳转到IsrIRQ处执行


(4) IsrIRQ 是interrupt.S中的标号

.globl IsrIRQ

IsrIRQ:

        @ Fix the return address

sub lr,lr,#4

        stmfd sp!,{r0-r3,r12,lr}  

#define rINTOFFSET 0x4a000014

LDR     R0, =rINTOFFSET

        LDR     R0, [R0]

       

        LDR     R1, =HandleEINT0

        MOV     LR, PC                          @ Save LR befor jump to the C function we need return back

        LDR     PC, [R1, R0, LSL #2]               

    

ldmfd sp!,{r0-r3,r12,pc}^


这段程序就是中断处理的核心代码:保存中断现场,然后跳转到相应的中断服务程序中,返回后恢复中断现场。这是一种最简单的中断处理方式。程序的开始修正了程序返回地址,我们知道由于流水线的问题,arm体系结构中pc不是指向正在执行的指令,而是指向正在取址的指令,因为中断而保存在lLR寄存器中的就是正在执行的指令的后两个指令,比如正在执行第n条指令,而cpu为我们保存的是第n+2条指令,中断返回后我们要执行第n+1条指令。一个指令四个字节,所以这里LR减4正好是程序应该返回的地址。把这个地址压入栈才是正确的。修正完返回地址,就可以保存寄存器了。这里保存的寄存器有r0-r3,r12,还有返回地址LR。这些寄存器的保存我是参考一些书上介绍的,这样保存事实上也可以正常工作。但是我一直有一个疑问:在arm不同的模式下共用的寄存器不止r0-r3,r0-r7都是各个模式共用的寄存器。那么为什么就保存r0-r3就可以了呢。保存完现场,然后通过rINTOFFSET寄存器找到究竟发生了哪个中断。rINTOFFSET是中断控制器的寄存器,发生中断后,这里记录了具体中断的偏移。所有的中断都是有号码的,我们可以利用这个号码来实现中断服务程序的跳转。这里读出中断号,左移2位也就是乘以4,加上HandleEINT0的值,最后将这个地址处存放的值赋值个pc,从而完成了中断的跳转。


rINTOFFSET 是start.S中的标号,在start.S的最后有这么一段代码:

.equ ISR_BADDR, 0x33ffff00        


HandleReset:

  .word (ISR_BADDR+4*0)

HandleUndef:

.word (ISR_BADDR+4*1)

HandleSWI:

.word (ISR_BADDR+4*2)

HandlePabort:

.word (ISR_BADDR+4*3)

HandleDabort:

.word (ISR_BADDR+4*4)

HandleReserved:

.word (ISR_BADDR+4*5)

HandleIRQ:

.word IsrIRQ

HandleFIQ:

.word (ISR_BADDR+4*7)



.globl HandleEINT0 

.equ HandleEINT0, (ISR_BADDR+4*8)

.equ HandleEINT1, (ISR_BADDR+4*9)

.equ HandleEINT2, (ISR_BADDR+4*10)

.equ HandleEINT3, (ISR_BADDR+4*11)

       ........


HandleEINT0等于ISR_BADDR+4*8,也就是0x33ffff00+4×8,这个地址处就是0号中断(也就是外部中断0)服务程序入口地址。其他中断入口地址都是在此基础上加上相应的偏移值。在应用程序中我们只需要将相应的中断服务程序入口地址保存在这里,发生中断的时候就会实现正确跳转。


在2440addr.h中,定义了一些宏,方便我们赋值。比如

#define _ISR_STARTADDRESS 0x33ffff0



#define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20))

#define pISR_EINT1 (*(unsigned *)(_ISR_STARTADDRESS+0x24))

#define pISR_EINT2 (*(unsigned *)(_ISR_STARTADDRESS+0x28))

#define pISR_EINT3 (*(unsigned *)(_ISR_STARTADDRESS+0x2c))

#define pISR_EINT4_7         (*(unsigned *)(_ISR_STARTADDRESS+0x30))


注意_ISR_STARTADDRESS 与 ISR_BADDR是相同的,这就保证了跳转的一致性。假如我们中断服务程序为irq_int0,在应用程序中我们只需要一条赋值语句就实现了中断程序的装载

pISR_EINT0 = irq_int0


以上的中断处理,在Nandflash启动下是正常工作的,但是在Norflash启动的情况下,用supervivi的D功能下载程序到内存中运行,程序是不能正确响应中断的,在串口终端输出奇怪的信息。原因是当中断发生时,cpu默认去0x00地址处取得中断向量表,而我们的程序的中断向量表在0x30000000处,地址0x00是norfalsh的地址空间,而这里存放的是supervivi的代码,所以发生找不到中断向量的错误。但是在Nandflash启动的情况下,cpu自动将我们代码前4k拷贝到SRAM里,当然也包括中断向量表。在这种情况下,0x00处是SRAM地址空间,正确存放着中断向量。


对于下载到内存中运行这种情况,为了实现正确的中断跳转。解决的办法有两种:第一种是将内存中的中断向量表拷贝到0x00处,但是这种方法是不可行的。原因一:麻烦,我们知道在norflash启动下,0x00是norflash的地址空间,虽然norflash读时序很简单,只需要普通的赋值就可以了。但是写时序很复杂,需要复杂的时序操作。这样一来就需要额外的代码。原因二:有副作用,在norflash里我们存放了supervivi,如果重写norflash就会破坏supervivi,导致下一次norflash不能正确启动。所以这种方法不可取,另一种方法就是通过mmu重映射的方式,将物理地址0x30000000开始处的一小段代码,映射到0x00处,cpu取地址取的是虚拟地址,所以当中断发生的时候,cpu照常到0x00出取中断向量,但是却是取到0x30000000的内容,而这里就是正确的中断向量。我就是用这种方式实现的内存中中断正常跳转的。


验证代码在我的资源里: http://download.csdn.net/detail/yaozhenguo2006/3845811


程序验证的是按键中断,每按下一个按键,串口终端都会提示按下的按键号


二. 完整工程验证

至此arm-linux-gcc编译裸机程序的所有问题都解决了,但是真正能否用于工程还是个未知数。所以得找一个完整的工程验证一下。因为在ADS下我曾经移植过ucosii,如果将ucosii编译通过,并且能够正确运行,那么就表明方法确实可以用。所以我进行了ucosii的移植,有了上面的基础,移植就简单多了,无非就是将arm汇编转换成gnu汇编,c代码基本上不用动,最后编写合适的makefile就可以了。


编译完成的代码在我的资源里: http://download.csdn.net/detail/yaozhenguo2006/3845817


将ucosii_mini2440.bin下载到mini2440中运行,会看到串口终端交替输出hello world 和my friend,同时led灯在闪烁。这说明程序能正确运行,两个任务互不影响的运行。如果要重新编译只需要根据自己系统的情况将链接选项修改一下就可以了。


推荐阅读

史海拾趣

问答坊 | AI 解惑

使用过滤口罩常见问题(1)

问题1:是不是越小的粉尘,越难被口罩过滤呢?   解答:一般而言,我们会以为越小的粉尘越难被过滤,但这是个似是而非的观念,以过滤理论而言,主要有五种过滤粉尘的方法:(1)布郎运动[Brownian Diffusion]-对越小的粒子,效应越强。(2)拦 ...…

查看全部问答>

载噪比(C/N)和调制误码率(MER)对BER 的影响

为了更好地保证数字有线电视的传输质量,需要合理地规划载噪比(C/N)和调制误码率(MER)以确保误码率(BER)指标能够保持在良好的范围内。 BER 被定义为发生误码的比特数与传输的总比特之比。BER 测量结果通常使用工程表示法,并常常显示为一个 ...…

查看全部问答>

Vishay - Power Components for SMPS, Wind Turbines, Electricity Meters

Vishay - Power Components for SMPS, Wind Turbines, Electricity Meters Vishay Intertechnology, Inc. highlighted its latest power capacitors, inductors, and diodes for high-current power converters, wind turbines, electricity meters ...…

查看全部问答>

能够自由站立的创意手电筒

能够自由站立的创意手电筒我们通常能够买到的手电筒只有单一的照明功效,而这款创意多功效手电可以让我们有更多的用处。它的手电筒灯头处有两轴旋转装置,可以随便调整照明方向。同时,它奇特的握柄既可以当做夹子固定手电,还可以张开作为支架。这 ...…

查看全部问答>

请问如何正确运行bluelab下的demo程序?

前几天购买了块CSR的蓝牙开发板. 现在想尝试写个蓝牙键盘玩一下. 可是我打开bluelab的app下的hid-keyboard蓝牙键盘例子程序. 编译并download到开发板上的蓝牙模块中. 运行后. 只看见板上灯在闪. 我插在PC上的蓝牙适配器并没有侦测到这个蓝牙键盘. ...…

查看全部问答>

USB总线过滤驱动中获取设备描述符后哪个标识表示设备是蓝牙?

如题,我要做一个USB总线过滤驱动,插入蓝牙后获取设备描述符,我想知道设备描述符里是否包含蓝牙的设备类型信息? 如果有,哪个是? 如果没有,我如何知道插入的设备就是蓝牙?怎么判断? 在google上没有搜到相关信息,请大家指点…

查看全部问答>

wince的窗口问题

我用MFC智能设备应用程序做出来一个子对话框,上面有按钮等控件,放在一款手机上,界面没有出现任何问题,但把程序放在另一款手机上,画面就乱了,原本居中的按钮位置也变了等等,弄不明白是什么原因,而这两款手机分辨率是一样的。 请问大家wince ...…

查看全部问答>

plc控制步进电机总是响,求教高手!

初学plc,用一个前辈留下的程序控制步进电机,为什么我把输入端I0.1用常闭触点时,电机就可以转,改成常开触点外接用一个开关时,打开开关点击就响不转呢?小弟初涉此道,很多东西不懂,希望高手多多指教,先谢过  …

查看全部问答>

以太网和串口bootloader更新可以用

以太网和串口bootloader更新可以用,但碰到一个棘手的问题,在串口boot_series中,是通过判断pulApp = (unsigned long *)APP_START_ADDRESS;     if((pulApp[0] == 0xffffffff) || ((pulApp[0]& 0xfff00000) != 0x20000000) ||   ...…

查看全部问答>

【登记帖】谁要购买EE_FPGA焊好成品?

为了节省大家的成本,之前推出了散件购买链接(https://bbs.eeworld.com.cn/thread-248286-1-1.html),很多朋友害怕购买散件出现虚焊。考虑到大家的需求,我们打算提供焊接好的成品,但需要提前登记报名,凑够10人一组去焊接。 焊接测试好的EE_FP ...…

查看全部问答>