看完了DJYOS以后想把它移植到自己手中的開(kāi)發(fā)板中,開(kāi)發(fā)板是原子的ALIENTEK,其實(shí)自己也想買(mǎi)一塊論
壇里講到的板子,但是自己手上目前有一塊,就沒(méi)必要浪費(fèi)MONEY了,這塊板子是用的STM32F103RBT6,
FLASH:128K,SRAM:20k.沒(méi)有外部SRAM,所以像GUI可能玩不了,不過(guò)現(xiàn)在也是初期,將系統(tǒng)配置簡(jiǎn)單點(diǎn)應(yīng)
該可以應(yīng)付像點(diǎn)燈的工作,等燈點(diǎn)亮了,再考慮一下串口與PC通信,看看內(nèi)存能不能使用shell,如果能
的話(huà)就可以試一下外圍硬件了,當(dāng)然也可以不使用GUI使用一下LCD?傊窃谶@么小的SRAM中多學(xué)一點(diǎn)
東西,當(dāng)都明白了再考慮下?lián)Q板子,或者自己想辦法外擴(kuò)一個(gè)SRAM和NANDFLASH.
因?yàn)樽约弘m然接觸這方面很久了,但是一直就是課余愛(ài)好還沒(méi)有做多少事之前只用avr做過(guò)一些CAN的實(shí)
驗(yàn)做硬件也花了不少大洋,自學(xué)過(guò)ucos,也在MEGA128上面移植過(guò),但也沒(méi)有深入的研究應(yīng)用程序,最近
發(fā)現(xiàn)了DJYOS,對(duì)它很是感興趣,也看了它配套的資料,熱情很高,所以現(xiàn)在想把熱情轉(zhuǎn)為實(shí)際行動(dòng),實(shí)
際的提高一下自己,現(xiàn)在先自己寫(xiě)下來(lái)將來(lái)在發(fā)出去,呵呵,不要讓別人感覺(jué)太菜了,好了言歸正傳,
我們開(kāi)始實(shí)際行動(dòng)吧
因?yàn)镈JYOS作者已經(jīng)在STM32F103上移植過(guò)了,所以我們可能不需要費(fèi)很大的力氣,只是一些配置不同而
已,所以自己就想邊看作者的的移植例程邊向自己的板子上面移植,那怎么開(kāi)始研究作者的例程呢?本
人想從單片機(jī)上電開(kāi)始依次跟蹤它的動(dòng)作,當(dāng)然這就要需要從上電第一步動(dòng)作開(kāi)始,上電后產(chǎn)生上電復(fù)
位,復(fù)位程序?qū)腇LASH的0x00000000處得到棧頂?shù)刂啡缓髲腇LASH的0x00000004開(kāi)始執(zhí)行代碼(SRAM起
始地址是0x2000 0000 主閃存存儲(chǔ)器地址為0x0800 0000 但啟動(dòng)時(shí)會(huì)映射到0x0000 0000),從
Debug.lds中我們可以發(fā)現(xiàn)定義段時(shí)有一行
KEEP(* (.isr_vector .isr_vector.*))
./src/bsp/arch/core/exceptions.o (.text .text.* .rodata .rodata.*)
這一句是將中斷向量表定義在FLASH起始位置,向量表在exceptions.s中定義,我們可以在exceptions.s
中發(fā)現(xiàn)
.section .isr_vector, "ax", %progbits
.align 3
.global isr_vector
isr_vector:
.word msp_top 聽(tīng)田園說(shuō)定義在.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
就是這個(gè)表定義了當(dāng)發(fā)生復(fù)位或中斷時(shí)程序應(yīng)該跳轉(zhuǎn)到哪里,復(fù)位后CPU從msp_top得到棧頂?shù)刂啡缓髲?/div>
void (*volatile pl_1st)(void) = pre_start;
u32 *src,*des;
u32 i, j;
for(i=0; i
if(src != des) {
}
}
cache_clean_data();
cache_invalid_inst();
pl_1st(); //用指針做遠(yuǎn)程調(diào)用
loader();
int_init();
critical();
loader();
start_sys(); //開(kāi)始啟動(dòng)系統(tǒng)
u32 (*ISR)(ufast_t line);
struct event_ECB *sync_event; //正在等待本中斷發(fā)生的事件
ucpu_t en_counter; //禁止次數(shù)計(jì)數(shù),等于0時(shí)表示允許中斷
ucpu_t int_type; //1=實(shí)時(shí)中斷,0=異步信號(hào)
bool_t enable_nest; //true=本中斷響應(yīng)期間允許嵌套,對(duì)硬件能力有依賴(lài)
//性,也與軟件設(shè)置有關(guān)。例如cortex-m3版本,異步
//信號(hào)被設(shè)置為最低優(yōu)先級(jí),從而所有異步信號(hào)都不
//允許嵌套。
//特別注意,實(shí)時(shí)中斷能夠無(wú)條件嵌套異步信號(hào)。
//中斷響應(yīng)后,由中斷引擎根據(jù)enable_nest的值使能
//或禁止中斷來(lái)控制是否允許嵌套,如果在響應(yīng)中斷
//后,硬件沒(méi)有立即禁止中斷,將有一個(gè)小小的"窗口"
//,在該窗口內(nèi),是允許嵌套的。例如cm3的實(shí)時(shí)中斷
uint16_t my_evtt_id;
u32 prio; //優(yōu)先級(jí),含義由使用者解析
//中斷線(xiàn)屬性位圖,0=異步信號(hào),1=實(shí)時(shí)中斷,數(shù)組的位數(shù)剛好可以容納中斷數(shù)量,與
//中斷線(xiàn)數(shù)據(jù)結(jié)構(gòu)的int_type成員含義相同。
ucpu_t property_bitmap[cn_int_bits_words];
ucpu_t nest_asyn_signal; //中斷嵌套深度,主程序=0,第一次進(jìn)入中斷=1,依次遞加
ucpu_t nest_real; //中斷嵌套深度,主程序=0,第一次進(jìn)入中斷=1,依次遞加
//中斷線(xiàn)使能位圖,1=使能,0=禁止,反映相應(yīng)的中斷線(xiàn)的控制狀態(tài),
//與總開(kāi)關(guān)/異步信號(hào)開(kāi)關(guān)的狀態(tài)無(wú)關(guān).
ucpu_t enable_bitmap[cn_int_bits_words];
ucpu_t en_trunk_counter; //全局中斷禁止計(jì)數(shù),=0表示允許全局中斷
ucpu_t en_asyn_signal_counter; //異步信號(hào)禁止計(jì)數(shù),=0表示允許異步信號(hào)
ufast_t ufl_line;
int_cut_trunk(); //關(guān)總中斷
int_echo_all_line();//此行動(dòng)作for(ufl=0; ufl < cn_int_bits_words; ufl++) //pg_int_reg->clrpend[ufl] = 0xffffffff;
//清除全部中斷線(xiàn)的掛起
pg_scb_reg->CCR |= 1<<bo_scb_ccr_usersetmpend;//USERSETMPEND 如果寫(xiě)成1,那么用戶(hù)代碼可
//以寫(xiě)軟件觸發(fā)中斷寄存器以觸發(fā)(掛起)一個(gè)主異
//常,該異常是和主堆棧指針相聯(lián)系的。
for(ufl_line=0;ufl_line <= cn_int_line_last;ufl_line++)
{
tg_int_lookup_table[ufl_line] = (ufast_t)cn_limit_ufast;
u32g_vect_table[ufl_line] = (u32)__start_asyn_signal; //全部初始化為異步信號(hào) 此數(shù)組為中斷 //向量表,預(yù)加載時(shí)已經(jīng)加載到RAM,現(xiàn)在是將數(shù)組全部初始化為異步信號(hào)處理的入口地址
}
for(ufl_line=0;ufl_line < ufg_int_used_num;ufl_line++)
{
tg_int_lookup_table[tg_int_used[ufl_line]] = ufl_line;
tg_int_table[ufl_line].en_counter = 1; //禁止中斷,計(jì)數(shù)為1
tg_int_table[ufl_line].int_type = cn_asyn_signal; //設(shè)為異步信號(hào)
//所有中斷函數(shù)指針指向空函數(shù)
tg_int_table[ufl_line].ISR = (u32 (*)(ufast_t))NULL_func;
tg_int_table[ufl_line].sync_event = NULL; //同步事件空
tg_int_table[ufl_line].my_evtt_id = cn_invalid_evtt_id; //不彈出事件
pg_int_reg->pri[ufl_line] = 0xff; //異步信號(hào)優(yōu)先級(jí)最低
}
for(ufl_line=0; ufl_line < cn_int_bits_words; ufl_line++)
{
pg_int_reg->clrena[ufl_line]=0xffffffff; //全部禁止
pg_int_reg->clrpend[ufl_line]=0xffffffff; //全部清除掛起狀態(tài)
//屬性位圖清零,全部置為異步信號(hào)方式
tg_int_global.property_bitmap[ufl_line] = 0;
//中斷使能位圖清0,全部處于禁止?fàn)顟B(tài)
tg_int_global.enable_bitmap[ufl_line] = 0;
}
tg_int_global.nest_asyn_signal =0;
tg_int_global.nest_real=0;
tg_int_global.en_asyn_signal_counter = 1; //異步信號(hào)計(jì)數(shù)
int_cut_asyn_signal();
tg_int_global.en_trunk_counter = 0; //總中斷計(jì)數(shù)
int_contact_trunk(); //接通總中斷開(kāi)關(guān)
cm3_cpsie_f(); //接通所有異常開(kāi)關(guān)
cpu_init函數(shù)開(kāi)始執(zhí)行,cpu_init屬于啟動(dòng)函數(shù)負(fù)責(zé)CPU的初始化工作.可以在initcpuc.c中找到,我們
可以看一下,這個(gè)函數(shù)做了什么:
1、設(shè)置了棧頂、
2、關(guān)閉了中斷(PRIMASK:這是個(gè)只有1 個(gè)位的寄存器。當(dāng)它置1 時(shí),就關(guān)掉所有可屏蔽的異常,只剩下NMI和硬fault 可以響應(yīng)。它的缺省值是0,表示沒(méi)有關(guān)中斷。FAULTMASK:這是個(gè)只有1 個(gè)位的寄存器。當(dāng)它置1 時(shí),只有NMI 才能響應(yīng),所有其它的異常,包括中斷和fault,通通閉嘴。它的缺省值也是0,表示沒(méi)有關(guān)異常)、
3、選擇主堆棧指針特權(quán)級(jí)線(xiàn)程模式
(CONTROL[1] 堆棧指針選擇 0=選擇主堆棧指針MSP(復(fù)位后缺省值) 1=選擇進(jìn)程堆棧指針PSP,在線(xiàn)程或基礎(chǔ)級(jí)(沒(méi)有在響應(yīng)異常——譯注),可以使用PSP。 在handler 模式下,只允許使用MSP,所以此時(shí)不得往該位寫(xiě)1。CONTROL[0] 0=特權(quán)級(jí)的線(xiàn)程模式,1=用戶(hù)級(jí)的線(xiàn)程模式,Handler 模式永遠(yuǎn)都是特權(quán)級(jí)的。)、
4、cortex-m3需要將堆棧雙字對(duì)齊、
5、設(shè)置FLASH等待周期、開(kāi)啟預(yù)取、
6設(shè)置時(shí)鐘、
7初始化SRAM,因?yàn)槲业拈_(kāi)發(fā)板沒(méi)有外擴(kuò)ram flash器件所以我的此處不需要
8、load_preload();再下一篇我們?cè)偃タ催@個(gè)函數(shù)做了什么
load_preload();這個(gè)函數(shù),今天我們分析一下它做了哪些工作,這個(gè)函數(shù)作用是預(yù)加載
系統(tǒng),我們可以在load/si/pre_loader.c 中找到。這里我們復(fù)制過(guò)來(lái)便于觀察。
void pre_start(void);
extern struct copy_table preload_copy_table;
//----預(yù)加載程序---------------------------------------------------------------
//功能:加載主加載器、中斷管理模塊,緊急代碼
//參數(shù): 無(wú)。
//返回: 無(wú)。
//----------------------------------------------------------------------------
//備注: 本函數(shù)移植敏感,與開(kāi)發(fā)系統(tǒng)有關(guān),也與目標(biāo)硬件配置有關(guān)
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
}
代碼一開(kāi)始就申明了一個(gè)外部結(jié)構(gòu),這個(gè)結(jié)構(gòu)是定義在lds文件中的,所以要熟悉lds文件,不熟悉的可
以在網(wǎng)上搜一下相關(guān)的文檔學(xué)習(xí)一下,因?yàn)閘ds文件關(guān)系到代碼如何存儲(chǔ)以及在rom還是ram運(yùn)行。
pre_load函數(shù)首先定義了一個(gè)指針pl_1st指向pre_start,然后對(duì)copy_table類(lèi)型的preload_copy_table
結(jié)構(gòu)數(shù)據(jù)進(jìn)行判斷代碼是否需要從rom復(fù)制到ram,從lds文件中對(duì)preload_copy_table分析可知.text代
碼段是定義在rom運(yùn)行的,運(yùn)行地址=加載地址 所以不需要復(fù)制,.data初始化好的數(shù)據(jù)段復(fù)制到內(nèi)
存,.bss未初始化的數(shù)據(jù)段將內(nèi)存相應(yīng)數(shù)據(jù)清零。這樣預(yù)加載程序的使命完成,最終調(diào)用pl_lst()即
pre_start。下一篇看一下pre_start做了什么。
pre_start屬于加載函數(shù),我們可以在loader/si/loader.c中找到它,我們還是把它復(fù)制到這里
void pre_start(void)
{
#ifdef debug
#endif
#ifndef debug
#endif
}
這里有個(gè)宏debug,debug已經(jīng)在eclipse中定義具體是在工程properties > c/c++Build > Settings
>preprocessor中,所以一開(kāi)始就要調(diào)用loader(),loader與pre_loader()功能差不多,這里是把剩余的
需要復(fù)制到ram里的代碼段數(shù)據(jù)段復(fù)制到ram中。然后就是執(zhí)行int_init()函數(shù),這個(gè)函數(shù)是完成中斷的
初始化,我們一會(huì)在看。繼續(xù)就是執(zhí)行critical()鉤子函數(shù),就是在啟動(dòng)系統(tǒng)之前需要做的緊急任務(wù)在
此完成。最后啟動(dòng)系統(tǒng)start_sys().
這里我們?cè)敿?xì)看一下int_init()這個(gè)函數(shù):
關(guān)于中斷djyos定義了兩個(gè)結(jié)構(gòu):
1、中斷線(xiàn)數(shù)據(jù)結(jié)構(gòu),每中斷一個(gè)
//移植敏感
struct int_line
{
};
2.中斷總控?cái)?shù)據(jù)結(jié)構(gòu).
struct int_master_ctrl
{
// bool_t en_trunk; //1=總中斷使能, 0=總中斷禁止
// bool_t en_asyn_signal; //1=異步信號(hào)使能,0=異步信號(hào)禁止
};
熟悉了以上兩個(gè)結(jié)構(gòu)我們看一下中斷初始化函數(shù),過(guò)程已經(jīng)詳細(xì)注釋?zhuān)瘮?shù)主要初始化了向量表,初始化
了中斷線(xiàn)數(shù)據(jù)結(jié)構(gòu),初始化中斷總控?cái)?shù)據(jù)結(jié)構(gòu)
//----初始化中斷---------------------------------------------------------------
//功能:初始化中斷硬件,初始化中斷線(xiàn)數(shù)據(jù)結(jié)構(gòu)
// 2.異步信號(hào)保持禁止,它會(huì)在線(xiàn)程啟動(dòng)引擎中打開(kāi).
// 3.總中斷允許,
// 用戶(hù)初始化過(guò)程應(yīng)該遵守如下規(guī)則:
// 1.系統(tǒng)開(kāi)始時(shí)就已經(jīng)禁止所有異步信號(hào),用戶(hù)初始化時(shí)無(wú)須擔(dān)心異步信號(hào)發(fā)生.
// 2.初始化過(guò)程中如果需要操作總中斷/實(shí)時(shí)中斷/異步信號(hào),應(yīng)該成對(duì)使用.禁止使
// 異步信號(hào)實(shí)際處于允許狀態(tài)(即異步和總中斷開(kāi)關(guān)同時(shí)允許).
// 3.可以操作中斷線(xiàn),比如連接、允許、禁止等,但應(yīng)該成對(duì)使用.
// 4.建議使用save/restore函數(shù)對(duì),不要使用enable/disable函數(shù)對(duì).
//參數(shù):無(wú)
//返回:無(wú)
//-----------------------------------------------------------------------------
void int_init(void)
{
// tg_int_global.en_asyn_signal = false;
// tg_int_global.en_trunk = true;
}
這里介紹一個(gè)eclipse的快捷鍵,Ctrl+h 可以調(diào)出查詢(xún)對(duì)話(huà)框,可以在工作區(qū)查找字符串 函數(shù)等,很方便.
中斷初始化已經(jīng)完成,接下來(lái)就是開(kāi)始啟動(dòng)系統(tǒng)了,下一篇我們?cè)倏?有些底層函數(shù)我們可能到系統(tǒng)啟動(dòng)
完成都沒(méi)有涉及到,是因?yàn)楸仨氂兄袛嗷蛘哒{(diào)用才會(huì)涉及到,以后的時(shí)間我們看一下,現(xiàn)在主要是看一
下系統(tǒng)是怎樣啟動(dòng)的。