【CubeMX-HAL库】STM32H743II——SDRAM配置所遇问题

embedded/2024/11/17 22:00:10/

使用FMC访问外部地址时,必须配置MPU(内存保护单元)!

推荐的博客和视频:

1、【CubeMX-HAL库】STM32H743—FMC配置SDRAM_stm32h743 sdram 速度-CSDN博客

2、【【STM32CubeMX教程】STM32全外设原理、配置和常用HAL、LL库API使用详解】

3、在百度网盘里有STM32H743的例程:【通过网盘分享的文件:STM32H743官方全部例程】

4、正点原子的开发指南:【通过网盘分享的文件:STM32H743 阿波罗开发指南V1.0】

注:以上就相当于是四种方法,FMC和SDRAM都可以配置好,具体的问题会在下面说。

 自己CubeMX中的配置

FMC初始化

 SDRAM时钟分频、延时和时序

时序是如何配置的可以看以上的博客和视频。时序在CubeMX中配置的时候是会有报错的,可以不用理会。

额外所需的SDRAM发送初始化序列配置

这些代码是在CubeMX中是没有配置的,所以需要额外写。在以上视频和博客中可以看到,官方库、其他人和正点原子都给你写好了,直接将它们的代码全部移植过来就行,并不困难。

例如这是我移植的正点原子的:

/*** @brief       发送SDRAM初始化序列* @param       无* @retval      无*/
void sdram_initialization_sequence(void)
{uint32_t temp = 0;/* SDRAM控制器初始化完成以后还需要按照如下顺序初始化SDRAM */sdram_send_cmd(0, FMC_SDRAM_CMD_CLK_ENABLE, 1, 0);                /* 时钟配置使能 */delay_us(500);                                                    /* 至少延时500us */sdram_send_cmd(0, FMC_SDRAM_CMD_PALL, 1, 0);                      /* 对所有存储区预充电 */sdram_send_cmd(0, FMC_SDRAM_CMD_AUTOREFRESH_MODE, 8, 0);          /* 设置自刷新次数 *//* 配置模式寄存器,SDRAM的bit0~bit2为指定突发访问的长度,* bit3为指定突发访问的类型,bit4~bit6为CAS值,bit7和bit8为运行模式* bit9为指定的写突发模式,bit10和bit11位保留位 */temp = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1  |                  /* 设置突发长度:1(可以是1/2/4/8) */SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL  |                  /* 设置突发类型:连续(可以是连续/交错) */SDRAM_MODEREG_CAS_LATENCY_2          |                  /* 设置CAS值:2(可以是2/3) */SDRAM_MODEREG_OPERATING_MODE_STANDARD|                  /* 设置操作模式:0,标准模式 */SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;                   /* 设置突发写模式:1,单点访问 */sdram_send_cmd(0, FMC_SDRAM_CMD_LOAD_MODE, 1, temp);              /* 设置SDRAM的模式寄存器 */
}

还有注意也要同时移植好测试函数,来测试读写SDRAM是否有问题:

/*** @brief       SDRAM内存测试* @param       x,y:起点坐标* @retval      无
*/
void sdram_test(uint16_t x, uint16_t y)
{  uint32_t i = 0;uint32_t temp = 0;uint32_t sval = 0;        /* 在地址0读到的数据 *//* 每隔16K字节,写入一个数据,总共写入2048个数据,刚好是32M字节 */for (i = 0; i < 32 * 1024 * 1024; i += 16 * 1024){*(volatile uint32_t *)(BANK5_SDRAM_ADDR + i) = temp; temp++;}/* 依次读出之前写入的数据,进行校验 */for (i = 0; i < 32 * 1024 * 1024; i += 16 * 1024) {temp =*(volatile uint32_t*)(BANK5_SDRAM_ADDR + i);if (i == 0){sval = temp;}else if (temp <= sval){break;             /* 后面读出的数据一定要比第一次读到的数据大 */}printf("SDRAM Capacity:%dKB\r\n", (uint16_t)(temp - sval + 1) * 16);           /* 打印SDRAM容量 */}
}

 所遇到的问题和解决方法

配置好FMC和SDRAM后,SDRAM测试程序中,不能对SDRAM进行正常读写,并且会跳入故障中断函数以及硬故障中断函数中。

第一次

以上的操作还是比较简单的。但是在我第一次测试的时候,发现代码卡住在测试代码中第一次写入一个数据中,然后就跳到了一个故障中断函数里:

是一个内存故障处理函数,于是我发现在CubeMX配置内存保护的里面,Speculation default mode(推测默认模式)被设置为了Enable,这个主要是用于平衡性能与功耗。但是在STM32等微控制器的配置中,可能会遇到与Speculation相关的设置选项,如CORTEX-M7核心的Speculation default mode。在这种情况下,将其从Enabled(启用)改为Disabled(禁用)可能是出于减少不必要的麻烦、提高稳定性或满足特定安全要求的考虑。

第二次

第二次我关闭了内存保护,测试的时候,跳到的是一个硬故障中断的函数中,这可能是由于处理器在访问内存时遇到了无法恢复的硬件错误:非法指令、栈溢出、访问未定义的内存地址等。此时就确信是内存保护的问题了。

第三次

重新打开MPU内存保护,第三次测试的时候,代码还是跳到硬故障中断函数中:

对照着正点原子的配置发现,就是内存保护单元没有配置好的问题,在Cube中配置好就行了,注意其内存保护单元的配置是在M7内核里配置的,对照着正点原子配置就好了:
(还有MPU Control Mode,我配置的是Background Region Privileged access only + MPU Enable during hard fault, NMI, and FAULTMASK handlers

/*** @brief       设置某个区域的MPU保护* @param       baseaddr:MPU保护区域的基址(首地址)* @param       size:MPU保护区域的大小(必须是32的倍数,单位为字节),可设置的值参考:CORTEX_MPU_Region_Size* @param       rnum:MPU保护区编号,范围:0~7,最大支持8个保护区域,可设置的值参考:CORTEX_MPU_Region_Number* @param       de:禁止指令访问;0,允许指令访问;1,禁止指令访问* @param       ap:访问权限,访问关系如下:可设置的值参考:CORTEX_MPU_Region_Permission_Attributes*   @arg       MPU_REGION_NO_ACCESS,无访问(特权&用户都不可访问)*   @arg       MPU_REGION_PRIV_RW,仅支持特权读写访问*   @arg       MPU_REGION_PRIV_RW_URO,禁止用户写访问(特权可读写访问)*   @arg       MPU_REGION_FULL_ACCESS,全访问(特权&用户都可访问)*   @arg       MPU_REGION_PRIV_RO,仅支持特权读访问*   @arg       MPU_REGION_PRIV_RO_URO,只读(特权&用户都不可以写)*   @arg       详见:STM32F7编程手册.pdf,4.6节,Table 89.* @param       sen  : 是否允许共用;MPU_ACCESS_NOT_SHAREABLE,不允许;MPU_ACCESS_SHAREABLE,允许* @param       cen  : 是否允许cache;MPU_ACCESS_NOT_CACHEABLE,不允许;MPU_ACCESS_CACHEABLE,允许* @param       ben  : 是否允许缓冲;MPU_ACCESS_NOT_BUFFERABLE,不允许;MPU_ACCESS_BUFFERABLE,允许* @retval      0,成功.*              其他,错误.*/
uint8_t mpu_set_protection(uint32_t baseaddr, uint32_t size, uint32_t rnum, uint8_t de, uint8_t ap, uint8_t sen, uint8_t cen, uint8_t ben)
{MPU_Region_InitTypeDef mpu_region_init_handle;HAL_MPU_Disable();                                                   /* 配置MPU之前先关闭MPU,配置完成以后在使能MPU */mpu_region_init_handle.Enable = MPU_REGION_ENABLE;                   /* 使能该保护区域 */mpu_region_init_handle.Number = rnum;                                /* 设置保护区域 */mpu_region_init_handle.BaseAddress = baseaddr;                       /* 设置基址 */mpu_region_init_handle.DisableExec = de;                             /* 是否允许指令访问 */mpu_region_init_handle.Size = size;                                  /* 设置保护区域大小 */mpu_region_init_handle.SubRegionDisable = 0X00;                      /* 禁止子区域 */mpu_region_init_handle.TypeExtField = MPU_TEX_LEVEL0;                /* 设置类型扩展域为level0 */mpu_region_init_handle.AccessPermission = (uint8_t)ap;               /* 设置访问权限, */mpu_region_init_handle.IsShareable = sen;                            /* 是否共用? */mpu_region_init_handle.IsCacheable = cen;                            /* 是否cache? */mpu_region_init_handle.IsBufferable = ben;                           /* 是否缓冲? */HAL_MPU_ConfigRegion(&mpu_region_init_handle);                       /* 配置MPU */HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                              /* 开启MPU */return 0;
}/*** @brief       设置需要保护的存储块* @param       无* @note        必须对部分存储区域进行MPU保护,否则可能导致程序运行异常*              比如MCU屏不显示,摄像头采集数据出错等等问题...*/
void mpu_memory_protection(void)
{/* 保护整个DTCM,共128K字节 */mpu_set_protection( 0x20000000,                 /* 基地址 */MPU_REGION_SIZE_128KB,      /* 长度 */MPU_REGION_NUMBER1, 0,      /* NUMER1,允许指令访问 */MPU_REGION_FULL_ACCESS,     /* 全访问 */MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共用 */MPU_ACCESS_CACHEABLE,       /* 允许cache */MPU_ACCESS_BUFFERABLE);     /* 允许缓冲 *//* 保护整个AXI SRAM,共512K字节 */mpu_set_protection( 0x24000000,                 /* 基地址 */MPU_REGION_SIZE_512KB,      /* 长度 */MPU_REGION_NUMBER2, 0,      /* NUMER2,允许指令访问 */MPU_REGION_FULL_ACCESS,     /* 全访问 */MPU_ACCESS_SHAREABLE,       /* 允许共用 */MPU_ACCESS_CACHEABLE,       /* 允许cache */MPU_ACCESS_NOT_BUFFERABLE); /* 禁止缓冲 *//* 保护整个SRAM1~SRAM3,共512K字节 */mpu_set_protection( 0x30000000,                 /* 基地址 */MPU_REGION_SIZE_512KB,      /* 长度 */MPU_REGION_NUMBER3, 0,      /* NUMER3,允许指令访问 */MPU_REGION_FULL_ACCESS,     /* 全访问 */MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共用 */MPU_ACCESS_CACHEABLE,       /* 允许cache */MPU_ACCESS_BUFFERABLE);     /* 允许缓冲 *//* 保护整个SRAM4,共64K字节 */mpu_set_protection( 0x38000000,                 /* 基地址 */MPU_REGION_SIZE_64KB,       /* 长度 */MPU_REGION_NUMBER4, 0,      /* NUMER4,允许指令访问 */MPU_REGION_FULL_ACCESS,     /* 全访问 */MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共用 */MPU_ACCESS_CACHEABLE,       /* 允许cache */MPU_ACCESS_BUFFERABLE);     /* 允许缓冲 *//* 保护MCU LCD屏所在的FMC区域,共64M字节 */mpu_set_protection( 0x60000000,                 /* 基地址 */MPU_REGION_SIZE_64MB,       /* 长度 */MPU_REGION_NUMBER5, 0,      /* NUMER5,允许指令访问 */MPU_REGION_FULL_ACCESS,     /* 全访问 */MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共用 */MPU_ACCESS_NOT_CACHEABLE,   /* 禁止cache */MPU_ACCESS_NOT_BUFFERABLE); /* 禁止缓冲 *//* 保护SDRAM区域,共32M字节 */mpu_set_protection( 0xC0000000,                 /* 基地址 */MPU_REGION_SIZE_32MB,       /* 长度 */MPU_REGION_NUMBER6, 0,      /* NUMER6,允许指令访问 */MPU_REGION_FULL_ACCESS,     /* 全访问 */MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共用 */MPU_ACCESS_CACHEABLE,       /* 允许cache */MPU_ACCESS_BUFFERABLE);     /* 允许缓冲 *//* 保护整个NAND FLASH区域,共256M字节 */mpu_set_protection( 0x80000000,                 /* 基地址 */MPU_REGION_SIZE_256MB,      /* 长度 */MPU_REGION_NUMBER7, 1,      /* NUMER7,禁止指令访问 */MPU_REGION_FULL_ACCESS,     /* 全访问 */MPU_ACCESS_NOT_SHAREABLE,   /* 禁止共用 */MPU_ACCESS_NOT_CACHEABLE,   /* 禁止cache */MPU_ACCESS_NOT_BUFFERABLE); /* 禁止缓冲 */
}

最后再测试的时候,就可以得到正常的结果了:

结论

在配置STM32H7的SDRAM的时候(其实不止这个,在配置有关内存的时候),都需要配置好内存保护单元。内存单元的配置在CubeMX中CORTEX_M7中配置,注意要关闭Speculation default mode(推测默认模式),也是为了减少不必要的麻烦、提高稳定性或满足特定安全要求。


http://www.ppmy.cn/embedded/138355.html

相关文章

网上商城开发:Spring Boot技术指南

2 相关技术 2.1 SpringBoot框架介绍 Spring Boot是一种不需要代码生成的一种框架&#xff0c;并且可以不需要配置任何的XML文件就可以&#xff0c;因为Spring Boot里面自带了很多接口&#xff0c;只需要配置不同的接口就会自动的应用并且识别需要的依赖&#xff0c;在配置方面非…

Redisson的可重入锁

初始状态&#xff1a; 表示系统或资源在没有线程持有锁的情况下的状态&#xff0c;任何线程都可以尝试获取锁。 线程 1 获得锁&#xff1a; 线程 1 首次获取了锁并进入受保护的代码区域。 线程 1 再次请求锁&#xff1a; 在持有锁的情况下&#xff0c;线程 1 再次请求锁&a…

CSS盒子的定位>(上篇)#定位属性#相对定位-附练习

一、定位属性 1.定位方式 position属性可以选择4种不同类型的定位方式。 语法格式&#xff1a;position&#xff1a;relation | absolute | fixed参数&#xff1a;①relative生成相对定位的元素&#xff0c;相对于其正常位置进行定位。 ②absolute生成绝对定位的…

uniapp 本地打包后使用http-server预览不了

使用nginx处理 我的环境 Apple M3 Max 14.1 (23B2073) 安装nginx brew install nginx找到nginx.conf文件 /opt/homebrew/etc/nginx/nginx.conf增加server server {listen 8091; # 或者其他未被占用的端口server_name localhost;location / {root /path/to/your/project/u…

如何实现主备租户的无缝切换 | OceanBase应用实践

对于DBA而言&#xff0c;确保数据库的高可用性、容灾等能力是其日常工作中需要持续思考和关注的重要事项。一方面&#xff0c;可以利用数据库自身所具备的功能来实现这些目标&#xff1b;若数据库本身不提供相应功能&#xff0c;DBA则需寻找其他工具来增强数据库的高可用性和容…

jenkins用户在执行scp的时候如何做免密登录

一、背景 在jenkins job中执行scp的shell命令&#xff0c;当然不希望每次输入密码&#xff0c;另外处于出于安全考虑&#xff0c;也不建议在scp命令中指定。 所以&#xff0c;我们需要对远程机器进行免密登录。 本文遇到的问题是&#xff0c;在jenkins机器上执行scp已做到了…

Flutter:InheritedWidget数据共享

未使用数据共享时&#xff0c;要传递数据&#xff0c;只能组件间一级一级向下传递 下边代码中&#xff0c;创建了一个按钮&#xff0c;当点击时_count&#xff0c; 并将数据通过Test1(count)传递给Test2(count)&#xff0c;最终传递给Test3(count)进行渲染展示。 import packa…

麒麟系统下docker搭建jenkins

首先我们需要创建宿主机挂载路径&#xff0c;我这里放在本地的/data/henkins/home,然后赋予权限&#xff0c;命令如下&#xff1a; mkdir -p /data/jenkins/home chown -R 1000:1000 /data/jenkins/home chmod -R 777 /data/jenkins/homedocker run -d --restart …