ucosii情景完全分析 之 uC-CPU分析

uC-CPU文件夹内容

  • cpu_def.h :主要是机器字长定义、机器大小端定义 、CPU进入临界区方式宏定义
  • cpu.h :主要是一些跟移植相关的数据类型重新定义(typedef),宏定义CM3的内部异常号,然后一些CM3内核寄存器的宏定义(非常主要),最后是一些配置宏定义依赖关系出错处理的预处理。
  • cpu_c.c :主要是位域操作宏定义和函数,然后就是使能、失能特定优先级中断的函数和修改中断优先级的函数。
  • cpu_a.asm:先导出文件后半部分定义的一些汇编函数,再定义一些汇编函数

———————————————————————————————

cpu_def.h分析

由于文件里面注释太多,所以仅仅复制相关代码

/* ------------------- CPU机器字长宏定义------------------ */
#define  CPU_WORD_SIZE_16                          2    /* 16-bit word size = sizeof(CPU_INT16x).                       */
#define  CPU_WORD_SIZE_32                          4    /* 32-bit word size = sizeof(CPU_INT32x).                       */
#define  CPU_WORD_SIZE_64                          8    /* 64-bit word size = sizeof(CPU_INT64x) [see Note #1a].        */

/* ------------------- CPU大小端定义------------------ */
#define  CPU_ENDIAN_TYPE_NONE                      0    /*                                                              */
#define  CPU_ENDIAN_TYPE_BIG                       1    /* Big-   endian word order (CPU words' most  significant ...   */
                                                        /*                           ... octet @ lowest mem addr).      */
#define  CPU_ENDIAN_TYPE_LITTLE                    2    /* Little-endian word order (CPU words' least significant ...   */
                                                        /*                           ... octet @ lowest mem addr).      */
/*CPU定义进入系统临界区的几种方式*/
#define  CPU_CRITICAL_METHOD_NONE                  0   
 /*                                                            */
#define  CPU_CRITICAL_METHOD_INT_DIS_EN            1    
/* DIS/EN       ints.   直接关中断进入临界区,退出临界区开中断*/
/* 不是首先的进入临界区方式,因为不支持中断嵌套,但是在一些编译器或CPU中也是无可奈何的选择*/
#define  CPU_CRITICAL_METHOD_STATUS_STK            2    
/* Push/Pop int status onto stk. 将中断状态保存到堆栈中去,然后关中断进入临界区,在退出临界区的时候从栈中恢复CPU的状态 */
/* 是推荐的进入临界区方式,但是需要编译器支持内嵌汇编,并且需要准确的入栈出栈*/
#define  CPU_CRITICAL_METHOD_STATUS_LOCAL          3    
/* Save/Restore int status to local var. 将中断状态保存到本地变量中去,然后关中断进入临界区,在退出临界区的时候从本地变量中恢复CPU的状态 */
/* 是推荐的进入临界区方式,因为支持多级中断且也不一定需要内嵌汇编*/

所谓进入临界区就是

 OS_CRITICAL_ENTER(); //进入临界区 
/* 临界区代码*/
OS_CRITICAL_EXIT() ; //推出临界区

最终根据宏定义的具体值来决定OS_CRITICAL_ENTER() 与 OS_CRITICAL_EXIT() 具体实现方式,看源码可以知道 ,ucosii是实现的方式三

#define  CPU_CFG_CRITICAL_METHOD        CPU_CRITICAL_METHOD_STATUS_LOCAL

由于需要将中断保存到本地变量中,所以在每个进入临界区代码起始部位都会有

#if OS_CRITICAL_METHOD == 3                               /* Allocate storage for CPU status register  */
    OS_CPU_SR  cpu_sr = 0;
#endif

cpu_sr 就是保存中断的那个本地local变量

OS_CRITICAL_ENTER() 与 OS_CRITICAL_EXIT() 具体实现方式在os_cpu.h中

#if OS_CRITICAL_METHOD == 3
#define  OS_ENTER_CRITICAL()  {cpu_sr = OS_CPU_SR_Save();}
#define  OS_EXIT_CRITICAL()   {OS_CPU_SR_Restore(cpu_sr);}
#endif
OS_CPU_SR_Save  ;CPU进入临界区代码前操作  -->OS_ENTER_CRITICAL()
    MRS     R0, PRIMASK  ;将PRIMASK状态保存存入R0(cpu_sr)
    CPSID   I      ;关中断
    BX      LR     ;返回

OS_CPU_SR_Restore  ;CPU退出临界区代码后操作  -->OS_CRITICAL_EXIT()
    MSR     PRIMASK, R0  ;将R0(cpu_sr)的状态存入PRIMASK
    BX      LR           ;返回

———————————————————————————————


cpu.h分析

  • 文件开始便是CPU移植相关的数据类型定义。这样做的目的主要是方便阅读、加强移植移植性。假设一款8位MCU A中 sizeof(int) = 2,另一款32位MCU B中 sizeof(int) = 4。如果有下列代码

    int num = 10;
    printf(sizeof(num));


两个平台运行结果完全不一样,会导致移植性很差,这就可以用数据类型重新定义来避免这种可移植性问题
typedef int cpu_int_t; //A平台
cpu_int_t num = 10;
printf(sizeof(num));

typedef short int cpu_int_t; //B平台
cpu_int_t num = 10;
printf(sizeof(num));


  • 然后确定了CPU数据宽度、地址宽度、大小端
#define  CPU_CFG_ADDR_SIZE              CPU_WORD_SIZE_32        
/* Defines CPU address word size.           */
#define  CPU_CFG_DATA_SIZE              CPU_WORD_SIZE_32        
/* Defines CPU data    word size.                       */
#define  CPU_CFG_ENDIAN_TYPE            CPU_ENDIAN_TYPE_LITTLE  
/* Defines CPU data    word-memory order.               */

  • 然后是一部分函数声明、宏定义,比较重要的如下
/****都是CM3内核异常标号,也是按地址排列的,是异常向量表一部分,这部分可以看我另一篇博客[ STM32启动文件全解 ]*******/
#define  CPU_INT_STK_PTR                                   0
#define  CPU_INT_RESET                                     1
#define  CPU_INT_NMI                                       2
#define  CPU_INT_HFAULT                                    3
#define  CPU_INT_MEM                                       4
#define  CPU_INT_BUSFAULT                                  5
#define  CPU_INT_USAGEFAULT                                6
#define  CPU_INT_RSVD_07                                   7
#define  CPU_INT_RSVD_08                                   8
#define  CPU_INT_RSVD_09                                   9
#define  CPU_INT_RSVD_10                                  10
#define  CPU_INT_SVCALL                                   11
#define  CPU_INT_DBGMON                                   12
#define  CPU_INT_RSVD_13                                  13
#define  CPU_INT_PENDSV                                   14
#define  CPU_INT_SYSTICK                                  15

STM32启动文件全解


  • 然后定义了一些内核寄存器地址。
#define  CPU_REG_NVIC_NVIC              (*((volatile CPU_INT32U *)(0xE000E004))) /* Int Ctrl'er Type Reg.               */
#define  CPU_REG_NVIC_ST_CTRL           (*((volatile CPU_INT32U *)(0xE000E010))) /* SysTick Ctrl & Status Reg.          */
#define  CPU_REG_NVIC_ST_RELOAD         (*((volatile CPU_INT32U *)(0xE000E014))) /* SysTick Reload      Value Reg.      */
#define  CPU_REG_NVIC_ST_CURRENT        (*((volatile CPU_INT32U *)(0xE000E018))) /* SysTick Current     Value Reg.      */
#define  CPU_REG_NVIC_ST_CAL            (*((volatile CPU_INT32U *)(0xE000E01C))) /* SysTick Calibration Value Reg.      */

这种寄存器访问方式是,先将地址强转为int *指针类型,然后再取内容,通过这种巧妙方式便可以直接对任何内存地址操作了!
这部分寄存器内容跟后来的中断,PendSV ,上下文切换等十分紧密。需要读者好好看看,可以参考我另一篇博客CM3内核寄存器初探


  • 最后是一些配置宏定义依赖关系出错处理的预处理。

#ifndef   CPU_CFG_ADDR_SIZE
#error   "CPU_CFG_ADDR_SIZE              not #define'd in 'cpu.h'              "
#error   "                         [MUST be  CPU_WORD_SIZE_08   8-bitalignment]"
#error   "                         [     ||  CPU_WORD_SIZE_16  16-bitalignment]"
#error   "                         [     ||  CPU_WORD_SIZE_32  32-bitalignment]"

#elif   ((CPU_CFG_ADDR_SIZE != CPU_WORD_SIZE_08) && \
         (CPU_CFG_ADDR_SIZE != CPU_WORD_SIZE_16) && \
         (CPU_CFG_ADDR_SIZE != CPU_WORD_SIZE_32))
#error   "CPU_CFG_ADDR_SIZE        illegally #define'd in 'cpu.h'              "
#error   "                         [MUST be  CPU_WORD_SIZE_08   8-bitalignment]"
#error   "                         [     ||  CPU_WORD_SIZE_16  16-bitalignment]"
#error   "                         [     ||  CPU_WORD_SIZE_32  32-bitalignment]"
#endif

如果没有定义CPU_CFG_ADDR_SIZE ,则在预处理阶段就会报错

#error   "CPU_CFG_DATA_SIZE              not #define'd in 'cpu.h'      "
#error   "                         [MUST be  CPU_WORD_SIZE_08   8-bitalignment]"
#error   "                         [     ||  CPU_WORD_SIZE_16  16-bitalignment]"
#error   "                         [     ||  CPU_WORD_SIZE_32  32-bitalignment]"

———————————————————————————————

cpu_c.c分析


  • 一开始是几个位域宏定义、位域操作函数
    可能很多读者还不了解位域操作,详见我的另一篇博客STM32位域操作
#define  CPU_BIT_BAND_SRAM_REG_LO       0x20000000 /* SRAM位域低地址*/
#define  CPU_BIT_BAND_SRAM_REG_HI       0x200FFFFF /* SRAM位域低地址*/
#define  CPU_BIT_BAND_SRAM_BASE         0x22000000 /* SRAM基地址*/

#define  CPU_BIT_BAND_PERIPH_REG_LO     0x40000000 /* 外设位域低地址*/
#define  CPU_BIT_BAND_PERIPH_REG_HI     0x400FFFFF /* 外设位域高地址*/
#define  CPU_BIT_BAND_PERIPH_BASE       0x42000000 /* 外设位基地址*/

大家看了我上面博客的话,两个位域操作函数就不必多讲了


  • 然后是禁止、解禁某一优先级的函数
void  CPU_IntSrcDis (CPU_INT08U  pos)
{
#if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)
    CPU_SR      cpu_sr;
#endif
    CPU_INT08U  group;
    CPU_INT08U  pos_max;
    CPU_INT08U  nbr;

    switch (pos) {
 /* --------- 几个保留的中断地址,当然不能使能或者失能了INVALID OR RESERVED -------*/
        case CPU_INT_STK_PTR:   
        case CPU_INT_RSVD_07:
        case CPU_INT_RSVD_08:
        case CPU_INT_RSVD_09:
        case CPU_INT_RSVD_10:
        case CPU_INT_RSVD_13:
             break;


      /* -----------------几个CM内部异常不能使能或者失能 SYSTEM EXCEPTIONS ---------------- */
        case CPU_INT_RESET:     /* Reset (see Note #2).*/
        case CPU_INT_NMI:    /* Non-maskable interrupt (see Note #2). */              
        case CPU_INT_HFAULT:   /* Hard fault (see Note #2).  */                          
        case CPU_INT_SVCALL:    /* SVCall (see Note #2).    */                            
        case CPU_INT_DBGMON:     /* Debug monitor (see Note #2). */                        
        case CPU_INT_PENDSV:   /* PendSV (see Note #2).   */                             
             break;

        case CPU_INT_MEM:    /* Memory management.   */
             CPU_CRITICAL_ENTER();    //进入临界区
             CPU_REG_NVIC_SHCSR &= ~CPU_REG_NVIC_SHCSR_MEMFAULTENA;
             CPU_CRITICAL_EXIT();     //退出临界区
             break;

        case CPU_INT_BUSFAULT:    /* Bus fault.     */                                      
             CPU_CRITICAL_ENTER();
             CPU_REG_NVIC_SHCSR &= ~CPU_REG_NVIC_SHCSR_BUSFAULTENA;
             CPU_CRITICAL_EXIT();
             break;

        case CPU_INT_USAGEFAULT:    /* Usage fault.*/                                         
             CPU_CRITICAL_ENTER();
             CPU_REG_NVIC_SHCSR &= ~CPU_REG_NVIC_SHCSR_USGFAULTENA;
             CPU_CRITICAL_EXIT();
             break;

        case CPU_INT_SYSTICK:  /* SysTick.   */                                          
             CPU_CRITICAL_ENTER();
             CPU_REG_NVIC_ST_CTRL &= ~CPU_REG_NVIC_ST_CTRL_ENABLE;
             CPU_CRITICAL_EXIT();
             break;


/* ----------- 除去前16号系统异常外的外部中断EXTERNAL INTERRUPT ---------------- */
        default:
            pos_max = CPU_INT_SRC_POS_MAX;
            if (pos < pos_max) {                 /* See Note #3. */                                      
                 group = (pos - 16) / 32;
                 nbr   = (pos - 16) % 32;

                 CPU_CRITICAL_ENTER();
                 CPU_REG_NVIC_CLREN(group) = DEF_BIT(nbr);
                 CPU_CRITICAL_EXIT();
             }
             break;
    }
}
#define  CPU_REG_NVIC_SHCSR             (*((volatile CPU_INT32U *)(0xE000ED24)))
/* System Handler Ctrl & State Reg.    */
#define  DEF_BIT_16                               0x00010000
#define  CPU_REG_NVIC_SHCSR_MEMFAULTENA         DEF_BIT_16 
/*0000 0000 0000 0001 0000 0000 0000 0000*/

ucosii情景完全分析 之 uC-CPU分析_第1张图片

line32:

CPU_REG_NVIC_SHCSR &= ~CPU_REG_NVIC_SHCSR_MEMFAULTENA;

将CPU_REG_NVIC_SHCSR 寄存器16位清零,就是将存储器异常服务屏蔽,其他操作都类似。


  • 然后是设置、获得某一异常中断入口地址的优先级的函数
void  CPU_IntSrcPrioSet (CPU_INT08U  pos,
                         CPU_INT08U  prio)
{
#if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)
    CPU_SR      cpu_sr;
#endif
    CPU_INT08U  group;
    CPU_INT08U  nbr;
    CPU_INT08U  pos_max;
    CPU_INT32U  prio_32;
    CPU_INT32U  temp;


    prio_32 = CPU_RevBits((CPU_INT08U)prio);
    prio    = (CPU_INT08U)(prio_32 >> (3 * DEF_OCTET_NBR_BITS));

    switch (pos) {
        /* ---------------- INVALID OR RESERVED --------------- */
        case CPU_INT_STK_PTR:    
        case CPU_INT_RSVD_07:
        case CPU_INT_RSVD_08:
        case CPU_INT_RSVD_09:
        case CPU_INT_RSVD_10:
        case CPU_INT_RSVD_13:
             break;


        /* ----------------- SYSTEM EXCEPTIONS ---------------- */
        case CPU_INT_RESET:      /* Reset (see Note #2).       */
        case CPU_INT_NMI:        /* Non-maskable interrupt (see Note #2).  */
        case CPU_INT_HFAULT:     /* Hard fault (see Note #2).           */
             break;

        case CPU_INT_MEM:     /* Memory management.        */
             CPU_CRITICAL_ENTER();
             temp       = CPU_REG_NVIC_SHPRI1;
             temp       &= ~(DEF_OCTET_MASK << (0 * DEF_OCTET_NBR_BITS));
             temp       |=  (prio           << (0 * DEF_OCTET_NBR_BITS));
             CPU_REG_NVIC_SHPRI1  = temp;
             CPU_CRITICAL_EXIT();
             break;

        case CPU_INT_BUSFAULT:  /* Bus fault.              */
             CPU_CRITICAL_ENTER();
             temp        = CPU_REG_NVIC_SHPRI1;
             temp        &= ~(DEF_OCTET_MASK << (1 * DEF_OCTET_NBR_BITS));
             temp        |=  (prio           << (1 * DEF_OCTET_NBR_BITS));
             CPU_REG_NVIC_SHPRI1  = temp;
             CPU_CRITICAL_EXIT();
             break;

        case CPU_INT_USAGEFAULT: /* Usage fault.            */
             CPU_CRITICAL_ENTER();
             temp        = CPU_REG_NVIC_SHPRI1;
             temp        &= ~(DEF_OCTET_MASK << (2 * DEF_OCTET_NBR_BITS));
             temp        |=  (prio           << (2 * DEF_OCTET_NBR_BITS));
             CPU_REG_NVIC_SHPRI1  = temp;
             CPU_CRITICAL_EXIT();
             break;

        case CPU_INT_SVCALL:   /* SVCall.                */
             CPU_CRITICAL_ENTER();
             temp    = CPU_REG_NVIC_SHPRI2;
             temp   &= ~((CPU_INT32U)DEF_OCTET_MASK <<(3 * DEF_OCTET_NBR_BITS));
             temp   |=  (prio            << (3 * DEF_OCTET_NBR_BITS));
             CPU_REG_NVIC_SHPRI2  = temp;
             CPU_CRITICAL_EXIT();
             break;

        case CPU_INT_DBGMON:  /* Debug monitor.          */
             CPU_CRITICAL_ENTER();
             temp         = CPU_REG_NVIC_SHPRI3;
             temp         &= ~(DEF_OCTET_MASK << (0 * DEF_OCTET_NBR_BITS));
             temp         |=  (prio           << (0 * DEF_OCTET_NBR_BITS));
             CPU_REG_NVIC_SHPRI3  = temp;
             CPU_CRITICAL_EXIT();
             break;

        case CPU_INT_PENDSV:   /* PendSV.                 */
             CPU_CRITICAL_ENTER();
             temp          = CPU_REG_NVIC_SHPRI3;
             temp          &= ~(DEF_OCTET_MASK << (2 * DEF_OCTET_NBR_BITS));
             temp          |=  (prio << (2 * DEF_OCTET_NBR_BITS));
             CPU_REG_NVIC_SHPRI3  = temp;
             CPU_CRITICAL_EXIT();
             break;

        case CPU_INT_SYSTICK:   /* SysTick.                 */
             CPU_CRITICAL_ENTER();
             temp          = CPU_REG_NVIC_SHPRI3;
             temp          &= ~((CPU_INT32U)DEF_OCTET_MASK << (3 * DEF_OCTET_NBR_BITS));
             temp          |=  (prio  << (3 * DEF_OCTET_NBR_BITS));
             CPU_REG_NVIC_SHPRI3  = temp;
             CPU_CRITICAL_EXIT();
             break;

        /* ---------------- EXTERNAL INTERRUPT ---------------- */
        default:
            pos_max = CPU_INT_SRC_POS_MAX;
            if (pos < pos_max) {      /* See Note #3.         */
                 group      = (pos - 16) / 4;
                 nbr         = (pos - 16) % 4;

                 CPU_CRITICAL_ENTER();
                 temp        = CPU_REG_NVIC_PRIO(group);
                 temp        &= ~(DEF_OCTET_MASK << (nbr * DEF_OCTET_NBR_BITS));
                 temp         |=  (prio        << (nbr * DEF_OCTET_NBR_BITS));
                 CPU_REG_NVIC_PRIO(group) = temp;
                 CPU_CRITICAL_EXIT();
             }
             break;
    }
}
#define  CPU_REG_NVIC_SHPRI1            (*((volatile CPU_INT32U *)(0xE000ED18))) /* System Handlers  4 to  7 Prio.      */
#define  DEF_OCTET_MASK                                 0xFF
#define  DEF_OCTET_NBR_BITS                                8
/*0000 0000 0000 0000 0000 0000 0000 1000*/

CPU_CRITICAL_ENTER();
             temp      = CPU_REG_NVIC_SHPRI1;
             /*将CPU_REG_NVIC_SHPRI1 值给temp*/
             temp     &= ~(DEF_OCTET_MASK << (0 * DEF_OCTET_NBR_BITS));
             /*然后将temp bit0 - bit7 清零 */
             temp     |=  (prio           << (0 * DEF_OCTET_NBR_BITS));
             /*然后将prio赋值给temp的bit0 - bit7*/
             CPU_REG_NVIC_SHPRI1  = temp;
             /*最后将temp写回 CPU_REG_NVIC_SHPRI1 寄存器中*/
             CPU_CRITICAL_EXIT();

ucosii情景完全分析 之 uC-CPU分析_第2张图片

就是将CPU_REG_NVIC_SHPRI1 值给temp,然后将temp bit0 - bit7 清零 ,然后将prio赋值给temp的bit0 - bit7,最后将temp写回 CPU_REG_NVIC_SHPRI1 寄存器中
这样做的主要原因是Line1 中 CPU_REG_NVIC_SHPRI1 是按 32位寄存器访问的,而存储器异常优先级寄存器只有8位,为了不影响其他24bit的值,才不得已采用这种方法。

获得某一异常地址的优先级函数也采用了相似方法。

你可能感兴趣的:(ucosii情景完全分析)