【STM32 LWIP配置】STM32H723ZG + Ethernet +LWIP 配置 cubemx
STM32H723ZG + LAN8742 + Ethernet +LWIP 配置 cubemx
🌞这边记录一下这块mcu 配置以太网的过程,IDE是KEIL MDK,其实就是在下面多次提到的blog的基础上 在scatter file进行配置
首先,如果想要简单一点 直接去cubemx 那边获取相关的例程,直接在例程上面改 根据实际要求注意改相关的引脚 和 时钟配置
感谢这个blog
墙裂感谢我超级好的同事 zxp哥 加班帮我解决问题!!
文章目录
- STM32H723ZG + LAN8742 + Ethernet +LWIP 配置 cubemx
- 法一 直接用cubemx 上面的库进行配置
- 法二 用cubemx自己配置
- 前置知识
- 不同的外设存储区可访问的区域不同 所以要自己定义相应的内存区域
- 为什么这么配置
- MPU的配置
- 1. MPU 区域 0:默认配置(Default Configuration)
- 2. MPU 区域 1:以太网 DMA 描述符(Ethernet DMA Descriptors)
- 3. MPU 区域 2:LwIP RAM 堆(LwIP RAM Heap)
- 开始配置
- 1.配置ETH 开启
- 2.串口的配置
- 3.LWIP配置
- 4.PHY的复位引脚配置 参考上述博客
- 5.MPU的配置
- 6.打开ethernetif.c文件 对相应的scatter file文件进行配置
- 测试
- 测试说明
- 最终我的main.c的代码
法一 直接用cubemx 上面的库进行配置
如果你的板子是stm32+lan8742 那可以参考这个方法进行配置
输入相应开发板的型号 然后直接配置 比如这个开发板
到时候可以直接在这个例程上改 成功的概率会比自己用cubemx移植的概率高
ps:后面一些相关的例程也可以用这种方法参考
法二 用cubemx自己配置
前置知识
ps 这两个前置知识 是两个坑点 当然也可以跳过这个前置知识 直接到cubemx的配置部分
不同的外设存储区可访问的区域不同 所以要自己定义相应的内存区域
以太网外设通常需要专用的内存区域来存储发送和接收的数据包。这些区域被称为DMA描述符(Descriptors)和 缓冲区Buffers
1️⃣ 首先以太网外设不是和其他外设一样 可以随意的放在单片机的不同地方
❗️ 不能将以太网的数据缓冲区随意放在 CPU 的 ITCM 里,也不能将 LTDC 的图像数据放在 D2 域的 SRAM1 里,因为它们位于不同的总线域,并且由不同的 DMA 控制器管理。需要在软件中根据这个硬件架构,将不同的外设缓冲区分配到它们所属的或可访问的存储区域中。
首先这张图清楚地说明了:不同的外设可以存储数据的地方是不一样的。
- 分域存储:
- D1 域(高性能域) 的外设(如 SDMMC, MDMA, DMA2D, LTDC)通常会访问 D1 域中的高速存储器,例如 AXI SRAM 和 Flash。这是因为这些外设需要高速的数据吞吐量来处理大量数据(例如图形、视频、高速存储卡)。
- D2 域(中速域) 的外设(如 Ethernet MAC, USBHS1)通常会访问 D2 域中的 SRAM1 和 SRAM2。虽然这些也是SRAM,但它们位于不同的总线矩阵上,且可能由不同的 DMA 控制器(DMA1, DMA2)来管理。
- DMA控制器与存储区域的绑定:
- D1 域 的 DMA 控制器(MDMA, DMA2D)通常用于在 D1 域的内存(AXI SRAM、Flash等)之间进行数据传输。
- D2 域 的 DMA 控制器(DMA1, DMA2)通常用于在外设(Ethernet MAC、USBHS1)与 D2 域的内存(SRAM1、SRAM2)之间进行数据传输。
- 专用存储区:
- D3 域 有一个 Backup SRAM,这个存储区通常是专门用来在低功耗或掉电模式下保留关键数据的。其他外设一般不会使用这块内存。
- CPU 有专门的 ITCM 和 DTCM,这些是紧耦合内存,只供CPU使用,用于存储最高速的指令和数据。
总结一下:
这张架构图的核心思想就是根据外设的性能需求和功能,将它们和相应的存储区域划分到不同的总线域中。
❗️ 所以,不能将以太网的数据缓冲区随意放在 CPU 的 ITCM 里,也不能将 LTDC 的图像数据放在 D2 域的 SRAM1 里,因为它们位于不同的总线域,并且由不同的 DMA 控制器管理。需要在软件中根据这个硬件架构,将不同的外设缓冲区分配到它们所属的或可访问的存储区域中。
从这个总线矩阵上可以看到,以太网外设可以被定义在SRAM1,SRAM2中,对应的就是0x30000000 - 0x30003FFF
于是 以太网的DMA描述符就得后续这么定义在相应的存储区
从上面的图也可以看到有两种不同的定义方式,一个是直接在前面定义
__attribute__((at(0x30000000))) ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
__attribute__((at(0x30000060))) ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
将一个名为 DMARxDscrTab
的、由 ETH_RX_DESC_CNT
个 ETH_DMADescTypeDef
结构体组成的数组,精确地放置在内存地址 0x30000000
上。 这块内存区域将作为以太网控制器接收DMA描述符的存储区域。
将一个名为 DMATxDscrTab
的、由 ETH_TX_DESC_CNT
个 ETH_DMADescTypeDef
结构体组成的数组,精确地放置在内存地址 0x30000060
上。 这块内存区域将作为以太网控制器发送DMA描述符的存储区域。
memory_RX_POOL_base 0x30000100 0x3F00 {*(.Rx_PoolSection)}
这个意思就是:在链接器脚本中,定义了一个内存区域,其起始地址为 0x30000100
,大小为 0x3F00
字节。然后,告诉链接器,将所有输入目标文件(*.o
)中所有名为 .Rx_PoolSection
的代码或数据段,都链接到这块内存区域中。
这是专门为**以太网接收缓冲区(RX Pool)**分配一块特定的内存区域,并确保这个区域位于一个固定的、已知的高速内存地址(例如 SRAM),以便于 DMA 控制器可以高效地访问
.sct文件在魔法棒这边 点击edit就会出现了
为什么这么配置
- DMA控制器的局限性: 并非所有内存区域都能被DMA控制器访问。通常,DMA控制器只能访问特定的片上SRAM或外部SRAM/SDRAM。而像闪存(Flash)或CPU的私有内存(如ITCM/DTCM)等区域是无法直接被DMA访问的。
- DMA描述符的存储位置: 以太网控制器(或任何DMA控制器)需要从一个预先配置好的内存地址读取DMA描述符,这些描述符包含了数据在内存中的地址、长度等信息。
- 确保可访问性: 为了确保以太网控制器能够正确地找到并访问这些描述符,必须将它们放置在一个DMA控制器能够访问的内存地址上。
通过 __attribute__((at(...)))
这样的方式,程序员手动指定了这些关键数据结构(DMA描述符)的内存地址,确保它们位于可被DMA访问的高速SRAM区域(例如,在STM32系列芯片中,0x30000000
通常指向D1域的高速SRAM)。
这与链接器脚本的 *(.Rx_PoolSection)
方式是异曲同工的,两者都是为了控制数据在内存中的绝对位置。前者是直接在代码中使用编译器扩展,后者是在链接阶段使用链接器脚本。在某些情况下,两者可以结合使用,以获得更精细的控制。
MPU的配置
Cortex-M7 的 **MPU (Memory Protection Unit,内存保护单元)**是一个硬件单元,它允许你定义多达 8 个独立的内存区域,并为每个区域设置不同的访问权限和属性。这些属性包括:
- 访问权限(AccessPermission): 读/写/禁止访问。
- 可执行性(DisableExec): 允许或禁止从该区域执行代码。
- 缓存属性(IsCacheable, IsBufferable, IsShareable): 控制该区域的内存如何与 CPU 的数据缓存(D-Cache)和总线交互。
通过配置 MPU 的缓存属性,来解决 Cortex-M7 的高速缓存(D-Cache)与以太网 DMA 控制器之间的数据一致性问题。
- 将 DMA 描述符和以太网数据缓冲区所在的内存区域设置为“不可缓存”,强制 CPU 每次都直接访问物理内存。
- 这样做虽然牺牲了部分 CPU 访问这些特定区域时的性能(因为没有使用缓存),但换来了数据一致性的可靠性,这对于网络通信这类对数据完整性要求极高的应用是至关重要的。
以太网相应的MPU配置
void MPU_Config(void)
{MPU_Region_InitTypeDef MPU_InitStruct;/* Disable the MPU */HAL_MPU_Disable();/* Configure the MPU as Strongly ordered for not defined regions */MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.BaseAddress = 0x00;MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;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 = 0x87;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/* Configure the MPU attributes as Device not cacheablefor ETH DMA descriptors */MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.BaseAddress = 0x30000000;MPU_InitStruct.Size = MPU_REGION_SIZE_256B;MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;MPU_InitStruct.Number = MPU_REGION_NUMBER1;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;MPU_InitStruct.SubRegionDisable = 0x00;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/* Configure the MPU attributes as Normal Non Cacheablefor LwIP RAM heap which contains the Tx buffers */MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.BaseAddress = 0x30004000;MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;MPU_InitStruct.AccessPermission = MPU_REGION_FULL_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_NUMBER2;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;MPU_InitStruct.SubRegionDisable = 0x00;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/* Enable the MPU */HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
1. MPU 区域 0:默认配置(Default Configuration)
/* Configure the MPU as Strongly ordered for not defined regions */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x00;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
...
HAL_MPU_ConfigRegion(&MPU_InitStruct);
- 作用: 这部分配置了一个覆盖整个 4GB 地址空间的默认区域(区域0)。
- 属性: 它被设置为
Strongly ordered
(强排序),No Access
(禁止访问),Not Cacheable
(不可缓存)。SubRegionDisable
设置为0x87
,这通常是为了禁用某些子区域,从而让其他区域的配置生效。 - 为什么这么做? 这是一个安全和规范的默认设置。它相当于一个“看门狗”,将所有未被明确配置的内存区域都设置为最严格的“禁止访问”和“强排序”属性。这样可以防止程序意外访问到未分配的内存区域,从而引发硬件异常,提高系统的鲁棒性。
2. MPU 区域 1:以太网 DMA 描述符(Ethernet DMA Descriptors)
/* Configure the MPU attributes as Device not cacheablefor ETH DMA descriptors */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x30000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_256B;
...
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
...
HAL_MPU_ConfigRegion(&MPU_InitStruct);
- 作用: 为以太网 DMA 描述符所在的内存区域(
0x30000000
)进行配置。这与你之前提到的代码__attribute__((at(0x30000000)))
是相对应的。 - 属性:
BaseAddress
和Size
: 准确地指定了 DMA 描述符表所在的内存地址和大小(256B)。IsCacheable = MPU_ACCESS_NOT_CACHEABLE
: 这是最关键的配置之一! 它告诉 CPU,这个区域的内存不能被缓存。IsBufferable = MPU_ACCESS_BUFFERABLE
: 允许写操作进行写缓冲(Write Buffer)。
- 为什么这么做?
- 缓存一致性问题: DMA 控制器和 CPU 是两个独立的实体,它们都可以访问同一块内存区域。当 CPU 读取数据时,它可能会将数据从 SRAM 拷贝到它的高速缓存(D-Cache)中。如果 DMA 在此期间更新了 SRAM 中的数据,但 CPU 仍然使用缓存中的旧数据,就会导致缓存不一致(Cache Incoherence)的问题,从而引发程序错误。
- 解决方案: 将 DMA 描述符所在的内存区域设置为 不可缓存(Not Cacheable),强制 CPU 每次读写该区域时都直接访问 SRAM。这样就保证了 CPU 看到的数据永远是最新的,与 DMA 控制器看到的数据是一致的。
3. MPU 区域 2:LwIP RAM 堆(LwIP RAM Heap)
/* Configure the MPU attributes as Normal Non Cacheablefor LwIP RAM heap which contains the Tx buffers */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x30004000;
MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
...
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
...
HAL_MPU_ConfigRegion(&MPU_InitStruct);
- 作用: 为 LwIP(一个轻量级TCP/IP协议栈)的 RAM 堆(Heap)进行配置,这个堆通常用于存储发送和接收数据包的缓冲区。
- 属性:
BaseAddress
和Size
: 指定了 RAM 堆的地址和大小。IsCacheable = MPU_ACCESS_NOT_CACHEABLE
: 同样,这个区域被设置为不可缓存。IsBufferable = MPU_ACCESS_NOT_BUFFERABLE
: 也被设置为不可缓冲。
- 为什么这么做?
- 原因与区域1类似,都是为了解决缓存一致性问题。LwIP 的数据缓冲区同样会被以太网 DMA 控制器读写,同时也可能被 CPU 读写。
- 如果这个区域可缓存,CPU 可能会读到过时的数据,或者 CPU 写入的数据还在缓存中,还没有写回 SRAM,而 DMA 却去读取了旧的数据。这都会导致网络通信的错误。
- 将该区域设置为不可缓存,确保 CPU 读写缓冲区时都直接操作物理内存,从而保证了 CPU 和 DMA 之间的数据一致性。
开始配置
这边配置ETH的部分多数是参考这个blog 后面也会多次提及
我主要是针对我这个板子 在keil MDK上的配置
首先RCC SYS 这些就不说了 就是比较常规的配置
1.配置ETH 开启
引脚全部配置成高速
注意mac地址不能随便配 默认就好 其他的需要好好配 对应于上面的前置知识所说的 地址
2.串口的配置
3.LWIP配置
这一部分开始大部分参考这篇blog 大家可以直接看原文
这里用dhcp 也就是用路由器给单片机分配ip
选择LAN8742
checksum 默认的就行
4.PHY的复位引脚配置 参考上述博客
5.MPU的配置
参考blog
最后导出到KEIL_MDK 按照这个blog说的 该加的地方就加 该添的地方添
6.打开ethernetif.c文件 对相应的scatter file文件进行配置
参考这边blog2可以看到相应的scatter file文件的编辑
现在这样操作可以让KEIL自动生成一个sct文件:
1.去掉勾选Usw Menory Layout from Target Dialog,这时Scatter File自动生成一个sct文件在obj文件的输出路径下
2.重新勾选Usw Menory Layout from Target Dialog,编译工程
3.编译成功后就生成了一个默认的sct文件,其配置与Target页面的内存布局含义一样,再回到Options->Linker就可以点击Edit打开该sct文件
然后就可以开始编辑了
加上这一句
memory_RX_POOL_base 0x30000100 0x3F00 {*(.Rx_PoolSection)}
测试
测试说明
用一根网线 一端连接单片机 一端连接路由器
在原有的代码加上这几句
/* USER CODE BEGIN PFP */
extern struct netif gnetif;
struct dhcp *pdhcp;
/* USER CODE END PFP */
pdhcp = netif_dhcp_data(&gnetif);
打开debug模式 把pdhcp添加到监控区 这边看到的就是路由器给我们板子分配的ip啦! 这样就说明连接上了。 后面就可以进行相应的udp/tcp测试~~ 偷个懒可以直接在cubemx上面下载相应的库 然后直接移植,亲测可用
最终我的main.c的代码
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "lwip.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
/* USER CODE BEGIN PFP */
extern struct netif gnetif;
struct dhcp *pdhcp;
/* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MPU Configuration--------------------------------------------------------*/MPU_Config();/* Enable the CPU Cache *//* Enable I-Cache---------------------------------------------------------*/SCB_EnableICache();/* Enable D-Cache---------------------------------------------------------*/SCB_EnableDCache();/* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* Enable I-Cache---------------------------------------------------------*/SCB_EnableICache();/* Enable D-Cache---------------------------------------------------------*/SCB_EnableDCache();/* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit */
__HAL_RCC_D2SRAM1_CLK_ENABLE();/* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_LWIP_Init();/* USER CODE BEGIN 2 *///DCacheSCB_CleanInvalidateDCache();//PHYHAL_GPIO_WritePin(RMII_NRST_GPIO_Port, RMII_NRST_Pin, GPIO_PIN_RESET);HAL_Delay(100);HAL_GPIO_WritePin(RMII_NRST_GPIO_Port, RMII_NRST_Pin, GPIO_PIN_SET);HAL_Delay(100);pdhcp = netif_dhcp_data(&gnetif);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */MX_LWIP_Process();}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Supply configuration update enable*/HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);/** Configure the main internal regulator output voltage*/__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;RCC_OscInitStruct.HSICalibrationValue = 64;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//* MPU Configuration */void MPU_Config(void)
{MPU_Region_InitTypeDef MPU_InitStruct = {0};/* Disables the MPU */HAL_MPU_Disable();/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.Number = MPU_REGION_NUMBER0;MPU_InitStruct.BaseAddress = 0x30000000;MPU_InitStruct.Size = MPU_REGION_SIZE_256B;MPU_InitStruct.SubRegionDisable = 0x00;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER1;MPU_InitStruct.BaseAddress = 0x030004000;MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;MPU_InitStruct.SubRegionDisable = 0x0;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER2;MPU_InitStruct.BaseAddress = 0x30004000;MPU_InitStruct.SubRegionDisable = 0x00;MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/* Enables the MPU */HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);}/*** @brief This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
最终我的代码 stm32H723ZG+LAN8742_HAL_KEIL 也上传到gitee上
然后墙裂安利一下可以比较文件的工具beyond compare 有兴趣的家人可以去看看
🌈ok 完结 撒花撒花★,°:.☆( ̄▽ ̄)/$:.°★ 。