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
- 然后定义了一些内核寄存器地址。
#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*/
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();
就是将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的值,才不得已采用这种方法。
获得某一异常地址的优先级函数也采用了相似方法。