专注电子技术学习与研究
当前位置:单片机教程网 >> STM32 >> 浏览文章

djyos与stm32学习心得

作者:佚名   来源:转自李夜雪幽梦空间   点击数:  更新时间:2014年08月29日   【字体:

 

 
看完了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)
{
    void (*volatile pl_1st)(void) = pre_start;
 
    u32 *src,*des;
    u32 i, j;
    for(i=0; i
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
    if(src != des) {
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
    cache_clean_data();
    cache_invalid_inst();
#endif
    pl_1st();   //用指针做远程调用
}
 
代码一开始就申明了一个外部结构,这个结构是定义在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
        loader();
#endif
        int_init();
 
        critical();
#ifndef debug
        loader();
#endif
    start_sys();        //开始启动系统
}
 
这里有个宏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
{
    u32 (*ISR)(ufast_t line);
    struct  event_ECB *sync_event;       //正在等待本中断发生的事件
    ucpu_t en_counter;          //禁止次数计数,等于0时表示允许中断
    ucpu_t int_type;            //1=实时中断,0=异步信号
    bool_t enable_nest;         //true=本中断响应期间允许嵌套,对硬件能力有依赖
                                //性,也与软件设置有关。例如cortex-m3版本,异步
                                //信号被设置为最低优先级,从而所有异步信号都不
                                //允许嵌套。
                                //特别注意,实时中断能够无条件嵌套异步信号。
                                //中断响应后,由中断引擎根据enable_nest的值使能
                                //或禁止中断来控制是否允许嵌套,如果在响应中断
                                //后,硬件没有立即禁止中断,将有一个小小的"窗口"
                                //,在该窗口内,是允许嵌套的。例如cm3的实时中断
 
    uint16_t my_evtt_id;
    u32  prio;                  //优先级,含义由使用者解析
};
 
2.中断总控数据结构.
struct int_master_ctrl
{
    //中断线属性位图,0=异步信号,1=实时中断,数组的位数刚好可以容纳中断数量,与
    //中断线数据结构的int_type成员含义相同。
    ucpu_t  property_bitmap[cn_int_bits_words];
    ucpu_t nest_asyn_signal;   //中断嵌套深度,主程序=0,第一次进入中断=1,依次递加
    ucpu_t nest_real;   //中断嵌套深度,主程序=0,第一次进入中断=1,依次递加
    //中断线使能位图,1=使能,0=禁止,反映相应的中断线的控制状态,
    //与总开关/异步信号开关的状态无关.
    ucpu_t  enable_bitmap[cn_int_bits_words];
//    bool_t  en_trunk;           //1=总中断使能,  0=总中断禁止
//    bool_t  en_asyn_signal;         //1=异步信号使能,0=异步信号禁止
    ucpu_t en_trunk_counter;   //全局中断禁止计数,=0表示允许全局中断
    ucpu_t en_asyn_signal_counter; //异步信号禁止计数,=0表示允许异步信号
};
 
熟悉了以上两个结构我们看一下中断初始化函数,过程已经详细注释,函数主要初始化了向量表,初始化
 
了中断线数据结构,初始化中断总控数据结构
 
//----初始化中断---------------------------------------------------------------
//功能:初始化中断硬件,初始化中断线数据结构
//      2.异步信号保持禁止,它会在线程启动引擎中打开.
//      3.总中断允许,
//      用户初始化过程应该遵守如下规则:
//      1.系统开始时就已经禁止所有异步信号,用户初始化时无须担心异步信号发生.
//      2.初始化过程中如果需要操作总中断/实时中断/异步信号,应该成对使用.禁止使
//        异步信号实际处于允许状态(即异步和总中断开关同时允许).
//      3.可以操作中断线,比如连接、允许、禁止等,但应该成对使用.
//      4.建议使用save/restore函数对,不要使用enable/disable函数对.
//参数:无
//返回:无
//-----------------------------------------------------------------------------
void int_init(void)
{
    ufast_t ufl_line;
    int_cut_trunk();    //关总中断                                     
    int_echo_all_line();//此行动作for(ufl=0; ufl < cn_int_bits_words; ufl++)                                               //pg_int_reg->clrpend[ufl] = 0xffffffff;
                        //清除全部中断线的挂起
    pg_scb_reg->CCR |= 1<<bo_scb_ccr_usersetmpend;//USERSETMPEND  如果写成1,那么用户代码可
                                                 //以写软件触发中断寄存器以触发(挂起)一个主异
                                                 //常,该异常是和主堆栈指针相联系的。
    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;  //全部初始化为异步信号 此数组为中断                    //向量表,预加载时已经加载到RAM,现在是将数组全部初始化为异步信号处理的入口地址
    }
    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;                   //禁止中断,计数为1
        tg_int_table[ufl_line].int_type = cn_asyn_signal;        //设为异步信号
                                                                 //所有中断函数指针指向空函数
        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;                        //异步信号优先级最低
    }
    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;    //全部清除挂起状态
        //属性位图清零,全部置为异步信号方式
        tg_int_global.property_bitmap[ufl_line] = 0;
        //中断使能位图清0,全部处于禁止状态
        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 = false;
    tg_int_global.en_asyn_signal_counter = 1;   //异步信号计数
    int_cut_asyn_signal();
//    tg_int_global.en_trunk = true;
    tg_int_global.en_trunk_counter = 0;       //总中断计数
    int_contact_trunk();                    //接通总中断开关
    cm3_cpsie_f();                           //接通所有异常开关
}
 
 
这里介绍一个eclipse的快捷键,Ctrl+h  可以调出查询对话框,可以在工作区查找字符串 函数等,很方便.
中断初始化已经完成,接下来就是开始启动系统了,下一篇我们再看.有些底层函数我们可能到系统启动
完成都没有涉及到,是因为必须有中断或者调用才会涉及到,以后的时间我们看一下,现在主要是看一
下系统是怎样启动的。
 
关闭窗口