轉(zhuǎn)自 | 老吳嵌入式
今天要介紹的開源軟件叫 c-periphery,一個(gè)用 C 語言編寫的硬件外設(shè)訪問庫。
我們可以用它來讀寫 Serial、SPI、I2C 等,非常適合在嵌入式產(chǎn)品上使用。
我們可以基于它優(yōu)秀的代碼框架,不斷地?cái)U(kuò)展出更多的功能模塊,最終形成自己產(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 行代碼,每個(gè)硬件模塊的代碼都是相對(duì)獨(dú)立,上手難度小。
能收獲什么?
1、降低硬件編程的門檻;
2、了解 Linux 應(yīng)用層如何訪問 GPIO / I2C / SPI / PWM 等硬件;
3、了解如何對(duì)硬件外設(shè)進(jìn)行封裝,并提供良好的 API;
4、了解如何將代碼封裝成庫;
5、了解如何為代碼編寫單元測(cè)試程序;
c-periphery 很好地示范了如何在 Linux 平臺(tái)上進(jìn)行硬件編程,定義出來的接口即豐富又實(shí)用。
另外,它最終輸出的是靜態(tài)庫 libperiphery.a,并且為每一個(gè)硬件模塊功能都編寫了單元測(cè)試代碼,代碼質(zhì)量有保障。
c-periphery 的用法
簡(jiǎn)單例子
我們以最常見的串口讀寫為例:
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 是對(duì)串口設(shè)備的抽象;
serial_new() 用于創(chuàng)建一個(gè)串口設(shè)備, 這里只是申請(qǐng)了數(shù)據(jù),使用完畢后, 要通過 serial_free() 將其釋放掉。
serial_open() 用于初始化串口,設(shè)置設(shè)備節(jié)點(diǎn)、波特率等; 相應(yīng)地,用 serial_close() 可以關(guān)閉串口。
serial_write() 用于給串口發(fā)數(shù)據(jù),模仿了系統(tǒng)調(diào)用 write()。
serial_read() 用于從串口讀數(shù)據(jù),比系統(tǒng)調(diào)用 read() 多了一個(gè) timeout_ms 的參數(shù),有了超時(shí)機(jī)制后,至少可以避免程序一直阻塞。
這就是一個(gè)最簡(jiǎn)單的基于 c-periphery 的串口示例。即便是嵌入式初學(xué)者,基于這些接口,也能輕松地讀寫串口了。
另外,這里只用到了最常用的幾個(gè) API。對(duì)于串口模塊,c-periphery 還有很多實(shí)用的 API:
比較有意思的幾個(gè) API:
serial_poll() 類似 select(),用于監(jiān)控串口是否有數(shù)據(jù),避免死等;
serial_get/set_xxx() 用于讀寫串口的屬性;
serial_fd() 用于獲取文件描述符,有了 fd 就意味這所有 Linux 應(yīng)用編程的機(jī)制都可以使用了。例如我們可以將這個(gè) fd 傳遞給 libev,然后就能進(jìn)行事件驅(qū)動(dòng)編程了。
c-periphery 的實(shí)現(xiàn)
關(guān)鍵數(shù)據(jù)
c-periphery 里對(duì)每個(gè)硬件模塊封裝的方法都是類似,用一個(gè)結(jié)構(gòu)體來保存模塊所有相關(guān)的信息,看下面這幾個(gè)例子。
Serial:
I2C:
GPIO:
它們的成員變量大多都有文件描述符 fd、用于記錄錯(cuò)誤狀態(tài)的 errno / error string,然后再加上一些硬件模塊特有的成員變量。
最終庫的調(diào)用者只會(huì)看到 serial_t、i2c_t、gpio_t 這種類似描述符的數(shù)據(jù)類型,使用時(shí)不需要關(guān)心內(nèi)部細(xì)節(jié)。
后續(xù)我們要添加自己的硬件模塊時(shí),可以依葫蘆畫瓢,模仿著定義出屬于該硬件的 xxx_t 結(jié)構(gòu)體,然后一步步地為 c-periphery 擴(kuò)展出新的功能模塊。
幾個(gè)關(guān)鍵 API 的實(shí)現(xiàn)
我們以 Serial 為例,看下其核心 API 的實(shí)現(xiàn)。
分配與釋放:
就是在申請(qǐng)分配和釋放 serial_t 的內(nèi)存。
寫數(shù)據(jù) serial_write() 就是調(diào)用 write(),讀數(shù)據(jù) serial_read() 則是利用 select() 實(shí)現(xiàn)了超時(shí)的功能:
serial_poll() 則是使用 poll() 來完成 io 監(jiān)控。
其他硬件模塊的實(shí)現(xiàn)都是類似的。
到此,c-periphery 的核心實(shí)現(xiàn)代碼就拆解完畢了。
為 c-periphery 添加新的硬件模塊
學(xué)以致用,我們按照 c-periphery 的框架,添加背光 Backlight 功能。
Backlight 的控制方法可以參考這篇文章:一個(gè)控制背光的命令行小工具。
先定義 backlight_t:
然后再實(shí)現(xiàn)好下面這些 API:
API 的具體實(shí)現(xiàn)代碼就不再這里展示了,因?yàn)榭刂票彻鉄o非就是讀寫 /sys/class/backlight/ 內(nèi)的文件節(jié)點(diǎn),難度不大。
總結(jié)
c-periphery 是一個(gè) C 語言編寫的硬件訪問庫,已支持 Serial、I2C、SPI、MMIO、PWM、GPIO 等硬件。約 4500 行代碼,每個(gè)硬件模塊的代碼都是相對(duì)獨(dú)立,上手難度小,非常使用在嵌入式 Linux 平臺(tái)上使用。
另外,我們可以基于它優(yōu)秀的代碼框架,不斷地?cái)U(kuò)展出自己需要的功能模塊,最終形成自己產(chǎn)品專用的 Linux 硬件抽象層,絕對(duì)的嵌入式開發(fā)的利器。
審核編輯:湯梓紅
-
嵌入式
+關(guān)注
關(guān)注
5082文章
19104瀏覽量
304796 -
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209323 -
SPI
+關(guān)注
關(guān)注
17文章
1706瀏覽量
91502 -
C語言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136683 -
開源
+關(guān)注
關(guān)注
3文章
3309瀏覽量
42471
原文標(biāo)題:嵌入式開發(fā)神器:硬件外設(shè)訪問庫
文章出處:【微信號(hào):strongerHuang,微信公眾號(hào):strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論