轉(zhuǎn)自 | 老吳嵌入式
今天要介紹的開源軟件叫 c-periphery,一個用 C 語言編寫的硬件外設訪問庫。
我們可以用它來讀寫 Serial、SPI、I2C 等,非常適合在嵌入式產(chǎn)品上使用。
我們可以基于它優(yōu)秀的代碼框架,不斷地擴展出更多的功能模塊,最終形成自己產(chǎn)品適用的 Linux 硬件抽象層。
源文件:
$tree. ├──src │├──gpio.c │├──gpio.h │├──i2c.c │├──i2c.h │├──led.c │├──led.h │├──mmio.c │├──mmio.h │├──pwm.c │├──pwm.h │├──serial.c │├──serial.h │├──spi.c │├──spi.h │├──version.c │└──version.h
約 4500 行代碼,每個硬件模塊的代碼都是相對獨立,上手難度小。
能收獲什么?
1、降低硬件編程的門檻;
2、了解 Linux 應用層如何訪問 GPIO / I2C / SPI / PWM 等硬件;
3、了解如何對硬件外設進行封裝,并提供良好的 API;
4、了解如何將代碼封裝成庫;
5、了解如何為代碼編寫單元測試程序;
c-periphery 很好地示范了如何在 Linux 平臺上進行硬件編程,定義出來的接口即豐富又實用。
另外,它最終輸出的是靜態(tài)庫 libperiphery.a,并且為每一個硬件模塊功能都編寫了單元測試代碼,代碼質(zhì)量有保障。
c-periphery 的用法
簡單例子
我們以最常見的串口讀寫為例:
intmain(void)
{
serial_t*serial;
uint8_ts[]="HelloWorld!";
uint8_tbuf[128];
intret;
serial=serial_new();
/*Open/dev/ttyUSB0withbaudrate115200,anddefaultsof8N1,noflowcontrol*/
if(serial_open(serial,"/dev/ttyUSB0",115200)0)?{
????????fprintf(stderr,?"serial_open():?%s
",?serial_errmsg(serial));
????????exit(1);
????}
????/*?Write?to?the?serial?port?*/
????if?(serial_write(serial,?s,?sizeof(s))?0)?{
????????fprintf(stderr,?"serial_write():?%s
",?serial_errmsg(serial));
????????exit(1);
????}
????/*?Read?up?to?buf?size?or?2000ms?timeout?*/
????if?((ret?=?serial_read(serial,?buf,?sizeof(buf),?2000))?0)?{
????????fprintf(stderr,?"serial_read():?%s
",?serial_errmsg(serial));
????????exit(1);
????}
????printf("read?%d?bytes:?_%s_
",?ret,?buf);
????serial_close(serial);
????serial_free(serial);
????return?0;
}
serial_t 是對串口設備的抽象;
serial_new() 用于創(chuàng)建一個串口設備, 這里只是申請了數(shù)據(jù),使用完畢后, 要通過 serial_free() 將其釋放掉。
serial_open() 用于初始化串口,設置設備節(jié)點、波特率等; 相應地,用 serial_close() 可以關(guān)閉串口。
serial_write() 用于給串口發(fā)數(shù)據(jù),模仿了系統(tǒng)調(diào)用 write()。
serial_read() 用于從串口讀數(shù)據(jù),比系統(tǒng)調(diào)用 read() 多了一個 timeout_ms 的參數(shù),有了超時機制后,至少可以避免程序一直阻塞。
這就是一個最簡單的基于 c-periphery 的串口示例。即便是嵌入式初學者,基于這些接口,也能輕松地讀寫串口了。
另外,這里只用到了最常用的幾個 API。對于串口模塊,c-periphery 還有很多實用的 API:

比較有意思的幾個 API:
serial_poll() 類似 select(),用于監(jiān)控串口是否有數(shù)據(jù),避免死等;
serial_get/set_xxx() 用于讀寫串口的屬性;
serial_fd() 用于獲取文件描述符,有了 fd 就意味這所有 Linux 應用編程的機制都可以使用了。例如我們可以將這個 fd 傳遞給 libev,然后就能進行事件驅(qū)動編程了。
c-periphery 的實現(xiàn)
關(guān)鍵數(shù)據(jù)
c-periphery 里對每個硬件模塊封裝的方法都是類似,用一個結(jié)構(gòu)體來保存模塊所有相關(guān)的信息,看下面這幾個例子。
Serial:

I2C:

GPIO:

它們的成員變量大多都有文件描述符 fd、用于記錄錯誤狀態(tài)的 errno / error string,然后再加上一些硬件模塊特有的成員變量。
最終庫的調(diào)用者只會看到 serial_t、i2c_t、gpio_t 這種類似描述符的數(shù)據(jù)類型,使用時不需要關(guān)心內(nèi)部細節(jié)。
后續(xù)我們要添加自己的硬件模塊時,可以依葫蘆畫瓢,模仿著定義出屬于該硬件的 xxx_t 結(jié)構(gòu)體,然后一步步地為 c-periphery 擴展出新的功能模塊。
幾個關(guān)鍵 API 的實現(xiàn)
我們以 Serial 為例,看下其核心 API 的實現(xiàn)。
分配與釋放:

就是在申請分配和釋放 serial_t 的內(nèi)存。
寫數(shù)據(jù) serial_write() 就是調(diào)用 write(),讀數(shù)據(jù) serial_read() 則是利用 select() 實現(xiàn)了超時的功能:

serial_poll() 則是使用 poll() 來完成 io 監(jiān)控。

其他硬件模塊的實現(xiàn)都是類似的。
到此,c-periphery 的核心實現(xiàn)代碼就拆解完畢了。
為 c-periphery 添加新的硬件模塊
學以致用,我們按照 c-periphery 的框架,添加背光 Backlight 功能。
Backlight 的控制方法可以參考這篇文章:一個控制背光的命令行小工具。
先定義 backlight_t:

然后再實現(xiàn)好下面這些 API:

API 的具體實現(xiàn)代碼就不再這里展示了,因為控制背光無非就是讀寫 /sys/class/backlight/ 內(nèi)的文件節(jié)點,難度不大。
總結(jié)
c-periphery 是一個 C 語言編寫的硬件訪問庫,已支持 Serial、I2C、SPI、MMIO、PWM、GPIO 等硬件。約 4500 行代碼,每個硬件模塊的代碼都是相對獨立,上手難度小,非常使用在嵌入式 Linux 平臺上使用。
另外,我們可以基于它優(yōu)秀的代碼框架,不斷地擴展出自己需要的功能模塊,最終形成自己產(chǎn)品專用的 Linux 硬件抽象層,絕對的嵌入式開發(fā)的利器。
審核編輯:湯梓紅
-
嵌入式
+關(guān)注
關(guān)注
5188文章
20187瀏覽量
329406 -
Linux
+關(guān)注
關(guān)注
88文章
11635瀏覽量
218160 -
SPI
+關(guān)注
關(guān)注
17文章
1868瀏覽量
99973 -
C語言
+關(guān)注
關(guān)注
183文章
7642瀏覽量
144740 -
開源
+關(guān)注
關(guān)注
3文章
4052瀏覽量
45637
原文標題:嵌入式開發(fā)神器:硬件外設訪問庫
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
用C語言實現(xiàn)一個圣誕樹!
C語言標準庫的基本使用
C語言中使用嵌入式SQL訪問Oracle數(shù)據(jù)庫的方法
基于keil mdk 標準外設庫的軟件開發(fā)
用ASP訪問數(shù)據(jù)庫的幾種常見方式
STM32開發(fā)標準外設庫的詳細資料說明
C和C++編寫環(huán)境下LabVIEW如何調(diào)用動態(tài)庫?

介紹一個用C語言編寫的硬件外設訪問庫
評論