Coloque la tabla de vectores de interrupciones en la RAM y guíe la ejecución del servicio de interrupciones desde la RAM

Estaba resolviendo un problema recientemente y vi un fragmento de código que ubica la tabla de vectores de interrupción en la RAM. El artículo donde se encuentra el código está aquí:

https://www.silabs.com/community/mcu/32-bit/knowledge-base.entry.html/2017/05/09/emu_e110_-_potential-i2Pn

 

Todo el mundo sabe que la tabla de vectores de interrupción de la MCU suele estar en la dirección 0x00000000 de la memoria flash, lo que se puede ver fácilmente en el archivo de mapa que compiló.

Por ejemplo, el archivo de mapa en mi proyecto:

.text           0x00008000    0x2a580
 *(.vectors)
 .vectors       0x00008000       0xe0 ./CMSIS/EFM32LG/startup_gcc_efm32lg.o
                0x00008000                __Vectors
                0x000080e0                __Vectors_End = .
                0x000000e0                __Vectors_Size = (__Vectors_End - __Vectors)
                0x000080e0                __end__ = .

La dirección de inicio de mi proyecto está configurada en 0x00008000, por lo que puede ver que está en la dirección de inicio de su programa.

Ahora, por algunas razones, necesito ubicar la tabla de vectores de interrupción en la RAM

Es decir

SCB->VTOR 的值由原来的0x00008000,指向RAM中新定位的地址

SCB-> SEGUNDO:

/**
  \brief  Structure type to access the System Control Block (SCB).
 */
typedef struct
{
  __IM  uint32_t CPUID;                  /*!< Offset: 0x000 (R/ )  CPUID Base Register */
  __IOM uint32_t ICSR;                   /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */
  __IOM uint32_t VTOR;                   /*!< Offset: 0x008 (R/W)  Vector Table Offset Register */

método específico:

1: Defina una matriz (utilizada como tabla de vectores, la tabla de vectores es en realidad la dirección de cada función de interrupción, es decir, defina una matriz de tipo uint32_t) y determine el tamaño de la matriz

Para encontrar el tamaño de la tabla de vectores, busque el número de IRQ del dispositivo en el manual de referencia y agregue 16 al elemento reservado Cortex-M. Por ejemplo, Giant Gecko tiene 39 IRQ, por lo que la tabla de vectores tiene 55 palabras o 220 bytes.

Yo uso la MCU Leopard Gecko EFM32LG, que tiene 40 interrupciones externas:

#define EXT_IRQ_COUNT 40 / ** <Número de interrupciones externas (NVIC) * /

Así que defina #define VECTOR_SIZE (16 + EXT_IRQ_COUNT)

uint32_t vectorTableNew [VECTOR_SIZE]

Obviamente queremos que SCB-> VTOR apunte a esta matriz Como puede imaginar, ¿qué área de memoria del programa es esta matriz? Área global? ¿Qué área del área global? Área de BSS, las variables globales no inicializadas están todas en el área de BSS, por supuesto, las inicializadas están en el área de datos.

Esto es lo que necesitamos agregar __attribute__ ((section (".noinit"), alineado (256)));

uint32_t vectorTableNew [VECTOR_SIZE] __attribute__ ((sección (".noinit"), alineado (256)));

 

__attribute__ Tenga en cuenta que no es una palabra clave del lenguaje C, es un atributo de compilación del compilador GCC, o una declaración de compilación,

Es decir, coloco una bandera aquí para decirle al compilador qué hacer con mi código

__attribute __ ((section ("nombre de la sección"))

Significa poner la función o variable en la sección denominada "nombre de sección"

Por supuesto, __attribute__ puede declarar no solo "segmentos", sino también otros atributos, como alineados,

Alineado se utiliza para indicar cuántos bytes están alineados, como __attribute __ ((alineado (4)) para alineación de 4 bytes, __attribute __ ((alineado (20)) para alineación de 20 bytes, __attribute __ (alineado (256)) Do 256 -alineación de bytes

El alineado ((empaquetado)) de uso frecuente cancela la alineación de bytes

Por ejemplo, la estructura que utiliza con frecuencia debe cancelarse mediante la alineación de bytes

 

struct your_struct {

uint32_t a;

char b;

} __ atributo __ ((empaquetado));

Mirando hacia atrás, la definición de esta matriz:

uint32_t vectorTableNew [VECTOR_SIZE] __attribute__ ((sección (".noinit"), alineado (256)));

Coloque la matriz en la sección .noinit y alinéela con 256 bytes. Tenga en cuenta que la matriz está alineada con 256 bytes, lo que significa que la matriz ocupa 256 bytes, en lugar de una alineación de 256 bytes para cada elemento de la matriz.

¿Por qué es 256, porque VECTORY_SIZE * 4 (el tamaño de la dirección de la función de interrupción)

Aquí hay una pregunta. ¿Es posible no declarar el nombre de la sección?

¡La respuesta es sí! No es necesario definir el nombre de la sección, solo para el propósito final del código actual.

 

Continuar hacia abajo:

Olvídalo, publico todo el código y lo olvido:

#include "em_device.h"
#include "em_ramfunc.h"
#include "string.h"
#include "moveIntVectorToRAM.h"


uint32_t vectorTableNew[VECTOR_SIZE] __attribute__ ((section (".noinit"), aligned (256) ));

volatile uint32_t Flash_Address;//volatile告诉编译器不要做指令优化,每次读取Flash_Address的值都是从它所在的内存地址上去取
const uint32_t* ptrFlashVectorTable = VTOR_FLASH_ADDR;

SL_RAMFUNC_DEFINITION_BEGIN//这个宏是告诉我们下面的这个函数是要放在RAM中
static void CheckFlash_IRQHandler(void)
{
  void (*fptr_irqhandler)(void) = NULL;//定义一个函数指针

  Flash_Address = ptrFlashVectorTable[VTABLE_EM2_ISR_OFFSET];     // Do a dummy read of the flash.  It is important to make sure
                                                                  // the compiled version of the code retains this call and is not
                                                                  // optimized out.
  Flash_Address = ptrFlashVectorTable[__get_IPSR()];              // Read the actual address for the active interrupt request.
  fptr_irqhandler = (void(*)(void))Flash_Address;                 // Use the original vectorTable located in flash, with IRQ
                                                                  // offset (IPSR)

  (*fptr_irqhandler)();
}
SL_RAMFUNC_DEFINITION_END
/*
 memcpy这很简单,是把原来在flash(存放代码的flash)上中断向量表copy到RAM中的vectorTableNew地址,注意一下size
 按道理说这样就可以,直接把SCB->VTOR指向vectorTableNew就可以了。
 中间这个vectorTableNew[VTABLE_EM2_ISR_OFFSET] = (uint32_t)CheckFlash_IRQHandler;是把出EM2之后要马上执行的中断处理函数地址
 赋值给新的中断向量表(数组)对应的元素上,清楚这一点,通俗一点,就是把一个地址赋值给一个地址数组的某个元素
 CheckFlash_IRQHandler就是返回的这个地址。
 
 想象一下,当从EM2唤醒的时候,先要出发唤醒源的中断处理函数,在这里就是触发中断向量表的vectorTableNew[VTABLE_EM2_ISR_OFFSET]这个地址,需要这个地址对应的函数去做执行
 然后就调用的RAM中的函数CheckFlash_IRQHandler,然后看CheckFlash_IRQHandler的执行
 1:先把Flash_Address赋值为ptrFlashVectorTable[VTABLE_EM2_ISR_OFFSET]; 处在flash上的对应中断函数的地址,
 2:再把Flash_Address赋值为ptrFlashVectorTable[__get_IPSR()]; 看后面注释硬是获取真正的中断在向量表中的位置,
 那么看下__get_IPSR())
 我们知道IPSR寄存器是这个中断状态寄存器 IPSR(Interrupt Status Register),它包含了正在执行的中断服务的编号
 所以拿到这个中断编号,就可以通过向量表首地址去定位这个中断服务的位置地址
 
 当然我们把这个地址赋值给函数指针fptr_irqhandler,然后去执行这个函数,达到我们最终执行中断服务的目的
 
 整个目的就是当从EM2唤醒的时候从RAM中去引导这个中断服务而不是从默认的Flash去引导
*/
void moveInterruptVectorToRam(void)
{
  // If we know the wake source from EM2, we can limit the size of the RAM vector table to be the size of the maximum vector location index
  memcpy(vectorTableNew, (uint32_t*)VTOR_FLASH_ADDR, sizeof(uint32_t) * (VECTOR_SIZE));  // Copy the flash vector table to RAM
  vectorTableNew[VTABLE_EM2_ISR_OFFSET] = (uint32_t)CheckFlash_IRQHandler;   // The only location in the RAM based vector table required is at the index 
                                                                             // of the IRQ handler that services the IRQ that exits EM2.  If there are more
										// IRQs that can exit EM2 they need to be added as well (they are not shown here).
  SCB->VTOR = (uint32_t)vectorTableNew; 
}
 memcpy这很简单,是把原来在flash(存放代码的flash)上中断向量表copy到RAM中的vectorTableNew地址,注意一下size
 按道理说这样就可以,直接把SCB->VTOR指向vectorTableNew就可以了。
 中间这个vectorTableNew[VTABLE_EM2_ISR_OFFSET] = (uint32_t)CheckFlash_IRQHandler;是把出EM2之后要马上执行的中断处理函数地址
 赋值给新的中断向量表(数组)对应的元素上,清楚这一点,通俗一点,就是把一个地址赋值给一个地址数组的某个元素
 CheckFlash_IRQHandler就是返回的这个地址。
 
 想象一下,当从EM2唤醒的时候,先要出发唤醒源的中断处理函数,在这里就是触发中断向量表的vectorTableNew[VTABLE_EM2_ISR_OFFSET]这个地址,需要这个地址对应的函数去做执行
 然后就调用的RAM中的函数CheckFlash_IRQHandler,然后看CheckFlash_IRQHandler的执行
 1:先把Flash_Address赋值为ptrFlashVectorTable[VTABLE_EM2_ISR_OFFSET]; 处在flash上的对应中断函数的地址,
 2:再把Flash_Address赋值为ptrFlashVectorTable[__get_IPSR()]; 看后面注释硬是获取真正的中断在向量表中的位置,
 那么看下__get_IPSR())
 我们知道IPSR寄存器是这个中断状态寄存器 IPSR(Interrupt Status Register),它包含了正在执行的中断服务的编号
 所以拿到这个中断编号,就可以通过向量表首地址去定位这个中断服务的位置地址
 
 当然我们把这个地址赋值给函数指针fptr_irqhandler,然后去执行这个函数,达到我们最终执行中断服务的目的
 
 整个目的就是当从EM2唤醒的时候从RAM中去引导这个中断服务而不是从默认的Flash去引导

A continuación se muestra el archivo .h

#ifndef _MOVE_INT_VECTOR_TO_RAM_H_
#define _MOVE_INT_VECTOR_TO_RAM_H_

#define VECTOR_SIZE (16 + EXT_IRQ_COUNT)

#define VTOR_FLASH_ADDR (0x8000)

#define VTABLE_EM2_ISR_OFFSET   GPIO_ODD_IRQn + 16 // This should equal the highest IRQ that
                                   // will be used to wake from EM2.  In this
                                   // example, 17 is the GPIO EVEN IRQ.

void moveInterruptVectorToRam(void);

#endif

/* Simplicity Studio, Atollic and vanilla armgcc */
#define SL_RAMFUNC_DECLARATOR          __attribute__ ((section(".ram")))
#define SL_RAMFUNC_DEFINITION_BEGIN    SL_RAMFUNC_DECLARATOR
#define SL_RAMFUNC_DEFINITION_END

 

Supongo que te gusta

Origin blog.csdn.net/yangkunhenry/article/details/105787279
Recomendado
Clasificación