You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32中能否为内部RAM特定地址设置读取触发中断?

在STM32中实现读取特定RAM地址触发中断的方案

STM32原生硬件没有直接提供“读取特定RAM地址即触发中断”的功能,但可以通过以下几种间接方式实现类似需求:

1. 利用MPU(内存保护单元)故障中断

MPU原本用于非法内存访问的保护,但可以通过配置实现“读取指定地址就触发中断”的效果:

  • 先将目标RAM地址区域配置为禁止读访问,当代码尝试读取该地址时,会触发MPU故障中断;
  • 在中断服务函数中,可临时放开该地址的访问权限、执行自定义逻辑,之后恢复MPU配置。

示例代码片段:

#include "stm32f4xx_hal.h"

#define TARGET_ADDR 0x20000000  // 目标RAM地址

void MPU_Config(void) {
  MPU_Region_InitTypeDef MPU_InitStruct = {0};
  
  HAL_MPU_Disable();
  
  // 配置目标地址区域为禁止任何访问
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = TARGET_ADDR;
  MPU_InitStruct.Size = MPU_REGION_SIZE_32B;
  MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.SubRegionDisable = 0x00;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
  
  HAL_MPU_ConfigRegion(&MPU_InitStruct);
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

void MPU_IRQHandler(void) {
  // 校验是否为目标地址的读访问故障
  if((SCB->CFSR & SCB_CFSR_MMARVALID_Msk) && (SCB->MMFAR == TARGET_ADDR)) {
    // 执行自定义中断逻辑
    
    // 临时放开目标地址的访问权限,确保读取操作完成
    MPU_Region_InitTypeDef MPU_InitStruct = {0};
    HAL_MPU_Disable();
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress = TARGET_ADDR;
    MPU_InitStruct.Size = MPU_REGION_SIZE_32B;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    
    // 清除故障标志
    SCB->CFSR |= SCB_CFSR_MMARVALID_Msk;
  }
}

注意:此方法依赖非法访问触发故障,需在中断中处理权限恢复,避免进入硬fault。

2. 使用DWT(数据监视与跟踪单元)的读监视功能

DWT的数据监视点可以监听特定地址的读/写操作,触发时可产生调试中断(无需调试器连接,部分型号支持独立运行):

  • 配置DWT的比较寄存器为目标地址,设置监视读访问;
  • 使能调试监视器中断,在中断服务函数中处理触发事件。

示例代码片段:

#include "stm32f4xx_hal.h"

#define TARGET_ADDR 0x20000000  // 目标RAM地址

void DWT_Watchpoint_Init(void) {
  // 使能DWT模块
  CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
  DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
  
  // 设置监视地址
  DWT->COMP0 = TARGET_ADDR;
  // 配置为监视读访问(FUNCTION0字段设为0b010)
  DWT->FUNCTION0 = (DWT_FUNCTION0_FUNCTION_READ << DWT_FUNCTION0_FUNCTION_Pos) |
                   (DWT_FUNCTION0_MATCH_VALUE << DWT_FUNCTION0_MATCH_Pos);
  
  // 使能调试监视器中断
  NVIC_EnableIRQ(DebugMonitor_IRQn);
  SCB->SHCSR |= SCB_SHCSR_DEBUGMONITORENA_Msk;
}

void DebugMonitor_IRQHandler(void) {
  // 校验是否为DWT读监视触发
  if(DWT->CTRL & DWT_CTRL_MATCH0_Msk) {
    // 执行自定义中断逻辑
    
    // 清除匹配标志
    DWT->CTRL &= ~DWT_CTRL_MATCH0_Msk;
  }
}

注意:不同STM32型号的DWT监视点数量、功能细节有差异,需参考对应芯片参考手册。

3. 软件模拟触发

如果硬件方案受限,可通过软件封装强制触发中断:

  • 将目标变量绑定到指定地址,封装读取函数;
  • 每次通过函数读取变量时,手动触发指定中断。

示例代码片段:

#include "stm32f4xx_hal.h"

#define TARGET_ADDR 0x20000000  // 目标RAM地址
uint32_t target_var __attribute__((at(TARGET_ADDR)));  // 指定变量地址

uint32_t read_target_var(void) {
  uint32_t val = target_var;
  // 触发自定义中断(示例使用TIM1更新中断,可替换为其他空闲中断)
  NVIC_SetPendingIRQ(TIM1_UP_IRQn);
  return val;
}

void TIM1_UP_IRQHandler(void) {
  // 执行自定义中断逻辑
  TIM1->SR &= ~TIM_SR_UIF;  // 清除中断标志
}

优点:实现简单、可控;缺点:仅能捕获通过封装函数的读取操作,无法拦截直接的地址访问(如汇编指令)。

内容的提问来源于stack exchange,提问作者Pjman Yucifee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 22:41:14