當前位置:才華齋>計算機>C語言>

C語言如何呼叫硬體

C語言 閱讀(1.17W)

大家都知道我們可以使用C語言寫一段程式來控制硬體工作,但你知道其工作原理嗎?本文特意為大家收集整理了C語言是如何呼叫硬體,希望大家喜歡!

C語言如何呼叫硬體

c語言在實際執行中,都是以彙編指令的方式執行的,由編譯器把C語言編譯成彙編指令,CPU直接執行彙編指令。

所以這個問題就變成,彙編指令是如何操作硬體的?

如果把硬體平臺限制在x86環境下,那麼彙編指令操作硬體基本上只有兩種方式:

  方式一:

通過向記憶體空間寫資料。硬體會把硬體上的各種暫存器(外行可以理解為訪問硬體的介面或者操作硬體的工具)對映到某一塊記憶體地址空間上,之後只要用匯編指令,甚至C語言去讀寫這一段記憶體地址空間(並非真正操作實體記憶體),就可以達到操作硬體的目的了。

如果題主還有WindowsXP環境(虛擬機器也可以),就可以用匯編指令直接操作視訊記憶體:

MOV AX,B800

MOV ES,AX

XOR DI,DI

MOV CX,0800

MOV AX,5555

REPZ STOSB

硬體的各種暫存器會被對映到某一塊實體記憶體中,這種方式稱為MMIO,在Windows的裝置管理器裡,右鍵點裝置,看屬性-》資源裡,不少硬體裝置都有“記憶體範圍”的引數,這裡的記憶體範圍就表示這個硬體的資源可以通過訪問這一段記憶體來控制它。

  方式二:

x86彙編中,還有兩個特殊的指令是IN和OUT,這是x86平臺才獨有的,上面圖裡的I/O範圍,就是用IN/OUT這兩個指令來訪問和控制的。

以上兩種訪問硬體的方式,第一種是可以用C語言實現的,上面一段彙編,本質上類似於C語言程式碼:

char ptr = 0xB8000;

int i;

for (i = 0; i 《0x800; i++)

{ptr + i = 0x55;

}

第二種IN/OUT方式沒有直接的C語言語法對應,需要自己封裝彙編。

那麼為什麼平時很難用C語言操作硬體呢?這是因為平時寫的程式碼大多數都在保護模式下,保護模式下,直接訪問實體地址會受到限制,C語言操作的地址都是虛地址。

對於Windows來說,要訪問實體地址,需要工作在核心模式,也就是的寫驅動才行。

而在視訊記憶體方面,首先,題主要先明白實體地址和虛擬地址的概念。

原來的8086cpu設計的時候,地址空間有一塊區域(640K-1M)之間,有一塊作為視訊記憶體使用

這裡你說的預留的地址,是指實體地址,這一段地址的準確範圍是000A0000-000BFFFF,不管是32位還是64位CPU,這一段實體記憶體地址一直都保留給視訊記憶體使用,不區分32位還是64位,也不區分保護模式還是真實模式。

可見這一段記憶體至今仍然是留給顯示卡使用的。

那麼現在為什麼不能直接用這段記憶體了?

因為現在的軟體都執行在保護模式下,訪問的地址都是虛擬地址,而並非實體地址,包括你使用cmd命令開啟的環境,都是虛擬地址,雖然32位XP裡能用debug命令向000B8000上寫資料並能顯示在cmd的介面裡,但本質上,這都是虛擬出來的。

如果要想用這段視訊記憶體怎麼辦?

自己寫一個簡易的作業系統,不啟動顯示卡的各種圖形加速功能,CPU進入保護模式後在GDT裡對映一個4G的資料段,與實體地址一致,那麼向000B8000上寫資料,就會像過去DOS一樣顯示在螢幕上,所以保護模式下也可以訪問這一段記憶體。所以,保護模式下,也可以用它。

顯示卡那麼多視訊記憶體是怎麼對映的?

有很多記憶體地址被對映給視訊記憶體了,就是通過這種對映關係,把一些實體地址留給視訊記憶體,使得CPU能像訪問記憶體一樣訪問視訊記憶體資源。

當然,實際情況是,2G視訊記憶體未必完全對映,而是隻對映一部分地址,顯示卡有一些開放的暫存器能夠控制哪部分視訊記憶體對映過來,這樣就能使得CPU在使用比較少的實體地址範圍的情況下,訪問全部的視訊記憶體。

還有一個很有意思的事情:在虛擬機器裡,找到對映的高地址部分的第一塊記憶體區域,寫一個能直接訪問實體地址的程式(比如一個驅動),去讀這一塊記憶體,然後寫到檔案裡,再用螢幕截圖,也寫到檔案裡,會發現截圖的內容和視訊記憶體裡讀出來的內容基本上是一樣的。

要回答你的'問題,我們需要要知道:

硬體是一種什麼樣的存在

什麼是驅動。

C語言怎麼操作硬體

我就不嚴格去定義這些概念了,我就以一個例子來通俗地講解一下吧。

首先講硬體:

先介紹一款微控制器晶片STM8。

這款晶片裡面有cpu, 記憶體,暫存器(先不要覺得看到新名詞壓力大,繼續往下看)等等,相當於我們的電腦了,但還要外接其它硬體。

這裡你需要知道的概念是:

晶片的引腳跟暫存器是相對應的,暫存器是8位的記憶體單元(對,存在於記憶體上面),當你往這個記憶體單元裡面寫入資料時,晶片的引腳的電壓會發生變化,比如說我寫入的是01100001,則晶片上與之對應的8個引腳的電壓狀態(分為高電平與低電平兩種)會輸出:低高高低低低低高。

cpu可以執行程式碼指令,指令可以操作記憶體。

結論:所以從上面兩點可以我們可以知道,cpu可以執行指令,使晶片的引腳電平(電壓)發生變化。

關於這款顯示器,我們需要知道的是:

它是有引腳的,這些引腳可以跟到前面介紹的那款微控制器晶片的引腳相連。

該顯示器有自帶的記憶體,用於儲存要顯示的字元,顯示器從該記憶體裡面讀取字元來來顯示。

微控制器晶片與該顯示器相連後,可以通過引腳往該顯示器的記憶體裡寫資料(通過多個引腳電平的高低不同來代表不同的資料,比如說:低高高低低低低高 代表01100001,這個資料寫在顯示器的記憶體裡面,被顯示器所顯示,當然,會根據ASCII來顯示數字對應的字元,01100001對應的字元是‘a’),除了接收資料的引腳外,還有控制顯示器的引腳(這個我們會在驅動那裡介紹,繼續往下看)。

結論:微控制器晶片與顯示器相連,可以通過引腳輸出的電平來控制顯示器的字元顯示。

那麼,綜合上面,也就是說,微控制器晶片cpu可以通過執行指令來控制顯示器的字元顯示。

而這裡,題主所說的硬體,指的就是這個顯示器了。

接下來講驅動:

那麼,什麼是驅動呢?驅動無非就是硬體跟軟體的中間層,但我們不糾結這種關係,直接來看一下,對於我們這個例子,驅動指的是什麼。首先我們要知道:

顯示器支援很多種操作,比如說清除顯示,游標移動,讀取資料,寫資料等等。

這些操作資料引腳和控制引腳來實現。

引腳可以通過微控制器晶片來控制。

結論:我們可以通過在微控制器晶片裡面寫顯示器的“驅動”程式來遮蔽掉硬體(顯示器硬體)層。

於是這裡驅動程式,指的是顯示器所支援操作的程式表示。比如說清除顯示,我們可以編寫一個clear()函式,游標移動,我們編寫一個move_cursor()函式,讀取資料和寫資料分別為read()和write(),然後分別實現就可以了(通過向暫存器裡寫資料的形式,進而控制引腳的電平變化,再而控制顯示器,這個過程前面已有介紹)。這些函式就是驅動程式了。為什麼上面說驅動程式可以遮蔽掉硬體呢?因為程式設計師可以使用前面的驅動程式來直接操作顯示器(硬體),而不用知道太多關於硬體的事情,而一般的驅動程式也可以由廠家來提供。

再說明一點:一般這些驅動程式可以用匯編寫(出於執行效率的考慮),也可以用C語言來編寫的,比如說我上面的例子,就可以直接用C語言來編寫。當然C語言內聯彙編的形式也可以。

最後講C語言怎麼操作硬體:

相信到這裡,C語言是怎麼操作硬體的已經比較明白了。

這裡總結一下:

C語言由CPU執行(實際上是先編譯成機器碼存在晶片裡面然後執行),可以去操作記憶體。

記憶體裡有一段是跟暫存器相對應的,而暫存器是跟晶片的引腳相對應的,於是操作該段記憶體就能控制晶片引腳的電壓變化。

硬體(比如說顯示器)有引腳(或者說排線,這些也是一樣的東西),這些引腳跟晶片的引腳相連可以接受晶片的控制。

可以把對某個硬體的操作做成一系列操作函式,這些操作函式就是驅動程式了。

於是我們的C語言只要去呼叫這個驅動程式就可以直接操作硬體了。(當然驅動程式也可以由C語言來編寫,所以C語言操作硬體並不一定要經過驅動程式)。

北極已經說的很到位了。我補充一些知識點:

1 語言層面上,C能直接操作的“硬體”只有記憶體地址。雖然C支援register關鍵字,但是不能指定某個特定的暫存器,所以只有記憶體地址。而C中操作記憶體地址的方式就是指標。例如:

char p = 。..;p = 。..;

2 根據1反推,可以明白如果要開放給C來操作某個硬體,最直接的方案就是設計硬體的時候預先分配好一些固定的地址的用途,然後實際專案中往這些固定地址寫入合法的資料。這樣就可以通過類似

uint32_t p = SCREEN_ADDR;p = RGBA(0xff,0xff,0xff,0xff);

這樣的程式碼來實現對硬體的操作了。

3 那這個地址怎麼拿到呢?什麼樣的資料才是合法的呢?要解答這些問題,就需要查閱具體裝置的spec了。例如這個一眼看過去就能的明白的例子(一眼沒看明白請反覆閱讀以完全理解上面第二點內容):

PS:x86架構的程式碼不能這麼寫,原因見北極的回答。

我們是用電腦的鍵盤來輸入的指令,每一個指令都對應一個ASCII碼,而這裡的ASCII碼就是有序的電壓的高低(或電流的有無,下面只提電壓的高低),即我們輸入的是電壓的高低,你所看到程式碼是這些電壓的高低控制顯示器所顯示的影象,其實電腦也不知道它是什麼,只知道這樣顯示。

結論:程式碼其實就是儲存在儲存器(記憶體、硬碟或者快閃記憶體等等)中有序的電壓的高低。

再說編譯:

編譯是一個有序的電壓的高低向另一種有序的電壓高低的一種轉換過程,下面以52微控制器為例,我們編譯是從表示ASCII碼的那種有序電壓高低轉換為52微控制器能夠識別的另一種規定好的有序電壓高低,即表示HEX檔案的電壓高低。

結論:編譯出的結果還是電腦中儲存的有序電壓高低。

到微控制器燒錄:

接下倆就是燒錄,理解了上面兩點就很容易理解下面的內容,燒錄就是電腦中的有序電壓高低通過資料線傳輸到微控制器中的ROM中。

接下來ROM就可以釋放其中的電壓來控制外圍的電路。

總結:從程式碼的編輯到最後對電路的控制都是電壓在起作用,只是為了方面我們而給我們展現的形式不一樣而已,而其本質都是電壓,這樣也就不存在轉換。

理解這句話:世界上沒有軟體,軟體只是對硬體的一種反映,就像意識是對世界的一種反映是一樣的!

相信這樣就很容易理解了。

看到有人贊同了我的觀點,很開心,針對題目我再補充一點:

只要你提到0/1,提到軟體,這個問題就沒法理解。..因為軟體【包括0/1】和硬體始終存在一道無法跨越的鴻溝;

你說你在微控制器中寫0,請問你是如何寫0的?在鍵盤上敲個0?實際還是電平【和我們理解的數字沒關係】,那個0只是你在電腦顯示器上電平的呈現形式,那個所謂的0【實質是電平】可以傳輸到微控制器中的ROM中,電平控制電平沒什麼疑問吧,這樣就輸出低電平了。