CubieBoard中文论坛

 找回密码
 立即注册
搜索
热搜: unable
查看: 11472|回复: 0

U-boot中使用通用定时器中断

[复制链接]
发表于 2013-5-27 22:27:13 | 显示全部楼层 |阅读模式
本帖最后由 andy 于 2013-5-27 23:16 编辑

继LED之后,学习使用A10中断,目的是学习U-boot中如何使用中断。
原文:http://blog.csdn.net/andy_wsj/article/details/8982158

软件环境: 笔记本一台,安装WindowsXP sp3
                XP下软件:Source Insight 3.5;SecureCRT;VMware Workstation7.0                    
                虚拟机中安装ubuntu10.04
                    
                ubuntu中软件:Vim;编译工具链 arm-none-eabi- 版本4.7.2

硬件环境: cubieboard,淘宝自带的串口线和电源线
                    
                microSD卡一只,读卡器一个

准备工作:U-boot实现SD卡启动,请参考另一篇文章 http://blog.csdn.net/andy_wsj/article/details/8515197
                  
               CB的原理图
                  
               A10用户手册


一、代码分析进入点,厘清脉络
   从上次打开LED控制就发现了,文件status_led.c文件内的函数status_led_tick将会在定时器中断中被
调用,在工程内搜索status_led_tick,获得4个结果,两个在interrupts.c下。进入查看,阅读上下文,可以
看出,这是在定时器中断中调用,实现定时LED闪烁控制。
    查看文件,希望获得中断处理部分代码。在任何平台下,使用中断之前都要初始化,因此在工程搜索这个文件里面
的函数interrupt_init,获得很多结果。调用的地方暂时不理会,只看定义。A10是ARM平台,理所当然的查看ARM架构lib里面的定义:
Interrupts.c (d:\share\u-boot-sunxi-sunxi\arch\arm\lib):int interrupt_init (void)
可以看到,若要使用中断,需要定义预编译宏:
CONFIG_USE_IRQ

在include/configs/sunxi-common.h中定义之,然后编译u-boot,提示两个错误:
CONFIG_STACKSIZE_IRQ 未定义
CONFIG_STACKSIZE_FIQ 未定义
就是中断的栈大小没定义,先在include/configs/sunxi-common.h定义两个,不知到多大合适,暂时定义10K
#define CONFIG_STACKSIZE_IRQ (10 * 1024)
#define CONFIG_STACKSIZE_FIQ (10 * 1024)

再次编译u-boot,再次提示错误,start.S里面的do_irq没定义,打开
\u-boot-sunxi-sunxi\arch\arm\cpu\armv7\start.S,搜索do_irq,可以看到:

#ifdef CONFIG_USE_IRQ

        .align        5
irq:
        get_irq_stack
        irq_save_user_regs
        bl        do_irq
        irq_restore_user_regs

        .align        5
fiq:
        get_fiq_stack
        /* someone ought to write a more effective fiq_save_user_regs */
        irq_save_user_regs
        bl        do_fiq
        irq_restore_user_regs

#else

        .align        5
irq:
        get_bad_stack
        bad_save_user_regs
        bl        do_irq

        .align        5
fiq:
        get_bad_stack
        bad_save_user_regs
        bl        do_fiq

#endif /* CONFIG_USE_IRQ */

很明显,这就是中断处理了。
继续看看标号irq,fiq,在文件start.S开头的地方:
.globl _start
_start: b        reset
        ldr        pc, _undefined_instruction
        ldr        pc, _software_interrupt
        ldr        pc, _prefetch_abort
        ldr        pc, _data_abort
        ldr        pc, _not_used
        ldr        pc, _irq
        ldr        pc, _fiq
#ifdef CONFIG_SPL_BUILD
_undefined_instruction: .word _undefined_instruction
_software_interrupt:        .word _software_interrupt
_prefetch_abort:        .word _prefetch_abort
_data_abort:                .word _data_abort
_not_used:                .word _not_used
_irq:                        .word _irq
_fiq:                        .word _fiq
_pad:                        .word 0x12345678 /* now 16*4=64 */
#else
_undefined_instruction: .word undefined_instruction
_software_interrupt:        .word software_interrupt
_prefetch_abort:        .word prefetch_abort
_data_abort:                .word data_abort
_not_used:                .word not_used
_irq:                        .word irq
_fiq:                        .word fiq
_pad:                        .word 0x12345678 /* now 16*4=64 */
#endif        /* CONFIG_SPL_BUILD */
我们已经来到整个u-boot开始的地方,即上电之后一条被执行的指令就是这里
_start: b        reset

接下来几句就是异常处理
        ldr        pc, _undefined_instruction
        ldr        pc, _software_interrupt
        ldr        pc, _prefetch_abort
        ldr        pc, _data_abort
        ldr        pc, _not_used
        ldr        pc, _irq
        ldr        pc, _fiq        
发生什么类型的异常就是跳到对应的位置进行处理,再看看
_irq:                        .word irq
_fiq:                        .word fiq

由此可见,假设发生一个irq,那么首先执行
        ldr        pc, _irq
_irq就是irq
(_irq==irq)->do_irq,就跳到中断处理函数去了,如何实现do_irq呢?在工程内搜索之。
结果不多,逐个查看,发现就是各种实现方式,对于A10,我觉得自己修改一种方式有利于学习,
最后挑了Interrupts.c (d:\share\u-boot-sunxi-sunxi\arch\arm\cpu\arm720t):void do_irq (struct pt_regs *pt_regs)
我决定把这一个文件修改成能在当前平台下使用,并将这个文件放在\u-boot-sunxi-sunxi\arch\arm\cpu\armv7\sunxi\下,
修改该目录Makefile文件,增加如下几句:
ifdef CONFIG_USE_IRQ
COBJS   += interrupts.o
endif

再增加一个文件Interrupts.h,用来定义A10的中断寄存器地址和中断源,放在\u-boot-sunxi-sunxi\arch\arm\include\asm\arch-sunxi目录下。
目的是同过timer0中断控制LED闪烁,其他中断暂时都不会打开,但是会定义,为进一步学习其他模块做准备。

二、了解中断机制,修改代码
准备写代码了,应该看一看A10用户手册,仔细阅读中断和定时器相关的章节
在“intterrupt controller”这一章,所有中断都在这里描述
在“timer controller”这一章,也描述了与定时器相关的寄存器

1、代码编写和修改。
修改\u-boot-sunxi-sunxi\arch\arm\cpu\armv7\sunxi\timmer.c里面的timmer_init函数,使之符合中断要求。
增加\u-boot-sunxi-sunxi\arch\arm\cpu\armv7\sunxi\interrupts.c,这个是修改过来的。修改Makefile,使之可编译到u-boot。
将status_led_tick增加到timer_isr调用中,若正常,板上的led将闪烁。
增加\u-boot-sunxi-sunxi\arch\arm\include\asm\arch-sunxi\interrupts.h,用来定义A10的中断寄存器地址,中断源和中断数据结构。代码:


2、了解调用关系,使编译之后的代码能正常运行
只要timmer0初始化和中断初始化正常,那么就可以产生timer0中断。
那么timer_init被谁调用了呢?工程中搜索timer_init,结果很多,逐个观察,可以肯定初始化在board.c里面,
A10为ARM,所以一定在ARM平台的代码内:
Board.c (d:\share\u-boot-sunxi-sunxi\arch\arm\lib):        timer_init,                /* initialize timer */
阅读上下文,可以看到,在函数board_init_f内通过一个转移表init_sequence调用了timer_init。

再来看看,中断初始化在哪调用的,搜索interrupt_init,跟timer_init在同一个文件里
Board.c (d:\share\u-boot-sunxi-sunxi\arch\arm\lib):        interrupt_init();
这个interrupt_init就是这篇文章开始收搜索的函数,在文件:Interrupts.c (d:\share\u-boot-sunxi-sunxi\arch\arm\lib):int interrupt_init (void)
它调用了arch_interrupt_init函数,不用看,这就在我们自己写的文件\u-boot-sunxi-sunxi\arch\arm\cpu\armv7\sunxi\interrupts.c里面
可以确定调用完整。

三、写到SD卡内,测试之。
中断周期太大,采用24M的外部时钟,而CONFIG_SYS_HZ的值为1000
即中断1000次为1秒钟,每次中断为1ms
因此timer0的重载值为  24M/1000
修改:
#define TIMER_LOAD_VAL     TICKS_PER_HZ
这个宏之后,可以获得正确的CONFIG_SYS_HZ时间值


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|粤ICP备13051116号|cubie.cc---深刻的嵌入式技术讨论社区

GMT+8, 2024-4-20 06:55 , Processed in 0.022225 second(s), 16 queries .

Powered by Discuz! X3.4

© 2001-2012 Comsenz Inc. | Style by Coxxs

返回顶部