最近又接手另外一个项目,用的是STM32。听说这份代码是大神写的。我粗略看了一下,云里雾里,好多好多的神技。有太多的代码写得令我惊怒交加,惊的是居然还能这么写代码,怒的是这写的实在太艰深难懂看得我想骂人。看完实在有太多想说的,今天先放一点出来分享一下,瞻仰一下大神的骚操作。
咱们就从一个中断服务函数开始,顺藤摸瓜来看看代码里怎么安排中断向量的。
ISR(USART2_IRQn)
{
uint8 data;
uint32 _if=USART2->ISR;
你没有看错,这就是中断服务函数的名字。我们来追踪一下这个ISR是个啥。在一个头文件里有如下的定义。可以看到ISR(USART2_IRQn)就等同于void ISR_NAME(USART2_IRQn)(void),ISR_NAME又等同于__ISR_NAME,__ISR_NAME又等同于__ISR_##A。这个就是最后的宏定义了,是个拼接宏。
#ifndef ISR_NAME
#define ISR_NAME(A) __ISR_NAME(A)
#define __ISR_NAME(A) __ISR_##A
#endif
#ifndef ISR_Extern
#define ISR_Extern(A) ISR(A)
#endif
#ifndef ISR_Implement
#define ISR_Implement(A) ISR(A)
#endif
#ifndef ISR_Define
#define ISR_Define(A) ISR(A)
#endif
#ifndef ISR
#define ISR(A) void ISR_NAME(A)(void)
#endif
也就说通过一层层的宏定义。ISR(USART2_IRQn)就等同于void __ISR_USART2_IRQn(void)。经常使用STM32的我们知道,串口的中断函数一般都是这个USART2_IRQHandler,并不是上面那个。那会不会是在中断服务函数里调用了上面那个函数的呢。我在整个项目范围内搜索了USART2_IRQHandler,并没有找到这个函数。
经过在项目文件里的一番查找,我找到了问题的答案。我在一个源文件里找到了这么一个数组的定义。__root的意思是不管这个数组有没有被其他文件用到,强制编译器编译放在编译后的文件中。@“.intvec"是告诉编译器,这个数组编译后放在.intvec段。这个.intvec段就是中断向量表存放的地方,代码这么写,就是说用这个数组覆盖中断向量表。这个数组里又是一层层的宏定义。
__root const intvec_elem __vector_table[] @".intvec"=
{
//======================================================================
//填充CM3系统异常/中断为默认值(放在最前面)
ISR_VAL_2_15_FILL_IN_VECT_TAB(_Deault_Handler),
//======================================================================
//堆栈指针,起始运行地址
[0].ptr = (uint32)__sfe( "CSTACK" ) - 32 ,
[1].handler = __iar_program_start,
//CM3系统异常/中断
ISR_VAL_IN_VECT_TAB(NonMaskableInt_IRQn,_Deault_Handler),
//ISR_VAL_IN_VECT_TAB(HardFault_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(MemoryManagement_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(BusFault_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(UsageFault_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(SVCall_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(DebugMonitor_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(PendSV_IRQn,ISR_NAME(PendSV_IRQn)),
ISR_VAL_IN_VECT_TAB(SysTick_IRQn,ISR_NAME(SysTick_IRQn)),
//==============================================================================
//填充用户中断
#if defined(STM32F10X_LD) //0-42
ISR_VAL_FILL_43_IN_VECT_TAB(_Deault_Handler),
#elif defined(STM32F10X_LD_VL) //0-55
ISR_VAL_FILL_56_IN_VECT_TAB(_Deault_Handler),
#elif defined(STM32F10X_MD) //0-42
ISR_VAL_FILL_43_IN_VECT_TAB(_Deault_Handler),
#elif defined(STM32F10X_MD_VL) //0-55
ISR_VAL_FILL_56_IN_VECT_TAB(_Deault_Handler),
#elif defined(STM32F10X_HD) //0-59
ISR_VAL_FILL_60_IN_VECT_TAB(_Deault_Handler),
#elif defined(STM32F10X_HD_VL) //0-60
ISR_VAL_FILL_61_IN_VECT_TAB(_Deault_Handler),
#elif defined(STM32F10X_XL) //0-59
ISR_VAL_FILL_60_IN_VECT_TAB(_Deault_Handler),
#elif defined(STM32F10X_CL) //0-67
ISR_VAL_FILL_68_IN_VECT_TAB(_Deault_Handler),
#else
#warning "unkown type!"
#endif
//======================================================================
//STM32中断
ISR_IN_VECT_TAB(USART1_IRQn),
ISR_IN_VECT_TAB(USART2_IRQn),
ISR_IN_VECT_TAB(TIM3_IRQn),
// ISR_IN_VECT_TAB(USB_HP_CAN1_TX_IRQn),
// ISR_IN_VECT_TAB(USB_LP_CAN1_RX0_IRQn),
// ISR_IN_VECT_TAB(SDIO_IRQn),
// ISR_IN_VECT_TAB(SD_SDIO_DMA_IRQn),
// ISR_IN_VECT_TAB(USBWakeUp_IRQn),
};
首先来看一下数组的类型。intvec_elem。联合体类型,可以是函数指针,也可以是一个32位的整形数。可以看到函数指针的类型,跟中断服务函数是一样的,无参数无返回。
typedef void ( vector_handler_t )( void );
typedef union { vector_handler_t * handler; uint32 ptr; } intvec_elem;
我们可以看到数组里有这么两句,也符合我们对M3内核的认知。向量表前面四个字节存储的是栈尾地址。__sfe是IAR的汇编语法,意思是去CSTACK段的末地址。向量表接着存储的是复位向量的地址。这里看到代码写着是__iar_program_start这个应该跟IAR有关了,追踪不下去。
//======================================================================
//堆栈指针,起始运行地址
[0].ptr = (uint32)__sfe( "CSTACK" ) - 32 ,
[1].handler = __iar_program_start,
我们接着来看数组里的宏定义。首先是ISR_VAL_2_15_FILL_IN_VECT_TAB
//填充CM3系统异常/中断为默认值(放在最前面)
ISR_VAL_2_15_FILL_IN_VECT_TAB(_Deault_Handler),
宏定义的内容如下
#define ISR_VAL_2_15_FILL_IN_VECT_TAB(A)\
[2].handler = A,\
[3].handler = A,\
[4].handler = A,\
[5].handler = A,\
[6].handler = A,\
[7].handler = A,\
[8].handler = A,\
[9].handler = A,\
[10].handler = A,\
[11].handler = A,\
[12].handler = A,\
[13].handler = A,\
[14].handler = A,\
[15].handler = A
数组还能有这种操作???我也是惊呆了 ,相当于你像下面这样定义数组。骚操作,从未遇到过。
typedef union{
uint32_t reg;
uint8_t buff[4];
}un_test_t;
un_test_t test[] = {
[0].reg = 0,
[1].reg = 1,
};
_Deault_Handler的定义如下,还把调试的时候要看堆栈和寄存器的需求给考虑了。看得出来大神对M3内核处理函数跳转时堆栈的操作是了如指掌,佩服佩服
#if VECT_DEBUG==1
typedef struct
{
uint32 _r0;
uint32 _r1;
uint32 _r2;
uint32 _r3;
uint32 _r12;
uint32 _lr;
uint32 _pc;
uint32 _xpsr;
}__irq_reg_t;
__root volatile __irq_reg_t __irq_reg;
#endif
#define _Deault_Handler ((void (*)(void))&__Deault_Handler)
__task void __Deault_Handler(void)
{
#if VECT_DEBUG==1
uint32 *sp=(uint32 *)__get_MSP();
for(int i=0;i
前面ISR_VAL_2_15_FILL_IN_VECT_TAB这个宏,相当于把中断向量表的第二到第十五都给赋了值,赋的是default_handler。去翻一下startup,看一下默认的中断向量表,可以知道2到15是系统中断向量。
再往下看数据里的宏,又是一顿嵌套宏,看得我好心累。像BusFalut_IRQn,这些是中断向量号,
//CM3系统异常/中断
ISR_VAL_IN_VECT_TAB(NonMaskableInt_IRQn,_Deault_Handler),
//ISR_VAL_IN_VECT_TAB(HardFault_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(MemoryManagement_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(BusFault_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(UsageFault_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(SVCall_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(DebugMonitor_IRQn,_Deault_Handler),
ISR_VAL_IN_VECT_TAB(PendSV_IRQn,ISR_NAME(PendSV_IRQn)),
ISR_VAL_IN_VECT_TAB(SysTick_IRQn,ISR_NAME(SysTick_IRQn)),
#define ISR_IN_VECT_N16_TAB(A) [(A)+16].handler = ISR_NAME(A)
#define ISR_VAL_IN_VECT_N16_TAB(A,B) [(A)+16].handler = B
#define ISR_IN_VECT_TAB(A) ISR_IN_VECT_N16_TAB(A)
#define ISR_VAL_IN_VECT_TAB(A,B) ISR_VAL_IN_VECT_N16_TAB(A,B)
我们知道,M3内核的中断向量号的负的,贴一部出来看,如下。在宏里面因为加了16所以,使得这部分内核级别的中断能够正确的放在中断向量表里。NonMaskableInt_IRQn加上16就等于2了。
typedef enum IRQn
{
/****** Cortex-M3 Processor Exceptions Numbers ***************************************************/
NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */
MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */
BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */
UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */
SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */
DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */
PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */
SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */
/****** STM32 specific Interrupt Numbers *********************************************************/
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */
TAMPER_IRQn = 2, /*!< Tamper Interrupt
等等,刚刚我们在ISR_VAL_2_15_FILL_IN_VECT_TAB宏里,不是对2到15赋了值么,怎么这里还能再对2到15里的内容再赋值,岂不是等同于如下,数组还能有这种操作的么。我实在佩服,从来没有见过如此的数组定义。
typedef union{
uint32_t reg;
uint8_t buff[4];
}un_test_t;
un_test_t test[] = {
[0].reg = 0,
[0].reg = 1,
};
数组接下来的又根据MCU不同的容量做了条件编译,我就看其中一个好了,
ISR_VAL_FILL_43_IN_VECT_TAB(_Deault_Handler),
追踪下去,这一段比较长,我就贴几句出来就好。等我揉揉眼睛,我看得有点眼花了。四十三定义了四十三和四十二,四十二又定义了四十二和四十一。。。。。接龙么。。。。话说,怎么不再像上面那样,是通过数组索引来定义数组的成员,而是直接把定义值写出来。
#define ISR_VAL_FILL_43_IN_VECT_TAB(A) A,ISR_VAL_FILL_42_IN_VECT_TAB(A)
#define ISR_VAL_FILL_42_IN_VECT_TAB(A) A,ISR_VAL_FILL_41_IN_VECT_TAB(A)
#define ISR_VAL_FILL_41_IN_VECT_TAB(A) A,ISR_VAL_FILL_40_IN_VECT_TAB(A)
#define ISR_VAL_FILL_40_IN_VECT_TAB(A) A,ISR_VAL_FILL_39_IN_VECT_TAB(A)
...
#define ISR_VAL_FILL_1_IN_VECT_TAB(A) A
岂不是约等于这么写,我的天,我已经在怀疑我的C语言是不是体育老师教的了,这明明是数组可是我完全没见过这种操作的。
typedef union{
uint32_t reg;
uint8_t buff[4];
}un_test_t;
un_test_t test[] = {
[0].reg = 0,
[0].reg = 1,
52,
};
数组的最后
//======================================================================
//STM32中断
ISR_IN_VECT_TAB(USART1_IRQn),
ISR_IN_VECT_TAB(USART2_IRQn),
ISR_IN_VECT_TAB(TIM3_IRQn),
追踪宏的定义,看到熟悉的ISR_NAME了,这不就是我们一开始看到的那个宏么。原来那个串口的中断服务函数被写到中断向量表里了。
#define ISR_IN_VECT_N16_TAB(A) [(A)+16].handler = ISR_NAME(A)
#define ISR_VAL_IN_VECT_N16_TAB(A,B) [(A)+16].handler = B
#define ISR_IN_VECT_TAB(A) ISR_IN_VECT_N16_TAB(A)
#define ISR_VAL_IN_VECT_TAB(A,B) ISR_VAL_IN_VECT_N16_TAB(A,B)
看完这一顿神操作,我脑子是糊的。一时语塞,不知道该说牛逼好,还是傻逼好。
对中断向量的重写,不可谓不高明。但是这层层的宏定义,宏拼接,实在是折磨看代码的人。代码里到处都是这种层层套的宏定义,找个时间我再来分享一篇。