RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

深入理解Cortex-M內存管理(GCC)

嵌入式大雜燴 ? 來源:嵌入式大雜燴 ? 作者:嵌入式大雜燴 ? 2023-04-27 10:58 ? 次閱讀

在討論Cortex-M的內存之前,先來看看Cortex-M的存儲器系統(tǒng),我們知道,Cortex-M系列的處理器,大都可以對32的存儲器進行尋址,因此存儲器的尋址空間能夠達到4G,這就意味著指定和數(shù)據(jù)共用相同的地址空間,也就是將程序存儲器、數(shù)據(jù)存儲器、寄存器和輸入輸出端口被組織在同一個4GB的線性地址空間內。數(shù)據(jù)字節(jié)以小端格式存放在存儲器中。一個字里的最低地址字節(jié)被認為是該字的最低有效字節(jié),而最高地址字節(jié)是最高有效字節(jié)。

1 Cortex-M存儲器架構

4G的地址空間就是地址編碼的范圍。所謂編碼就是對每一個程序存儲器、數(shù)據(jù)存儲器、寄存器和輸入輸出端口(一個字節(jié))分配一個唯一的地址號碼,這個過程又叫做“編址”或者“地址映射”。這個過程就好像在日常生活中我們給每家每戶分配一個地址門牌號。與編碼相對應的是“尋址”過程——分配一個地址號碼給一個存儲單元的目的是為了便于找到它,完成數(shù)據(jù)的讀寫,這就是“尋址”,因此地址空間有時候又被稱作“尋址空間”。

有了4G的可尋址空間,我們就可通過尋址來操作相應的地址對象。這就需要將程序存儲器、數(shù)據(jù)存儲器、寄存器和輸入輸出端口進行統(tǒng)一編號,也就是存儲器映射。

存儲器映射是指把芯片中或芯片外的FLASH,RAM,外設,BOOTBLOCK等進行統(tǒng)一編址。即用地址來表示對象。這個地址絕大多數(shù)是由廠家規(guī)定好的,用戶只能用而不能改。用戶只能在掛外部RAM或FLASH的情況下可進行自定義。

如下圖,是Cortex-M3存儲器映射結構圖。

1682564017434e4w7hhxwn2

Cortex-M3是32位的內核,因此其PC指針可以指向2^32=4G的地址空間,也就是0x0000_0000——0xFFFF_FFFF這一大塊空間。根據(jù)圖中描述,Cortex-M3內核將0x0000_0000——0xFFFF_FFFF這塊4G大小的空間分成8大塊:代碼、SRAM、外設、外部RAM、外部設備、專用外設總線-內部、專用外設總線-外部、特定廠商等,因此使用該內核的設計者必須按照這個進行各自芯片的存儲器結構設計。

首先,我們對比一下Cortex-M3存儲器結構和STM32存儲器結構:

C:\\Users\\ouxiaolong\\Desktop\\科技部哪幾種.jpg

圖中可以很清晰的看到,STM32的存儲器結構和Cortex-M3的很相似,不同的是,STM32加入了很多實際的東西,如:Flash、SRAM等。只有加入了這些東西,才能成為一個擁有實際意義的、可以工作的處理芯片——STM32。

STM32的存儲器地址空間被劃分為大小相等的8塊區(qū)域,每塊區(qū)域大小為512MB。

地址范圍 描述
0x0000 0000 ~0x2000 0000 根據(jù)啟動引腳的狀態(tài)決定哪個存儲空間被映射到此處。 片內系統(tǒng)存儲區(qū)起始地址:0x1fff0000(2K字節(jié)的空間)
0x2000 0000 ~0x4000 0000 SRAM區(qū),64K,其中位帶別名區(qū)首地址為:0x2200 0000
0x4000 0000 ~0x6000 0000 用于片內外設,外設寄存器的別名區(qū)首地址:0x4200 0000
0x6000 0000 ~0x8000 0000
0x8000 0000 ~0xa000 0000 片上flash存儲區(qū)512M
0xa000 0000 ~0xc000 0000
0xc000 0000 ~0xe000 0000
0xe000 0000 ~0xffff ffff

對STM32存儲器知識的掌握,實際上就是對Flash和SRAM這兩個區(qū)域知識的掌握。由STM32的系統(tǒng)結構可以看出,F(xiàn)lash和SRAM這兩個區(qū)域分別由ICode總線和DCode總線與處理器通信,以此完成相應的數(shù)據(jù)交換。

168256401840268iwud23g6

當然啦,其他Cortex-M的處理和STM32的也是類似的,比如GD32、CH32等。

下面將重點描述Flash和SRAM的知識。

1.1 Cortex-M的SRAM

RAM隨機存儲器(Random Access Memory)表示既可以從中讀取數(shù)據(jù),也可以寫入數(shù)據(jù)。當機器電源關閉時,存于其中的數(shù)據(jù)就會丟失。比如電腦的內存條。

RAM有兩大類,一種稱為靜態(tài)RAM(Static RAM/SRAM),SRAM速度非常快,是目前讀寫最快的存儲設備了,但是它也非常昂貴,所以只在要求很苛刻的地方使用,譬如CPU的一級緩沖,二級緩沖。另一種稱為動態(tài)RAM(Dynamic RAM/DRAM),DRAM保留數(shù)據(jù)的時間很短,速度也比SRAM慢,不過它還是比任何的ROM都要快,但從價格上來說DRAM相比SRAM要便宜很多,計算機內存就是DRAM的。

DRAM分為很多種,常見的主要有FPRAM/FastPage、EDORAM、SDRAM、DDR RAM、RDRAM、SGRAM以及WRAM等,這里介紹其中的一種DDR RAM。

DDR RAM(Date-Rate RAM)也稱作DDR SDRAM,這種改進型的RAM和SDRAM是基本一樣的,不同之處在于它可以在一個時鐘讀寫兩次數(shù)據(jù),這樣就使得數(shù)據(jù)傳輸速度加倍了。這是目前電腦中用得最多的內存,而且它有著成本優(yōu)勢,事實上擊敗了Intel的另外一種內存標準-Rambus DRAM。在很多高端的顯卡上,也配備了高速DDR RAM來提高帶寬,這可以大幅度提高3D加速卡的像素渲染能力。

為什么需要RAM,因為相對FlASH而言,RAM的速度快很多,所有數(shù)據(jù)在FLASH里面讀取太慢了,為了加快速度,就把一些需要和CPU交換的數(shù)據(jù)讀到RAM里來執(zhí)行。

STM32單片機內部的 RAM 為 SRAM。不同類型的Cortex-M單片機的SRAM大小是不一樣的,但起始地址都是0x2000 0000,終止地址都是0x2000 0000+其固定的容量大小。SRAM相對容量小,速度快,掉電數(shù)據(jù)丟失,其作用是用來存取各種動態(tài)的輸入輸出數(shù)據(jù)、中間計算結果以及與外部存儲器交換的數(shù)據(jù)和暫存數(shù)據(jù)。設備斷電后,SRAM中存儲的數(shù)據(jù)就會丟失。

1.2 Cortex-M的Flash

Cortex-M的Flash,嚴格說,應該是Flash模塊。該Flash模塊包括: Flash主存儲區(qū)(Main memory)、Flash信息區(qū)(Information block),以及Flash存儲接口寄存器區(qū)(Flash memory interface) 。三個組成部分分別在0x0000 0000——0xFFFF FFFF不同的區(qū)域。下面介紹STM32的Flash,如下表所示。

1682564019028gm87ecnlid

STM32的閃存模塊由:__主存儲器、信息塊和閃存儲器塊__3部分組成。

主存儲器 ,該部分用來存放代碼和數(shù)據(jù)常數(shù)(如加const類型的數(shù)據(jù))。對于大容量產(chǎn)品,其被劃分為256頁,每頁2K,注意,小容量和中容量產(chǎn)品則每頁只有1K字節(jié)。主存儲起的起始地址為0X08000000,B0、B1都接GND的時候,就從0X08000000開始運行代碼。

信息塊 ,該部分分為2個部分,其中啟動程序代碼,是用來存儲ST自帶的啟動程序,用于串口下載,當B0接3.3V,B1接GND時,運行的就這部分代碼,用戶選擇字節(jié),則一般用于配置保護等功能。

閃存儲器塊 ,該部分用于控制閃存儲器讀取等,是整個閃存儲器的控制機構。

對于主存儲器和信息塊的寫入有內嵌的閃存編程管理;編程與擦除的高壓由內部產(chǎn)生。

在執(zhí)行閃存寫操作時,任何對閃存的讀操作都會鎖定總線,在寫完成后才能正確進行,在進行讀取或擦除操作時,不能進行代碼或者數(shù)據(jù)的讀取操作。

2 C程序內存分析

在C/C++程序中,編譯的程序占用內存分為5個區(qū),分別為__棧區(qū)、堆區(qū)、全局/靜態(tài)存儲區(qū)、常量存儲區(qū)、代碼區(qū)__。

1682564019718t41ik6fneo

1.Text段(Code Segment/Text Segment,代碼段) :通常是指用來存放程序執(zhí)行代碼的一塊內存區(qū)域,也就是存放CPU執(zhí)行的機器指令(machine instructions)。這部分區(qū)域的大小在程序運行前就已經(jīng)確定,并且內存區(qū)域通常屬于只讀(某些架構也允許代碼段為可寫,即允許修改程序)。在代碼段中,也有可能包含一些只讀的常數(shù)變量,例如字符串常量等。

2.全局初始化數(shù)據(jù)區(qū)/靜態(tài)數(shù)據(jù)區(qū)(Initialized data segment/Data segment) :該區(qū)包含了在程序中明確被初始化的全局變量、靜態(tài)變量(包括全局靜態(tài)變量和局部靜態(tài)變量)和常量數(shù)據(jù)(如字符串常量)。數(shù)據(jù)段屬于靜態(tài)內存分配。static聲明的變量放在data段。

3.BSS段(Block Started by Symbol) :BSS段通常是指用來存放程序中未初始化的全局變量的一塊內存區(qū)域。BSS段屬于靜態(tài)內存分配。

4.堆(heap) :堆是用于存放程序運行中被動態(tài)分配的內存段,它的大小并不固定,可動態(tài)擴張或縮減。也就是常說的用malloc,calloc, realloc 等函數(shù)分配的變量空間是在堆上。當程序調用malloc等函數(shù)分配內存時,新分配的內存就被動態(tài)添加到堆上(堆被擴張);當利用free等函數(shù)釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)。

5.棧(stack) :棧又稱堆棧,是用戶存放程序臨時創(chuàng)建的局部變量,也就是說我們函數(shù)括弧“{}”中定義的變量(但不包括static聲明的變量,static意味著在數(shù)據(jù)段中存放變量)。除此以外,在函數(shù)被調用時,其參數(shù)也會被壓入發(fā)起調用的進程棧中,并且待到調用結束后,函數(shù)的返回值也會被存放回棧中。由于棧的先進先出(FIFO)特點,所以棧特別方便用來保存/恢復調用現(xiàn)場。從這個意義上講,我們可以把堆??闯梢粋€寄存、交換臨時數(shù)據(jù)的內存區(qū)。

一個程序本質上都是由 __bss段、data段、text段__三個組成的。

在C/C++程序編譯完成之后,已初始化的全局變量保存在data 段中,未初始化的全局變量保存在bss 段中。

text和data段都在可執(zhí)行文件中(在嵌入式系統(tǒng)里一般是固化在鏡像文件中),由系統(tǒng)從可執(zhí)行文件中加載;而bss段不在可執(zhí)行文件中,由系統(tǒng)初始化。

3 STM32程序的存儲分配

3.1 程序所占RAM和Flash大小分析

為例調試方便,這里使用一個裸機串口例子,關于串口的使用請參看筆者博文:

串口通信:https://bruceou.blog.csdn.net/article/details/79341769

使用GCC編譯代碼,編譯信息如下:

16825640200889qfp6m8i9a

其中:

  • text 代表執(zhí)行的代碼,程序中所有的函數(shù)都位于此處。當然還包括RO-data(Read Only)代表只讀數(shù)據(jù),程序中所定義的全局常量數(shù)據(jù)和字符串都位于此處,如const型。
  • __data__代表已初始化的讀寫數(shù)據(jù),程序中定義并且初始化的全局變量和靜態(tài)變量位于此處。
  • bss代表未初始化的讀寫數(shù)據(jù),程序中定義了但沒有初始化的全局變量和靜態(tài)變量位于此處。GCC編譯器默認是把你沒有初始化的變量都賦值為例0。即上述的bss段。

值得注意的是,這些參數(shù)的單位是Byte。

text和data兩個段需要燒錄到FLASH等非易失性器件中。

data段需要燒錄到FLASH中,而bss段則不用,但在運行時,它們都必須裝載到可讀可寫的RAM中。

因此我們可以計算出FLASH和RAM的大小:

Flash = test + data

RAM = data + bss

這就要涉及到程序的兩種狀態(tài):加載域和運行域。

[]()[]()Figure ? 程序的加載域和運行域

加載域 :向Flash中下載程序時,其實僅僅下載的是text+data的內容,意思就是說,在掉電情況下,F(xiàn)lash里面的內存僅包含text+data的內容。

運行域 :當上電后,程序運行時,首先程序會從特定的地址進行啟動,啟動時會將data的數(shù)據(jù)加載到SRAM中,單片機的test區(qū)域不需要加載到 SRAM,內核直接從 FLASH 讀取指令運行。那bss的數(shù)據(jù)怎么辦呢?對于初始值為0全局變量來說,因為要在Code區(qū)要調用該全局變量,所以肯定要對其進行描述,程序運行時就知道了,原來你是初始值為0的全局變量呀,然后就在SRAM區(qū)給你分配了一段固定區(qū)域的地址;對于局部變量來說,會自動分配大小。bss有統(tǒng)計作用,并且SRAM中一段特定的區(qū)域是運行bss數(shù)據(jù),data +bss就是程序運行總共會占用SRAM的長度,生成局部變量的棧空間包含在bss區(qū)的范圍。

3.2 程序堆棧使用分析

我們知道,程序運行需要占用的大小是RAM = data + bss,而堆棧的大小是程序開始運行后才能確定的。

那么堆和棧到底能占用多大呢,堆棧的大小是在STM32F103ZETx_FLASH.ld中設置的,這里以STM32F103ZET6為例進行分析,其內部棧的大小為1KB,堆的大小為0.5KB。

1682564020921379dug4zyh

使用objdump查看elf文件:

1682564021308yi8wwzpnm8

堆棧段起始地址為0x2000 002c,大小為0x604,這0x4又是怎么來的?這里查看map文件。

1682564021626x68xagxl0m

堆占用了0x200字節(jié),棧占用了0x400字節(jié),而剩下的0x4字節(jié)來自于 ALIGN(0x8),即8字節(jié)對齊,因為堆棧段緊跟.bss段之后,那首地址應該是0x2000 0070,但是規(guī)定了8字節(jié)對齊,所以最小為32,即需要補上4個字節(jié),所以堆棧段起始地址應該是0x2000 00F4。

【注】棧:向低地址擴展,堆:向高地址擴展。如果依次定義變量,先定義的棧變量的內存地址比后定義的棧變量的內存地址要大,先定義的堆變量的內存地址比后定義的堆變量的內存地址要小。

【Tips】

1、堆棧的大小在編譯器編譯之后是不知道的,只有運行的時候才知道,所以需要注意一點,就是別造成堆棧溢出了,不然就會發(fā)生hard fault錯誤。

2、所有在處理的函數(shù),包括函數(shù)嵌套,遞歸,等等,都是從這個“棧”里面,來分配的。所以,如果棧大小為2K,一個函數(shù)的局部變量過多,比如在函數(shù)里面定義一個char buf[512],這一下就占了1/4的棧大小了,再在其他函數(shù)里面來搞兩下,程序崩潰是很容易的事情,這時候,一般你會進入到hardfault…。

3、STM32的棧,是向下生長的。事實上,一般CPU的棧增長方向,都是向下的。而堆的生長方向,都是向上的。堆和棧,只是他們各自的起始地址和增長方向不同,他們沒有一個固定的界限,所以一旦堆棧沖突,系統(tǒng)就到了崩潰的時候了。

4、程序中的常量,如果沒加const也會編譯到SRAM里,加了const會被編譯到flash中。

3.3 實例代碼分析

前面分析了那么多,下面通過一個實例來驗證前面的分析。

main.c函數(shù)代碼如下:

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include 
#include 
/* 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 ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */
uint8_t buffer[10];//聲明了一個初始化為0的全局數(shù)組
uint8_t data = 1;//初始化的全局變量

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* 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 */
  uint8_t stack_i; //未初始化的局部變量,
  uint8_t stack_j = 1; //初始化的局部變量

  uint8_t *pHeap1 = (uint8_t *)malloc(10);//指針pHeap指向堆區(qū)分配了一個uint8_t類型10大小的空間
  uint8_t *pHeap2 = (uint8_t *)malloc(10);

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
   
  printf("First address of uninitialized global variable buffer: %p\\r\\n", buffer);
  printf("Address of initialized global variable data: %p\\r\\n", &data);
  printf("Address of uninitialized local variable stack_i: %p\\r\\n", &stack_i);
  printf("Address of uninitialized local variable stack_j: %p\\r\\n", &stack_j);
    
  printf("The first address of pHeap1 in the heap: %p\\r\\n", pHeap1);
  printf("The first address of pHeap2 in the heap: %p\\r\\n", pHeap2);

  free(pHeap1);
  free(pHeap2);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
    HAL_Delay(500);
  }
  /* USER CODE END 3 */
}

編譯后內存分配如下:

1682564021945e4xdx34h1h

運行程序,打印信息如下:

1682564022324vi2833tz0a

data是初始化的全局變量,在.data區(qū);buffer是未初始化的全局變量,在.bss區(qū);pHeap是通過malloc分配的空間,在堆區(qū),逐漸增加;局部變量都在棧區(qū),增加減小。

4 堆棧的使用總結

堆的使用:

1、堆的使用是要結合malloc函數(shù),即使用一次malloc所得到的內存空間既是屬于堆的空間。

2、堆的增長方向是向上,所以malloc申請的地址也是越來越大的,前提是連續(xù)申請且在最后一次申請后再釋放內存(free)。則第一次申請的地址永遠小于后面申請的地址。

3、堆是不連續(xù)的,由于RAM中還存在局部變量,代碼段和棧等等,所以動態(tài)分配的內存是取暫時空閑的內存,而不是預先劃出一塊區(qū)域,這就是動態(tài)分配內存的好處。

4、使用堆的壞處,由于使用malloc申請內存時,不單只申請了所需的大小空間,還要額外暫用管理這部分空間的內存,而釋放時又只釋放申請的內存,所以使用堆會引入內存碎片。當然如果不是在短時間內頻繁的使用malloc申請和free釋放內存,那么操作系統(tǒng)就有足夠的時間來回收碎片空間。

棧的使用:

1、由編譯器分配,目的是將RAM劃分處一塊區(qū)域供程序運行時的局部變量參數(shù)等使用;

2、棧是一塊連續(xù)的內存空間,由上往下增長,即使用棧時地址是會越來越小的,如先聲明的局部變量比后聲明的地址要高;

3、棧是由程序(操作系統(tǒng))自動分配,不會有內存碎片的問題;

4、棧的壞處:棧是固定且連續(xù)的一個大小,如果使用局部變量等超出了棧的大小則會造成內存溢出,而編譯器通常是發(fā)現(xiàn)不了的,只有當程序運行到那個函數(shù)時才會發(fā)生的。這就會引入很難查找的bug。另外如如果使用malloc申請的內存不規(guī)范使用,當釋放內存后,沒將指針地址清空,仍指向那個地址剛好是棧的地址,則會造成越界訪問。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 存儲器
    +關注

    關注

    38

    文章

    7484

    瀏覽量

    163760
  • GCC
    GCC
    +關注

    關注

    0

    文章

    107

    瀏覽量

    24835
  • 內存管理
    +關注

    關注

    0

    文章

    168

    瀏覽量

    14134
  • Cortex-M
    +關注

    關注

    2

    文章

    229

    瀏覽量

    29752
收藏 人收藏

    評論

    相關推薦

    深入理解Android

    深入理解Android
    發(fā)表于 08-20 15:30

    深入理解和實現(xiàn)RTOS_連載

    ,那里有更詳細的注釋,而且是全中文的。深入理解和實現(xiàn)RTOS_連載5_多任務機制應用演示本節(jié)我們通過在評估板上的實例來演示有關線程調度和管理的API。因為每個例子中都包括了線程的創(chuàng)建,所以這里就不單獨再介紹如何創(chuàng)建線程。示例程序盡量設計的簡單,主要依靠評估板上的LED 燈
    發(fā)表于 05-29 11:20

    深入理解和實現(xiàn)RTOS_連載

    和trcohili的帖子。trochili rtos完全是作者興趣所在,且行且堅持,比沒有duo。深入理解和實現(xiàn)RTOS_連載1_RTOS的前生今世今天發(fā)布的是第一篇,"RTOS的前生今世"
    發(fā)表于 05-30 01:02

    深入理解LINUX內存管理》學習筆記

    深入理解LINUX內存管理》學習筆記1
    發(fā)表于 11-07 10:20

    Cortex-M內核的GCC編譯器

    下載ARM官方對應Cortex-M內核的GCC編譯器
    發(fā)表于 08-24 06:44

    對棧的深入理解

    為什么要深入理解棧?做C語言開發(fā)如果棧設置不合理或者使用不對,棧就會溢出,溢出就會遇到無法預測亂飛現(xiàn)象。所以對棧的深入理解是非常重要的。注:動畫如果看不清楚可以電腦看更清晰啥是棧先來看一段動畫:沒有
    發(fā)表于 02-15 07:01

    為什么要深入理解

    [導讀] 從這篇文章開始,將會不定期更新關于嵌入式C語言編程相關的個人認為比較重要的知識點,或者踩過的坑。為什么要深入理解棧?做C語言開發(fā)如果棧設置不合理或者使用不對,棧就會溢出,溢出就會遇到無法
    發(fā)表于 02-15 06:09

    深入理解Linux虛擬內存管理_愛爾蘭/戈爾曼著

    電子發(fā)燒友網(wǎng)站提供《深入理解Linux虛擬內存管理_愛爾蘭/戈爾曼著.txt》資料免費下載
    發(fā)表于 02-09 15:19 ?0次下載

    深入理解Android》文前

    深入理解Android》文前
    發(fā)表于 03-19 11:23 ?0次下載

    深入理解Android:卷I》

    深入理解Android:卷I》
    發(fā)表于 03-19 11:23 ?0次下載

    深入理解Android網(wǎng)絡編程

    深入理解Android網(wǎng)絡編程
    發(fā)表于 03-19 11:26 ?1次下載

    深入理解C指針(C/C++程序員進階必備,透徹理解指針與內存管理)pdf

    深入理解C指針
    發(fā)表于 03-21 09:42 ?118次下載

    Cortex-M內核的MPU內存保護單元

    講講Cortex-M內核的MPU內存保護單元
    的頭像 發(fā)表于 03-04 11:17 ?3673次閱讀
    <b class='flag-5'>Cortex-M</b>內核的MPU<b class='flag-5'>內存</b>保護單元

    一點理解之 CmBacktrace: ARM Cortex-M 系列 MCU 錯誤追蹤庫

    一點理解之 CmBacktrace: ARM Cortex-M 系列 MCU 錯誤追蹤庫
    發(fā)表于 11-30 19:51 ?10次下載
    一點<b class='flag-5'>理解</b>之 CmBacktrace: ARM <b class='flag-5'>Cortex-M</b> 系列 MCU 錯誤追蹤庫

    深入理解Cortex-M內存管理(Keil)

    在討論Cortex-M內存之前,先來看看Cortex-M的存儲器系統(tǒng),我們知道,Cortex-M系列的處理器,大都可以對32的存儲器進行尋址,因此存儲器的尋址空間能夠達到4G,這就意
    的頭像 發(fā)表于 04-27 10:41 ?5221次閱讀
    <b class='flag-5'>深入理解</b><b class='flag-5'>Cortex-M</b><b class='flag-5'>內存</b><b class='flag-5'>管理</b>(Keil)
    RM新时代网站-首页