打印
[应用相关]

stm32移植日志之三

[复制链接]
2568|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
djyos|  楼主 | 2009-8-14 12:27 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
看本文请参考《都江堰操作系统与嵌入式系统设计》第15章,该书可在www.djyos.com下载。
    做了许多功课以后,终于开始写代码了。
    移植操作系统,首先修改的永远是启动代码,启动代码是为后续程序准备最起码的运行环境的,每个OS都要有自己独特的要求,像IAR、MDK之类的工具自带的启动代码一般是不能用了,你把它翻译到gcc汇编也没用。Gcc自备的启动代码crt0.s呢,一般来说也没有用了,启动代码必须自己写,这点是不能偷懒的。
djyos过去的版本中,总有洋洋数百行的启动代码initcpu.s,令人望而生畏!不过,cm3是单片机级别的cpu,需要用汇编设置的东西比较少,initcpu.s只有了了几行。initcpu.s的内容如下:
    .extern     handler_msp     @ld文件中定义的主栈指针初始化值
    .extern     thread_psp      @ld文件中定义的线程栈指针初始化值
    .extern     cpu_init        @c写的cpu初始化函数
    .text      @代码段开始
    .global _start    @把符号_start输出给ld文件使用,以便在elf文件
           @中把_start标识为程序入口地址
    .code 16     @thumb2汇编
    .syntax unified             @使用cm3的16位和32位统一汇编架构
    .section reset_vect,"x"     @初始化代码段

_start:
    .word handler_msp
    .word reset_start            
    .word rst_fault_handler     @nmi_fault_handler               
    .word rst_fault_handler     @hard_fault_handler               
rst_fault_handler:
    b   .

reset_start:
    CPSID   I               @PRIMASK=1,关中断
    CPSID   F               @FAULTMASK=1,关异常
    ldr     r0,=thread_psp
    msr     psp,r0   @初始化线程栈指针,主栈指针是自动初始化的
   
    mov     r0,#0x20000000 @下面初始化ram中的中断向量表中系统异常部分。
    ldr     r1,=handler_msp
    str      r1,[r0]
    ldr     r1,=reset_start
    str     r1,[r0,#0x044]
    ldr     r1,=rst_fault_handler
    str     r1,[r0,#0x08]
    str     r1,[r0,#0x0c]
    str     r1,[r0,#0x10]
    str     r1,[r0,#0x14]
    str     r1,[r0,#0x18]
    str     r1,[r0,#0x1c]
    str     r1,[r0,#0x20]
    str     r1,[r0,#0x24]
    str     r1,[r0,#0x28]
    str     r1,[r0,#0x2c]
    str     r1,[r0,#0x30]
    str     r1,[r0,#0x34]
    str     r1,[r0,#0x38]
    str     r1,[r0,#0x3c]
   
    ldr     r1,=0xe000ed08             //NVIC.SCB.VTOR地址
    str     r0,[r1]
   
    bl      cpu_init        @ 用C实现的cpu初始化部分
    bl      load_preload   @开始操作系统预加载
    前几句跟gcc汇编的语法相关,注释很详细,这里就不多说明了。
    接下来三行放置了主栈指针地址,复位向量、nmi 和hardfault向量,这也是cortex-m3要求的,为的是能捕获启动时发生的nmi和hard异常。我们看到,nmi上只有一条死循环指令,在系统初始化完成之前,先给它搭一个临时避难所。cm3在复位后,nmi和hard异常是出于允许状态的,其异常向量必须在启动前准备好,否则如果在启动时发生nmi和hard异常的话,cpu将无所适从。至于除这两个异常以外的其他异常,我找了许多资料,都没写复位后的使能状态,依经验,暂且当他们是禁止的,等程序运行起来后再测试其使能状态,若使能,则必须向nmi和hard异常一样,要事先为他们准备好异常向量表。
    复位后,cpu实际上市从reset_start处开始执行指令的,首先关闭了所有中断和硬fault。接下来两句这里初始化了线程栈指针psp,djyos的异常(包括中断)用msp,线程用psp,就是在这里初始化的。接下来初始化了一个异常向量表,把所有系统异常塞进前面说说的临时避难所中。为什么要这样做呢?这个跟djyos的启动过程是有关的,djyos允许用在最紧急的控制系统中,在系统启动前,就加载了中断系统,并且允许用户在OS系统启动过程中用中断执行最紧急的控制。这使得用户代码可以在系统复位后几个微秒内获得关键控制权。这是djyos对用户的特别关怀,任何系统都可能因bug而崩溃,崩溃导致重新启动时,系统会短暂失效,用户肯定希望关键功能失效时间越短越好,djyos的危急代码加载和实时中断机制就是为这个量身定做的。初始化中断系统会修改PRIMASK和FAULTMASK寄存器,系统异常将被使能,如果系统从灾难中恢复,有些系统异常可能出于使能状态,而此时OS还没启动,异常处理的代码也还没有加载,只能先给他们临时安置在紧急避难所中。
    接下来就进入C语言环境继续初始化工作,完成后便开始系统加载了。cpu_init的代码如下:
void cpu_init(void)
{
    switch(tg_scb_reg.CPUID)
    {
        case cn_revision_r0p0:break;    //市场没有版本0的芯片
        case cn_revision_r1p0:
            tg_scb_reg.CCR |= bm_scb_ccr_stkalign;
            break;
        case cn_revision_r1p1:
            tg_scb_reg.CCR |= bm_scb_ccr_stkalign;
            break;
        case cn_revision_r2p0:break;    //好像没什么要做的
    }
    if((tg_rcc_reg.CR & cn_cr_check_mask) == cn_cr_check)   //检查时钟状态
    {
        if((tg_rcc_reg.CFGR & cn_cfgr_check_mask) == cn_cfgr_check)
            return ;    //时钟已经初始化好
    }
    //开始初始化时钟
    //step1:复位时钟控制寄存器
    tg_rcc_reg.CR |= (uint32_t)0x00000001;
    // 复位 SW[1:0], HPRE[3:0], PPRE1[2:0], PPRE2[2:0], ADCPRE[1:0] MCO[2:0] 位
    tg_rcc_reg.CFGR &= (uint32_t)0xF8FF0000;
    // 复位 HSEON, CSSON and PLLON 位
    tg_rcc_reg.CR &= (uint32_t)0xFEF6FFFF;
    // 复位 HSEBYP 位
    tg_rcc_reg.CR &= (uint32_t)0xFFFBFFFF;
    // 复位 PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE 位
    tg_rcc_reg.CFGR &= (uint32_t)0xFF80FFFF;
    // 禁止所有中断
    tg_rcc_reg.CIR = 0x00000000;

    //step2:设置各时钟控制位以及倍频、分频值
    tg_rcc_reg.CFGR = cn_cfgr_set+(111<<24);   // set clock configuration register
    tg_rcc_reg.CR   = cn_cr_set;     // set clock control register

    while(bb_rcc_cr_hserdy ==0);
    while(bb_rcc_cr_pllrdy ==0);
}
    这里首先检查cpu版本,根据cpu版本做了相应的设置,代码很简单。
    接下来是时钟初始化,单片机经常用在实时控制中,这种系统要求快速启动,尤其是系统灾难恢复时,更加要求缩短系统失效时间。有硬件经验的人都知道,晶体起振和PLL稳定是需要比较长的时间的,在灾难恢复中,灾难事件往往没有破坏系统的时钟部分,这种情况就无需重新初始化时钟,以节约时间。所以在代码的开头,我们首先判断时钟状态,也就是判断RCC寄存器组的CR和CFGR两个寄存器中跟主时钟相关的成员的值是否等于我们的设定值。如果正常的话,则直接返回,否则把他们复位后再重新初始化。
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

454

帖子

1

粉丝