miniOS_V2.0更新
来源:互联网 发布:软件研发质量管理 编辑:程序博客网 时间:2024/06/01 10:25
写在前面的:
本人所著《深入浅出嵌入式底层软件开发》书中裸机部分实验为一个小型的多任务操作系统,由底层到上层应用程序全部知识面均涉及到,不过,当时由于交稿所催,很草率的实现了其功能,最近一直也在忙于其它不相关事情,所以没有将其完善。这几天,给几个大学里的学生做实训,拿出来了这个做Topic,用了两天时间接合书籍技术讨论群里对该OS代码的建议做了下功能的改善和更新,希望读者们或意图从事ARM相关嵌入式开发的朋友们能继续多多关注与支持,谢谢~
本实验的意义:
学过Linux的朋友们应该都知道Linux的发展史,Linux操作系统是Linus本人借鉴了Andrew S. Tanenbaum所写的MINIX操作系统,然后加入了具有前瞻性的开源思想,借助于网络的兴起而著名的网络操作系统。
其背景如下:
当时欧洲和美国大学计算机课程中,关于操作系统的课程是一门全新的课程,而操作系统在当时数量很少,只有在大学里开源的Unix比较普及,而Unix操作系统是一个大工程学科,不太适合于教学,因此,两会会员Andrew S. Tanenbaum自己写了一个小型的MINIX操作系统,用于其就职的大学里教学,虽然是Andrew 这哥们很出名,但是它也可能是年纪大了,仅将其用于教学中,没有将其商业应用和推广。而这时大学生Linus在看了MINIX全部源码后,感觉很多地方需要进行改进,于是,自己根据MINIX,写下了Linux操作系统,当然后面的事情就不在多说了。
通过上面的背景,我想表达的:
- 欧美等发达国家,技术的研究是跑在企业前面的,而大学和研究机构是最新技术的倡导和规划者,指导着新技术的发展方向。
反观国内,由于本人一边从事IT行业,一边从事教育工作,所以,对国内大学还是比较了解的。Android系统从2008年上市到现在已经过去了快4年了,去年年底即2010年,国内的大学里才开始打算开这门课,申请新专业要一年时间,从新生招生到大四毕业要4年,也就是说,最早一批Android专业学生毕业应该是在2015年,而当前Android应用开发人员,市场上几近饱和(指应用层),国内的教育是走在企业的后面的,这种机构出来的学生,能跟上企业的需要吗???
2. 欧美等发达国家,大学里学习C语言,学习操作系统是用的Linux或Unix。
反观国内,清一色的选用Windows平台,我不是说Windows平台不好,而是整个中华人民共和国被Microsoft养懒了,做软件,拖拖拽拽,不知道数据结构的应用场合?不了解应用程序的工作机制?不知道编译是怎么回事?更不知道文件系统和内核有什么关系?
我们大学里开了很多计算机课程,大学毕业了,第一感觉是,大学里学的东西太多,根本就联系不起来?这其实就是教育方式的问题,我不敢说,我的这种说法是对的,但是,经过实验证明,我的做法让很多朋友更加明确了:计算机系统是什么?大学里的操作系统和大学里的数据结构及计算机组成原理和上层语言有什么关系。
我不敢说这个实验能让多少人会喜欢上计算机、更了解计算机、能成为计算机编程高手和老鸟,但是我可以告诉你一些你看书学不到的东西。
miniOS V2.0
版本改进:
2010.05: 为了将《深入浅出嵌入式底层软件开发》这本书里前面的裸机驱动部分全部串起来,借鉴Linux操作系统和网上前辈们的总结,写了一个小型多任务操作系统,主要有以下功能和特点:
- 支持62个任务同时运行(包含内核任务)
- 开启了MMU内存管理
- SDRAM,LED,KEY,watch dog,时钟,UART串口,中断,定时器等嵌入式入门常用案例
- 将大学里操作系统的理论知识:任务调度,进程管理,进程切换等完全用代码实现
- 将ARM中的所有异常全部进行了处理
- 使用了SWI指令,更深入理解System Call系统调用的实现及与应用程序的意义
- 通过简单的内存管理方式,实现对多任务支持
- 通过自己编写的简单应用程序,让你了解,谁调用了main函数,main函数的参数与main函数的返回处理
- 通过ADS环境进行编译(很多人不太理解为什么使用ADS,我承认ADS编译器太老了,但是,对于入门的朋友来说,调试更便宜,方便)
2012.03.08:
- 支持64个任务同时运行(包含内核任务)
- 优化了内存页表映射关系(相对V1.0)
- 应QQ技术群里读者建议,增加了异常时打印栈信息(stack_dump),内存数据打印(mem_dump)等调试功能
- 增加了蜂鸣器驱动(这个没有什么值得作为更新的)
- 更改了目录结构(重新整理了目录结构,不在是全部放在工程目录里)
- 调度器做了一点简单优化(内核空闲进程不会与用户进程抢占CPU)
- 增加了s3c2440_h.S汇编头文件,减少大量变量的重复定义,使代码更容易阅读
- MMU初始化移到了start.S中,内存页表分前后二次初始化,保证虚拟地址正常映射(后面有详细解释)
- 代码的运行地址从0x33ff0000换到了0x800f0000
miniOS V2.0内存分布图:
看过V1.0代码的朋友会记得:原先的物理内存地址有2M空间是未使用的,在这次的版本上,物理内存空间全部用上了。
Linux内核中内核地址空间分为:
- 直接内存映射区(Direct Memory Region)即:低端物理内存
- 动态内存映射区(VMalloc Region)即:高端物理内存
- 其它映射区
miniOS的0x80000000~0x84000000相当于直接内存映射区,可以通过线性减一个偏移地址来管理物理内存0x30000000~0x34000000
同样外设寄存器空间0x48000000~0x60000000也是直接内存映射区,可以直接来访问。
Norflash的2MB空间被映射到了更高的地址处:0xC0000000~0xC0200000
物理内存空间结构:
由上图可知:V2.0版本,页表放到了内存地址0x30000000处
最开始的1MB物理内存地址0x30000000~0x30100000为0号内核进程地址空间,其实就是OS代码区和内核栈区及页表区。
由于OS代码放到了物理内存0x300F0000处,其被映射到虚拟地址0x800F0000处,所以ADS里设置的OS代码的运行地址为0x800F0000。
重要变化:
原先的mmu.c分成了两个文件:page_table.c和mmu.S
page_talbe.c
#include "s3c2440.h"#include "serial.h"#include"mmu.h"#define MANAGER_AP (0x3<<10)#define USER_AP (0x1<<10)#define MANAGER_DOMAIN(0x1<<5)#define USER_DOMAIN(0x0<<5)#define FAULT_DOMAIN(0x0<<5)#define CB (0x1<<2)#define DESC_FLAG (0x2<<0)#define FAULT_DESC (0x0<<0)#define SEC_DESC_WB(MANAGER_AP | MANAGER_DOMAIN | CB | DESC_FLAG)#define NONE_SEC_DESC (FAULT_DESC)#define TTB_BASE ((unsigned long *)SDRAM_BASE)#define VERTOR_NEW_BASE(SDRAM_BASE)/***************************************************************************** 页表建立函数*____________________________________________________________________________* 段页表项entry:[31:20]段基址,[11:10]为AP(控制访问权限),[8:5]域,* [3:2]=CB(decide cached &buffered),[1:0]=0b10-->页表项为段描述符** 0~32MB:为内核进程空间* 32MB~0x80000000:为用户进程空间* 0x80000000~0x84000000:为物理内存完全映射区* 0x98000000~0xB0000000:为外设寄存器完全映射区间*____________________________________________________________________________****************************************************************************/void __create_page_tables_early() {unsigned long phyaddr, viraddr;/* 建立到Norflash的2MB的地址空间的映射 *//* 0x90000000 映射到0开始的1MB地址空间 */ *( TTB_BASE + (NOR_1M_VM >> 20) ) = 0x0 | SEC_DESC_WB; /* 0x90100000 映射到0x100000~0x1FFFFF的1MB地址空间 */ *( TTB_BASE + (NOR_2M_VM >> 20) ) = MB | SEC_DESC_WB;/* 令0x30000000~0x34000000的64MB虚拟地址等于物理地址空间,方便miniOS内部进程管理 */ for(phyaddr = SDRAM_BASE; phyaddr < SDRAM_BASE + SDRAM_SIZE; phyaddr += MB) {*(TTB_BASE + (phyaddr >> 20) ) = phyaddr | SEC_DESC_WB; }/* 令0x80000000~0x84000000的64MB虚拟地址映射到0x30000000~0x34000000 */for(phyaddr = SDRAM_BASE, viraddr = KERNEL_SPACE_BASE; phyaddr < SDRAM_BASE + SDRAM_SIZE; phyaddr += MB, viraddr+= MB) {*(TTB_BASE + (viraddr >> 20) ) = phyaddr | SEC_DESC_WB; }/* 令0x48000000~0x60000000的虚拟地址等于物理地址空间,方便miniOS内部外设管理 */ for(phyaddr = SFR_PA_BASE; phyaddr < SFR_PA_LIMIT; phyaddr += MB) {*(TTB_BASE + (phyaddr >> 20) ) = phyaddr | SEC_DESC_WB; } /* 令0x98000000~0xB0000000的虚拟地址映射到0x48000000~0x60000000 */ for(phyaddr = SFR_PA_BASE, viraddr = phyaddr + VA_TO_PA_OFT; phyaddr < SFR_PA_LIMIT; phyaddr += MB, viraddr+= MB) {*(TTB_BASE + (viraddr >> 20) ) = phyaddr | SEC_DESC_WB; }/* * 异常向量表 * 0xFFFF0000为高地址异常向量表,可以通常设置CP15,C1寄存器V位,当异常产生时,由硬件自动去0xFFFF0000* 地址处执行异常跳转执行,而不是之前的0地址处异常向量表跳转,我们将该虚拟地址映射到0x33F00000这1MB地址* 空间,同样,将全部miniOS代码拷贝到这1MB地址空间来。*/*(TTB_BASE + (0xffff0000>>20)) = (VERTOR_NEW_BASE | SEC_DESC_WB);}void IDCaches_Restart(void){__asm{movr0, #0/* 使ICaches和DCaches无效 */mcrp15, 0, r0, c7, c7, 0/* 使能写入缓冲器 */mcrp15, 0, r0, c7, c10, 4/* 使指令,数据TLB无效 */mcrp15, 0, r0, c8, c7, 0nopnopnopnop}}void __create_page_tables_post(void) { unsigned long taskId = 1;unsigned long phyaddr;// 切断原先的0x30000000~0x34000000的映射关系,使其为无效描述符 for(phyaddr = SDRAM_BASE; phyaddr < SDRAM_BASE + SDRAM_SIZE; phyaddr += MB) {*(TTB_BASE + (phyaddr >> 20) ) = NONE_SEC_DESC; } // 为每个进程空间进行地址映射,映射关系为 taskID -> taskID*32M~taskID*32M + 0x1ffffffor(taskId = 1; taskId < TASK_SZ; taskId++) {*(TTB_BASE + ((taskId*32*MB) >> 20)) = (SDRAM_BASE + taskId*MB) | SEC_DESC_WB; } IDCaches_Restart();}
mmu.S
INCLUDE s3c2440_h.S;++++++++++++++++++++++++++++++++++++++++++++++++++++FAULT_DESCEQU(0x0<<0)NONE_SEC_DESCEQU(FAULT_DESC);++++++++++++++++++++++++++++++++++++++++++++++++++++VECTOREQU(1<<13); 设置异常向量表位置,0 = 低地址0x0 1 = 高地址0xFFFF0000ICACHEEQU(1<<12); 设置ICACHE,0 = 禁用 1 = 使用R_S_BITEQU(3<<8); 和页表项中描述符一起确定内存访问仅限ENDIANEQU(1<<7); 确定系统使用大,小端字节序,0 = 小端模式 1 = 大端模式DCACHEEQU(1<<2); 设置DCACHE,0 = 禁用 1 = 使用ALIGNEQU(1<<1); 设置地址对齐检查,0 = 禁用 1 = 使用MMU_ONEQU(1<<0); 设置MMU,0 = 禁用 1 = 使用;++++++++++++++++++++++++++++++++++++++++++++++++++++AREA MMU_INIT, CODE, READONLYEXPORT __enable_mmu__enable_mmuldr r0, =TTB_BASEldr r1, =VECTORldr r2, =ICACHEorr r3, r2, r1ldr r1, =DCACHEorr r3, r3, r1ldr r1, =ALIGNorr r3, r3, r1ldr r1, =MMU_ONorr r3, r3, r1ldr r1, =R_S_BITorr r4, r3, r1ldr r1, =ENDIANorr r4, r4, r1movr2, #0; 使ICaches和DCaches无效 mcrp15, 0, r2, c7, c7, 0; 使能写入缓冲器 mcrp15, 0, r2, c7, c10, 4; 使指令,数据TLB无效无效 mcrp15, 0, r2, c8, c7, 0; 页表基址写入C2 mcrp15, 0, r0, c2, c0, 0; 将0x2取反变成0xFFFFFFFD,Domain0 = 0b01为用户模式,其它域为0b11管理模式mvnr2, #0x2; 写入域控制信息 mcrp15, 0, r2, c3, c0, 0; 取出C1寄存器中值给reg0 mrcp15, 0, r2, c1, c0, 0; 先清除不需要的功能,现开启bicr2, r2, r4; 设置相关位并开启MMUorrr2, r2, r3mcrp15, 0, r2, c1, c0, 0nopnopnopnopmov pc, lrEND
start.S文件也发生了改变:
- 引入了s3c2440_h.S头文件
- 异常向量跳转换成了b指令相对跳转
- 看门狗用汇编写的
- 运行地址发生了改变,所以代码拷贝和清空BSS修改了
- 开启MMU之前,运行地址为物理地址空间0x30000000~0x34000000,开启之后是0x80000000~0x84000000的虚拟地址空间,所以mmu初始化函数的返回地址做了修正
- 异常模式的栈发生了改变,全部使用虚拟地址
;; start.S:主要安装异常向量表,初始化必要的硬件,; 将代码从Norflash中拷贝到SDRAM,; 然后跳转到SDRAM中去执行,最后调用main函数,开始miniOS启动第二阶段;INCLUDE s3c2440_h.S; 以下为时钟相关寄存器地址LOCKTIMEEQU 0x4c000000MPLLCON EQU0x4c000004CLKDIVN EQU0x4c000014RUN_BASEEQU 0x33ff0000; OS内存运行地址MEM_REG_BASEEQU 0x48000000MEM_REG_END EQU 0x48000034WTD_TIMER EQU 0x53000000; 其它异常栈指针,miniOS处理了所有的异常IMPORT HandleSWI; 以下引入其它文件中声明函数名IMPORT CopyCode2RamIMPORT xmainIMPORT handle_irqIMPORT undef_excpIMPORT prefetch_abtIMPORT data_abtAREA Start, CODE, READONLYENTRY; 代码段开始 b Reset; 异常向量表,其运行地址为0,pc自动由硬件设置 ; 该地址处指令为一跳转指令,跳往reset异常处理 bHandleUndef ; 未定义异常处理跳转指令,跳往_HandleUndef处 bHandleSWI; 软件中断异常处理跳转指令,跳往_HandleSWI处 b HandlePrefetchAbort; 预取指令中止异常处理跳转指令,跳往_HandlePrefetchAbort处 b HandleDataAbort; 数据中止异常处理跳转指令,跳往_HandleDataAbort处HandleNotUsed bHandleNotUsed; 未使用异常处理跳转指令,没有处理 b HandleIRQ; 一般中断异常处理跳转指令,跳往本源文件中 ;HandleIRQ符号处HandleFIQ bHandleFIQ; 快速中断异常处理跳转指令,没有处理Reset ; Reset异常处理符号 blclock_init; 跳往时钟初始化处理 blmem_init; 跳往内存初始化处理 ldrsp, =SVC_STACK_BASE; 设置管理模式栈指针 bldisable_watchdog; 关闭看门狗; 代码拷贝,将代码拷贝到内存里去运行,如果从Nandflash;启动运行,其RAM steppingstone只有4k,不足已运行全部代;码,如果从Norflash启动,其硬件特性决定其运行速度较慢,;因此,将代码拷贝到内存里去运行copy_code ; 代码拷贝开始符号 mov r0, #0x0; R0中为数据开始地址 (ROM数据保存在0地址开始处) ldrr1, =|Image$$RO$$Base|; R1中存放RO输出域运行地址,; 该值由符号变量Image$$EXEC_RO$$Base取得ldr r2, =|Image$$ZI$$Limit| ; 该值由符号变量Image$$EXEC_ZI$$Limit取得 sub r2, r2, r1; R2 = R2 - R1,得出待拷贝数据长度 ldr r1, =KERNEL_RUN_PA blCopyCode2Ram; 将R0,R1,R2三个参数传递给CopyCode2Ram函数执行拷贝 ldrr0, =|Image$$ZI$$Base| ldr r1, =|Image$$ZI$$Limit| sub r0, r0, #VA_TO_PA_OFT sub r1, r1, #VA_TO_PA_OFTblclear_bss_region; mmuIMPORT __create_page_tables_earlyIMPORT __enable_mmuIMPORT __create_page_tables_postbl delaybl __create_page_tables_earlyldr lr, =RUN_VM; RUN_VM是运行地址的偏移地址(0x80000000+OFT)ldr r0, =__enable_mmu; r0也是运行地址的偏移地址(0x80000000+__enable_mmu)sub r0, r0, #VA_TO_PA_OFT; 因为MMU还没有使能,所以还在物理内存里执行,因此要减VA_TO_PA_OFTmov pc, r0; 一旦开启了MMU,虚拟地址0x80000000和0x30000000都可以使用RUN_VMbl __create_page_tables_post; 这儿已经开启了MMU,要切断原先的0X300000000地址映射,所以保证代码运行在0x800000000中 ; init all mode stack bl stack_init; 跳往栈初始化代码处 msrcpsr_c,#0x5f ; 开启系统中断,进入系统模式 ldrlr, =halt_loop; 设置返回地址 ldrpc, =xmain; 跳往main函数,进入OS启动处理halt_loop ; OS返回地址,其实这儿永远不可能被执行到,因为只要OS工; 作,它就会运行到世界末日 bhalt_loop; 死循环delaymov r0, #0x100000mov r1, #0loopcmp r1, r0sub r0, r0, #1bne loopmov pc, lrHandleIRQ ; 系统中断处理 sublr, lr, #4; 修正返回地址 ldrsp, =(IRQ_STACK_BASE + VA_TO_PA_OFT) ; 设置中断模式下栈指针 stmdb sp!, {r0-r12,lr} ; 保存现场 ldrlr, =int_return; 设置中断处理程序的返回地址 ldrpc, =handle_irq; 跳往中断处理程序int_return ; 返回地址 ldmia sp!, {r0-r12,pc}^; 恢复被中断打断现场clock_init ; 时钟初始化代码,详细见时钟初始化章节 ; Set lock time ldr r0, =LOCKTIME ldr r1, =0x00ffffff str r1, [r0] ; Set clock divison ldr r0, =CLKDIVN mov r1, #0x05 str r1, [r0] ; Set system clock to asynchronous mode mrc p15, 0, r1, c1, c0, 0 orr r1, r1, #0xc0000000 mcr p15, 0, r1, c1, c0, 0 ldr r0, =MPLLCON ldr r1, =0x5c011; MPLL is 400MHz str r1, [r0] mov pc, lrmem_init ; 内存初始化代码,详细见内存初始化章节 ldr r0, =MEM_REG_BASE ldr r1, =MEM_REG_END adr r2, memdatamem_init_loop ldr r3, [r2], #4 str r3, [r0], #4 teq r0, r1 bne mem_init_loop mov pc,lrmemdata DCD 0x22111110;BWSCON DCD0x00000700;BANKCON0 DCD 0x00000700;BANKCON1 DCD 0x00000700;BANKCON2 DCD 0x00000700;BANKCON3 DCD 0x00000700;BANKCON4 DCD0x00000700;BANKCON5 DCD 0x00018005;BANKCON6 DCD 0x00018005;BANKCON7 DCD 0x008e04f4;REFRESH DCD 0x000000b1;BANKSIZE DCD 0x00000030;MRSRB6 DCD 0x00000030;MRSRB7clear_bss_regionmov r2, #0clear_loopcmp r0, r1beq quit_loopstr r2, [r0], #4b clear_loopquit_loopmov pc, lrdisable_watchdogldr r0, =WTD_TIMERmov r1, #0str r1, [r0]mov pc, lrstack_init ; 栈指针初始化 ; undefine_stack; 未定义异常 msr cpsr_c, #0xdb ldrsp, =(UND_STACK_BASE + VA_TO_PA_OFT) ; abort_stack; 未定义异常模式 msr cpsr_c, #0xd7 ldrsp, =(ABT_STACK_BASE + VA_TO_PA_OFT) ; irq_stack; 中断模式 msrcpsr_c,#0xd2 ldrsp, =(IRQ_STACK_BASE + VA_TO_PA_OFT) ; sys_stack; 系统模式 msrcpsr_c,#0xdf ldrsp, =(SYS_STACK_BASE + VA_TO_PA_OFT) ; svr_stack; 切换回管理模式 msrcpsr_c,#0xd3 mov pc, lr HandleUndef ; 未定义异常处理 add lr, lr, #4; 修正返回地址 stmdb sp!, {lr}; pc stmdb sp!, {lr}^; lr stmdb sp!, {sp}^; sp stmdb sp!, {r0-r12}; 保存现场 mrs r0, spsr; 发生异常时,将状态寄存器里的数据保存在栈里 stmdb sp!, {r0} mov r0, sp; 发生异常时,将将栈指针传递给异常处理函数用于打印异常现场信息 ldr pc, =undef_excp bhalt_loopHandlePrefetchAbort ; 未定义异常处理 sub lr, lr, #4; 修正返回地址 stmdb sp!, {lr}; pc stmdb sp!, {lr}^; lr stmdb sp!, {sp}^; sp stmdb sp!, {r0-r12}; 保存现场 mrs r0, spsr; 发生异常时,将状态寄存器里的数据保存在栈里 stmdb sp!, {r0} mov r0, sp; 发生异常时,将将栈指针传递给异常处理函数用于打印异常现场信息 ldr pc, =prefetch_abt bhalt_loopHandleDataAbort ; 未定义异常处理 sub lr, lr, #8; 修正返回地址 stmdb sp!, {lr}; pc stmdb sp!, {lr}^; lr stmdb sp!, {sp}^; sp stmdb sp!, {r0-r12}; 保存现场 mrs r0, spsr; 发生异常时,将状态寄存器里的数据保存在栈里 stmdb sp!, {r0} mov r0, sp; 发生异常时,将将栈指针传递给异常处理函数用于打印异常现场信息 ldr pc, =data_abt bhalt_loop END
excp_handle.c增加了一点功能:
当出现异常状态时,蜂鸣器会长鸣,同时打印出异常发生前所有寄存器的值和Kernel panic,以方便朋友们调试。
#include "serial.h"#include "buzzer.h"#include "libs.h"/* print the kernel panic information */#define kernel_panic() \ do{\printk("\r\nKernel panic!!\r\n");\buzzer_on();\while(1); \ } while(0)void undef_excp(unsigned long * sp){ printk("\r\nUndefine exception\r\n"); stack_dump(sp); kernel_panic(); // this will print the stack content}void prefetch_abt(unsigned long * sp){ printk("\r\nPrefetch abort exception\r\n"); stack_dump(sp); kernel_panic(); // this will print the stack content}void data_abt(unsigned long * sp){ printk("\r\nData abort exception\r\n"); stack_dump(sp); kernel_panic(); // this will print the stack content}
stack_dump实现在libs/libs.c中:
里面主要有:
- 16进制数转换成字符串函数:xtos
- 10进制数转换成字符串函数:dtos
- 内存设置函数:memset
- 内存数据打印函数:mem_dump和栈数据打印函数:stack_dump
- mem_dump可以放到os中任意位置,打印指定地址处的数值。
#include "serial.h"#include "s3c2440.h" void xtos(unsigned long n){unsigned long i;if((i=n/16)!=0)xtos(i);if(n%16 > 9)putc(n%16 - 10 +'A');elseputc(n%16+'0');}void dtos(unsigned long n){unsigned long i;if((i=n/10)!=0)dtos(i);putc(n%10+'0');} void memset(char * dest, long len, int value){if(dest == NULL || len <= 0)return;while(len--)*dest++ = value;return ;} // not safe copy char * memcpy(char * dest, const char * src, long len){char * temp = dest;if(dest == NULL || src == NULL || len <= 0)return NULL;while((dest - temp) != len)*dest++ = *src++;return temp;}void mem_dump(const unsigned long * src, long len){const unsigned long * temp = src;if(src == NULL || len <= 0)return;while(src-temp != len){printk("\r\n 0x");xtos(*src++);if((src-temp)%5 == 0)printk("\r\n");}printk("\r\n");}void stack_dump(unsigned long * sp){printk("\r\n CPSR:0x");xtos(*sp++);printk("\r\n R0:0x");xtos(*sp++);printk("\r\n R1:0x");xtos(*sp++);printk("\r\n R2:0x");xtos(*sp++);printk("\r\n R3:0x");xtos(*sp++);printk("\r\n R4:0x");xtos(*sp++);printk("\r\n R5:0x");xtos(*sp++);printk("\r\n R6:0x");xtos(*sp++);printk("\r\n R7:0x");xtos(*sp++);printk("\r\n R8:0x"); xtos(*sp++);printk("\r\n R9:0x");xtos(*sp++);printk("\r\n R10:0x");xtos(*sp++);printk("\r\n R11:0x");xtos(*sp++);printk("\r\n R12:0x");xtos(*sp++);printk("\r\n SP:0x");xtos(*sp++);printk("\r\n LR:0x");xtos(*sp++);printk("\r\n PC:0x");xtos(*sp++);}
主要就是上述的改变,最新源码,请到我的CSDN空间资源中下载。
2440版本:http://download.csdn.net/detail/mr_raptor/4127806
6410版本:http://download.csdn.net/detail/mr_raptor/4902699
下载的代码只有针对QQ2440的,mini2440的和TQ2440的朋友,自己可以去将Key的驱动改一下即可。
- miniOS_V2.0更新
- Hibernate3.0批量更新
- mongodb2.0 更新内容
- 更新Android5.0
- Unity5.0更新列表
- ESXi5.0更新补丁
- iOS9.0更新内容
- Android5.0更新内容简介
- swift3.0更新
- React 15.5.0更新
- Android Studio3.0更新
- 更新
- 更新!
- 更新
- 更新
- 更新
- 更新..
- 更新
- Linux学习笔记。。。
- struts2常量配置详解
- U-Boot与Linux内核的交互
- 我的山寨生涯(5)
- POJO
- miniOS_V2.0更新
- struts2 properties配置详解
- 数学之美 系列---发表者: 吴军, Google 研究员
- struts2.0 配置文件、常量配置详解
- 《Computer Networks (fifth edition)》第四章学习小结
- struts2注解详解
- 把HTML表单提交的数据转化成XML文件-XML+Ajax教程
- 表达式树 (rec) => new ParadigmSearchListData { Number = rec.NUMBER, Name = rec.RES_NAME };
- spring注解详解(二)