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

djyos与stm32学习心得

作者:佚名   来源:转自李夜雪幽梦空间   点击数:x  更新时间: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  可以调出查询对话框,可以在工作区查找字符串 函数等,很方便.
中断初始化已经完成,接下来就是开始启动系统了,下一篇我们再看.有些底层函数我们可能到系统启动
完成都没有涉及到,是因为必须有中断或者调用才会涉及到,以后的时间我们看一下,现在主要是看一
下系统是怎样启动的。
 
发表评论】【告诉好友】【收藏此文】【关闭窗口

文章评论

188bet uedbet 威廉希尔 明升 bwin 明升88 bodog bwin 明升m88.com 18luck 188bet unibet unibet Ladbrokes Ladbrokes casino m88明升 明升 明升 m88.com 188bet m88 明陞 uedbet赫塔菲官网 365bet官网 m88 help