改写STM32中断向量表的内容的大神代码

最近又接手另外一个项目,用的是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)

看完这一顿神操作,我脑子是糊的。一时语塞,不知道该说牛逼好,还是傻逼好。

对中断向量的重写,不可谓不高明。但是这层层的宏定义,宏拼接,实在是折磨看代码的人。代码里到处都是这种层层套的宏定义,找个时间我再来分享一篇。

你可能感兴趣的:(代码片段)