割り込みベクタテーブルをRAMに配置し、RAMからの割り込みサービスの実行をガイドします。

私は最近問題を解決していて、RAM内の割り込みベクタテーブルを見つけるコードを見ました。コードが置かれている記事はここにあります:

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

 

MCUの割り込みベクタテーブルは通常フラッシュのアドレス0x00000000にあることは誰もが知っています。これは、コンパイルしたマップファイルから簡単に確認できます。

たとえば、私のプロジェクトのマップファイルは次のとおりです。

.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__ = .

プロジェクトの開始アドレスは0x00008000に設定されているため、プログラムの開始アドレスにあることがわかります。

いくつかの理由で、RAM内の割り込みベクタテーブルを見つける必要があります

あれは

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

SCB-> 2番目:

/**
  \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 */

特定の方法:

1:配列を定義し(ベクトルテーブルとして使用されます。ベクトルテーブルは実際には各割り込み関数のアドレスです。つまり、uint32_t型の配列を定義します)、配列のサイズを決定します。

ベクトルテーブルのサイズを見つけるには、リファレンスマニュアルでデバイスのIRQ番号を調べ、Cortex-M予約アイテムに16を追加します。たとえば、Giant Geckoには39個のIRQがあるため、ベクトルテーブルは55ワードまたは220バイトです。

私はLeopardGecko EFM32LGMCUを使用しています。これには40個の外部割り込みがあります。

#define EXT_IRQ_COUNT 40 / ** <外部(NVIC)割り込みの数* /

したがって、#define VECTOR_SIZE(16 + EXT_IRQ_COUNT)を定義します

uint32_t vectorTableNew [VECTOR_SIZE]

明らかに、SCB-> VTORがこの配列を指すようにします。ご想像のとおり、プログラムのどのメモリ領域がこの配列ですか?グローバルエリア?グローバルエリアのどのエリアですか?BSS領域、初期化されていないグローバル変数はすべてBSS領域にあり、もちろん初期化されたものはデータ領域にあります

__attribute __((section( ".noinit")、aligned(256)));を追加する必要があります。

uint32_t vectorTableNew [VECTOR_SIZE] __attribute __((section( ".noinit")、aligned(256)));

 

__attribute__これはC言語のキーワードではなく、GCCコンパイラのコンパイル属性またはコンパイルステートメントであることに注意してください。

つまり、ここにフラグを設定して、コードをどう処理するかをコンパイラーに指示します。

__attribute __((section( "seaction name"))

これは、関数または変数を「セクション名」という名前のセクションに配置することを意味します

もちろん、__ attribute__は「セグメント」だけでなく、alignedなどの他の属性も宣言できます。

整列は、整列されるバイト数を示すために使用されます。たとえば、4バイトの整列の場合は__attribute __((aligned(4))、20バイトの整列の場合は__attribute __((aligned(20))、__ attribute __(aligned(256))Do 256 -バイトアラインメント

頻繁に使用されるaligned((packed))はバイトアライメントをキャンセルします

たとえば、よく使用する構造は、バイトアラインメントによってキャンセルする必要があります

 

struct your_struct {

uint32_t a;

char b;

} __ attribute __((packed));

振り返ってみると、この配列の定義は次のとおりです。

uint32_t vectorTableNew [VECTOR_SIZE] __attribute __((section( ".noinit")、aligned(256)));

配列を.noinitセクションに配置し、256バイトに整列します。配列は256バイトに整列されることに注意してください。つまり、配列は、配列内の各要素の256バイトの整列ではなく、256バイトを占有します。

VECTORY_SIZE * 4(割り込み関数のアドレスサイズ)であるため、256である理由

ここに質問がありますが、セクション名を宣言しないことは可能ですか?

答えはイエスです!現在のコードの最終目的のためだけに、セクションの名前を定義する必要はありません。

 

下に進む:

それを忘れて、私はコード全体を投稿してそれを忘れます:

#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去引导

以下は.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

 

おすすめ

転載: blog.csdn.net/yangkunhenry/article/details/105787279