日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

ARM Cortex-M嵌入式C基础编程(上)

發布時間:2023/11/28 生活经验 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ARM Cortex-M嵌入式C基础编程(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

ARM Cortex-M嵌入式C基礎編程(上)

ARM Cortex-M Embedded C Fundamentals/Tutorial -Aviral Mittal

此技術是關于從編寫簡單的嵌入式C代碼到執行的過程。

這項技術試圖不使用行話,并針對任何人誰有興趣知道如何開始編寫一個嵌入式C程序或ARM Cortex-M系列處理器的匯編語言程序。

世界上充斥著信息,然而這些信息的存在方式使得所有的信息對于一個來自其他背景的人來說都是垃圾。例如,如果您來自硬件背景,則軟件信息看起來很神秘。例如,在軟件世界中,他們使用“圖像”一詞來表示從相應的C程序獲得的二進制文件。說真的,如果你不知道“圖像文件”是什么,那么jpeg或gif就會出現在你的想象中。我不知道為什么它叫“形象”。這項技術是一種試圖保持它非常簡單,而不使用’什么軟件假設每個人都知道出生’之類的行話。

這都是關于ARM架構的,所以它關注的是嵌入式c,也就是為基于ARM的微控制器編寫的c程序。

C代碼是人類可讀的,處理器無法執行它。它必須轉換成0和1,因為這是處理器可以執行的。

因此,在執行C代碼之前,必須將此C代碼轉換為二進制格式的處理器指令,然后將此二進制格式的指令放入內存中,然后處理器將開始從該內存中獲取并執行指令(請參閱軟件術語“圖像文件”的使用)

但這聽起來很簡單。在幕后發生了很多事情,很多事情都要考慮。

The very basic Embedded C program:

本節描述了一個非常簡單的嵌入式C程序,并給出了一些解釋。

讓我們考慮一個非常簡單的嵌入式C程序:

typedef unsigned long uint32_t;

int main ()

{

int ii;

for(ii=0;ii<305419896;ii++) {

*((uint32_t *)0x40E00018) = 0x87654321;

asm(“NOP”);

}

while(1){}

}

如果您之前不知道嵌入式C,那么上面的代碼已經很神秘了,但是讓我逐行解釋一下:
第一行是數據類型的定義,程序將使用。定義了一種新的數據類型,稱為“uint32”。這被定義為“長”long’是C語言中預定義的數據類型,表示32位寬的二進制數。

第二行是“main”函數調用。對每一個C程序都是必不可少的。這是用戶寫他們想做的事情的地方。

“int ii”是一個不言而喻的整數聲明,它將在“for循環”中使用。在C語言中,需要顯式聲明所有變量,然后才能使用它們。

for(ii……)同樣是不言而喻的,一個for循環被啟動,它將執行305419896次。

然后你就有了這行命令:

*((uint32_t *)0x40E00018) = 0x87654321;

看起來像是第二次世界大戰的加密密碼,用來指示某人發射魚雷!。

讓我解釋一下:

C語言使用所謂的“指針”。指針是指向內存位置的地址。“星號”或“星號”用于定義/聲明指針。

“0x”:這意味著“0x”后面的值是十六進制格式。

現在,上面的代碼行簡單地表示,用戶希望將0x8765_4321的十六進制值發送到內存位置0x40E0_0018。很簡單。

上面用大括號寫的’(unit32_t*)表示用戶打算將“0x40E0_0018”的常量值轉換為另一種類型的數據,即指針,以便它可以用作內存位置的地址。指針指向簡單的內存位置。

這里我們剛剛解釋了如何將常量數據(0x40E0_0018)轉換為另一種類型的數據,稱為指針數據類型。軟件人員稱之為“類型轉換”。也就是,把一種數據轉換成另一種數據。或者將一種類型的數據更改為另一種類型的數據。所以這里是“類型選擇”行話。

這里我們剛剛解釋了如何將常量數據(0x40E0_0018)轉換為另一種類型的數據,稱為指針數據類型。軟件人員稱之為“類型轉換”。也就是,把一種數據轉換成另一種數據。或者將一種類型的數據更改為另一種類型的數據。所以這里是“類型選擇”行話。

那么“((uint32_t)0x40E00018)=0x87654321”總體上意味著用戶現在希望將0x8765_4321的值寫入內存位置0x40E0_0018。指針的“星”表示指針指向的位置處的值。因此,在上面的行中,用戶將0x8765_4321分配給(uint32_t*)0x40E00018的“star”。記住,’ (uint32_t*)0x40E00018’是指向內存位置0x4E0_0018的指針。

asm(“NOP”)是匯編語言中的“no operation”指令,在這里使用,因為我不知道C語言中有什么替代方法。要使用C語言中的匯編指令,請使用asm(“匯編指令”)。

現在是while循環:

while (1) {}。

這段代碼將出現在大多數嵌入式C程序中。在嵌入式世界,只要處理器有電,它就可以運行。它就像一個瓶子里的金妮,它必須一直做些什么。如果處理器的電源沒有關閉,或者處理器沒有進入睡眠狀態,它將繼續執行“某些操作”。所以上面的while(1)do nothing循環的作用完全相同。

為某目標微控制器編寫了嵌入式C程序。典型的微控制器至少有微處理器、存儲器、外圍設備和時鐘源。上述C程序寫入一個內存位置該內存位置可能屬于外圍設備中的“寄存器”。

The Compile Flow:

技術的這一部分解釋編譯器/鏈接器生成的機器代碼(匯編代碼)的位。

程序集代碼的部分已顯示/描述。

本文還描述了編譯過程中的幕后操作。

好吧,但什么是“編譯”?

處理器只能執行二進制指令。編譯器將用高級語言(如C)編寫的可讀程序轉換成二進制指令。然后將這些二進制指令放入內存。處理器啟動時,從內存中獲取這些二進制指令并執行它們。將人類可讀代碼轉換為二進制代碼的過程稱為“編譯”

下面是前面介紹的簡單C程序:

typedef unsigned long
uint32_t;

int main ()

{

int ii;

for(ii=0;ii<305419896;ii++) {

*((uint32_t *)0x40E00018) = 0x87654321;

asm(“NOP”);

}

while(1){}

}

Compile it using KEIL
uVision: Click Here to go to a Very simple Quick Tutorial

編譯上述C代碼和’statup.s’文件會生成一個名為’axf’的可執行文件。axf是一個人類無法讀取的二進制文件,但是可以生成這個“axf”文件的人類可讀取版本,它將以“匯編語言”顯示指令:這是由Keil提供的名為“fromelf”實用程序完成的。Keil教程演示了如何使用這個實用程序和精確的命令語法來完成這個轉換

下面是“axf”文件的可讀版本的一節。可以看到C語言中的“main()”是如何轉換為匯編指令的。

下面截取的代碼還顯示了每個指令的地址,即在內存中存儲該段代碼的位置。例如,“main()”的第一條指令存儲在位置0x0000_0134,這是指令MOVS r0,#0。這意味著在寄存器r0中移動值“0”。

還可以注意到,上述“C”程序中的所有“常量”值都存儲在從0x0000_014C開始的完全獨立的內存位置。有3個這樣的常量,如下所示。

注意,上面的main()中的指令不是從地址0x0000_0000開始的,而是從0x0000_0134開始的。

然后,如果對“axf”文件的全文版本進行分析,它將顯示出許多情況。上面的“main”代碼只有幾行。

“axf”文件中的額外內容是什么。

“axf”文件包含許多調試信息。當代碼下載到目標設備上,并且設備仍連接到主機PC時,此調試信息有助于調試代碼。當目標代碼加載到目標本身時,代碼和調試信息都加載到開發主機PC的內存中。

當使用某些編譯時選項刪除調試信息時,axf文件將如下所示:

這又是很多代碼,這是多余的’主要’代碼。

將二進制axf文件轉換成ARM體系結構能夠執行的格式需要多余的信息。

在執行用戶“main()”之前,將調用以下函數。

__main -> this is not the user main(), but a function called at
the start of the binary executable, which calls other functions.

__scatterload__rt_entry__rt_entry in turn will call__rt_lib_initUser

Code (your code inside main)

    exit()

主程序是用戶程序的入口點。此主函數是預定義的(盡管用戶可以編寫自己的主函數)。請注意,這個main與用戶的C程序中的main()不同。如果用戶打算編寫自己的’uuu main’,他們可以使用自己的代碼和名稱。但是,用戶必須更新鏈接器的默認“–startup”選項,例如“–startup my\u main”,因為默認鏈接器選項是以下“–startup=\u main”。如果用戶愿意,也可以使用“–no_startup”。但是,這樣做的后果超出了本教程的范圍。
__ __main then calls __scatterload。

對于understand __scatterload,重要的是要進一步了解代碼如何存儲在內存中以及如何執行。

典型的微控制器系統通常有幾種類型的存儲器。例如,閃存、ROM、RAM等。

這意味著,同一代碼可能在不執行時駐留在一個內存中,然后在執行時移動到另一個內存中。例如,代碼及其數據在不執行時可以駐留在ROM中,然后將其移動到RAM中執行。

在另一個例子中,代碼可以直接從ROM執行,但是它的變量必須復制到RAM,因為這些變量可能需要由運行的代碼更新。Keil教程2展示了如何將變量的初始值存儲在只讀存儲器中,而變量本身存儲在讀寫存儲器中,然后在程序執行之前將這些變量的初始值復制到讀寫存儲器中。在Keil教程2中,可以看到C程序有兩個整數數組變量,即avar[10]和bvar[10],它們有一些初始值。avar[10]的初始值存儲在地址0x0000_015c到0x0000_0180處。這可能是ROM地址。

然而,當程序執行時,變量avar[10]和bvar[10]存儲在堆棧存儲器的某處堆棧存儲器是從0x2000_0000開始的區域中的讀/寫存儲器。在執行用戶的main()之前,這些變量的初始值已經在堆棧中可用。這意味著在執行用戶的main()之前,這些初始值是如何從區域0x0000_0xxx(只讀區域)復制到0x2000_0yyy(讀寫區域)的。

總結

以上是生活随笔為你收集整理的ARM Cortex-M嵌入式C基础编程(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。