看完了DJYOS以后想把它移植到自己手中的开发板中,开发板是原子的ALIENTEK,其实自己也想买一块论
坛里讲到的板子,但是自己手上目前有一块,就没必要浪费MONEY了,这块板子是用的STM32F103RBT6,
FLASH:128K,SRAM:20k.没有外部SRAM,所以像GUI可能玩不了,不过现在也是初期,将系统配置简单点应
该可以应付像点灯的工作,等灯点亮了,再考虑一下串口与PC通信,看看内存能不能使用shell,如果能
的话就可以试一下外围硬件了,当然也可以不使用GUI使用一下LCD。总之是在这么小的SRAM中多学一点
东西,当都明白了再考虑下换板子,或者自己想办法外扩一个SRAM和NANDFLASH.
因为自己虽然接触这方面很久了,但是一直就是课余爱好还没有做多少事之前只用avr做过一些CAN的实
验做硬件也花了不少大洋,自学过ucos,也在MEGA128上面移植过,但也没有深入的研究应用程序,最近
发现了DJYOS,对它很是感兴趣,也看了它配套的资料,热情很高,所以现在想把热情转为实际行动,实
际的提高一下自己,现在先自己写下来将来在发出去,呵呵,不要让别人感觉太菜了,好了言归正传,
我们开始实际行动吧
因为DJYOS作者已经在STM32F103上移植过了,所以我们可能不需要费很大的力气,只是一些配置不同而
已,所以自己就想边看作者的的移植例程边向自己的板子上面移植,那怎么开始研究作者的例程呢?本
人想从单片机上电开始依次跟踪它的动作,当然这就要需要从上电第一步动作开始,上电后产生上电复
位,复位程序将从FLASH的0x00000000处得到栈顶地址然后从FLASH的0x00000004开始执行代码(SRAM起
始地址是0x2000 0000 主闪存存储器地址为0x0800 0000 但启动时会映射到0x0000 0000),从
Debug.lds中我们可以发现定义段时有一行
KEEP(* (.isr_vector .isr_vector.*))
./src/bsp/arch/core/exceptions.o (.text .text.* .rodata .rodata.*)
这一句是将中断向量表定义在FLASH起始位置,向量表在exceptions.s中定义,我们可以在exceptions.s
中发现
.section .isr_vector, "ax", %progbits
.align 3
.global isr_vector
isr_vector:
.word msp_top 听田园说定义在.lds中
.word cpu_init
.word nmi_handler
.word hardfault_handler
.word 0 @ Reserved
.word 0 @ Reserved
.word 0 @ Reserved
.word 0 @ Reserved
.word 0 @ Reserved
.word 0 @ Reserved
.word 0 @ Reserved
.word exp_svc_handler @ SVCall Handler
.word 0 @ Reserved
.word 0 @ Reserved
.word 0 @ PendSV Handler
.word exp_systick_handler @ SysTick Handler
就是这个表定义了当发生复位或中断时程序应该跳转到哪里,复位后CPU从msp_top得到栈顶地址然后从
cpu_init函数开始执行,cpu_init属于启动函数负责CPU的初始化工作.可以在initcpuc.c中找到,我们
可以看一下,这个函数做了什么:
1、设置了栈顶、
2、关闭了中断(PRIMASK:这是个只有1 个位的寄存器。当它置1 时,就关掉所有可屏蔽的异常,只剩下NMI和硬fault 可以响应。它的缺省值是0,表示没有关中断。FAULTMASK:这是个只有1 个位的寄存器。当它置1 时,只有NMI 才能响应,所有其它的异常,包括中断和fault,通通闭嘴。它的缺省值也是0,表示没有关异常)、
3、选择主堆栈指针特权级线程模式
(CONTROL[1] 堆栈指针选择 0=选择主堆栈指针MSP(复位后缺省值) 1=选择进程堆栈指针PSP,在线程或基础级(没有在响应异常——译注),可以使用PSP。 在handler 模式下,只允许使用MSP,所以此时不得往该位写1。CONTROL[0] 0=特权级的线程模式,1=用户级的线程模式,Handler 模式永远都是特权级的。)、
4、cortex-m3需要将堆栈双字对齐、
5、设置FLASH等待周期、开启预取、
6设置时钟、
7初始化SRAM,因为我的开发板没有外扩ram flash器件所以我的此处不需要
8、load_preload();再下一篇我们再去看这个函数做了什么
load_preload();这个函数,今天我们分析一下它做了哪些工作,这个函数作用是预加载
系统,我们可以在load/si/pre_loader.c 中找到。这里我们复制过来便于观察。
void pre_start(void);
extern struct copy_table preload_copy_table;
//----预加载程序---------------------------------------------------------------
//功能:加载主加载器、中断管理模块,紧急代码
//参数: 无。
//返回: 无。
//----------------------------------------------------------------------------
//备注: 本函数移植敏感,与开发系统有关,也与目标硬件配置有关
void load_preload(void)
{
src = (u32*) preload_copy_table.record[i].load_start_address;
des = (u32*) preload_copy_table.record[i].run_start_address;
if(preload_copy_table.record[i].type == 1) { //copy
for(j=0; j
{
*des=*src;
j+=4;
}
} else if(preload_copy_table.record[i].type == 0) { //zero init
for(j=0; j
*des=0;
j+=4;
}
}
#if cfg_cache_used == 1
#endif
}
代码一开始就申明了一个外部结构,这个结构是定义在lds文件中的,所以要熟悉lds文件,不熟悉的可
以在网上搜一下相关的文档学习一下,因为lds文件关系到代码如何存储以及在rom还是ram运行。
pre_load函数首先定义了一个指针pl_1st指向pre_start,然后对copy_table类型的preload_copy_table
结构数据进行判断代码是否需要从rom复制到ram,从lds文件中对preload_copy_table分析可知.text代
码段是定义在rom运行的,运行地址=加载地址 所以不需要复制,.data初始化好的数据段复制到内
存,.bss未初始化的数据段将内存相应数据清零。这样预加载程序的使命完成,最终调用pl_lst()即
pre_start。下一篇看一下pre_start做了什么。
pre_start属于加载函数,我们可以在loader/si/loader.c中找到它,我们还是把它复制到这里
void pre_start(void)
{
#ifdef debug
#endif
#ifndef debug
#endif
}
这里有个宏debug,debug已经在eclipse中定义具体是在工程properties > c/c++Build > Settings
>preprocessor中,所以一开始就要调用loader(),loader与pre_loader()功能差不多,这里是把剩余的
需要复制到ram里的代码段数据段复制到ram中。然后就是执行int_init()函数,这个函数是完成中断的
初始化,我们一会在看。继续就是执行critical()钩子函数,就是在启动系统之前需要做的紧急任务在
此完成。最后启动系统start_sys().
这里我们详细看一下int_init()这个函数:
关于中断djyos定义了两个结构:
1、中断线数据结构,每中断一个
//移植敏感
struct int_line
{
};
2.中断总控数据结构.
struct int_master_ctrl
{
// bool_t en_trunk; //1=总中断使能, 0=总中断禁止
// bool_t en_asyn_signal; //1=异步信号使能,0=异步信号禁止
};
熟悉了以上两个结构我们看一下中断初始化函数,过程已经详细注释,函数主要初始化了向量表,初始化
了中断线数据结构,初始化中断总控数据结构
//----初始化中断---------------------------------------------------------------
//功能:初始化中断硬件,初始化中断线数据结构
// 2.异步信号保持禁止,它会在线程启动引擎中打开.
// 3.总中断允许,
// 用户初始化过程应该遵守如下规则:
// 1.系统开始时就已经禁止所有异步信号,用户初始化时无须担心异步信号发生.
// 2.初始化过程中如果需要操作总中断/实时中断/异步信号,应该成对使用.禁止使
// 异步信号实际处于允许状态(即异步和总中断开关同时允许).
// 3.可以操作中断线,比如连接、允许、禁止等,但应该成对使用.
// 4.建议使用save/restore函数对,不要使用enable/disable函数对.
//参数:无
//返回:无
//-----------------------------------------------------------------------------
void int_init(void)
{
// tg_int_global.en_asyn_signal = false;
// tg_int_global.en_trunk = true;
}
这里介绍一个eclipse的快捷键,Ctrl+h 可以调出查询对话框,可以在工作区查找字符串 函数等,很方便.
中断初始化已经完成,接下来就是开始启动系统了,下一篇我们再看.有些底层函数我们可能到系统启动
完成都没有涉及到,是因为必须有中断或者调用才会涉及到,以后的时间我们看一下,现在主要是看一
下系统是怎样启动的。