引言
很多人都聽說過 IoT (物聯網)這個詞,越來越多的人在裝修時開始選擇智能家居,很多人也購買智能音箱做智能家居控制,想必未來一定是 AI + 物聯網的時代。
一種技術要發展並走向成熟必須要降低門檻,提高迭代速度。傳統的嵌入式開發太片段化,很多時候還在使用代碼複製、粘貼、修改的開發方法。如果不提供一個開箱即用的開發平台,讓磚瓦能一層層疊上去,是很難開發更進階的應用的。
因此需要一個這樣的平台:
- 提供足夠的硬體抽象能力
- 提供友好的編程介面和調試介面
同時結合 IoT 的特點還要:
因此 IoT 作業系統應運而生。
目前很多公司已經開始布局 IoT 作業系統,如 RT-Thread,華為的 LiteOS 等。
Chino OS
Chino OS 由包括我在內的Team Dev設計開發的一款開源 IoT 作業系統。GitHub 地址:github.com/chino-os/chino-os
Chino 主要使用 C++ 語言編寫,配有專用的 GNU 工具鏈和 C 運行庫,使用 DeviceTree 描述硬體設定。目前系統具有以下特性:
- 多任務 (6 個優先順序, Round robin 調度)
- 線程同步 (Mutex, Recurisve Mutex, Semaphore, Event)
- 處理序間通訊 (Mailslot)
- 統一的驅動模型
- 網路支援 (基於 LwIP 的 Socket API)
Chino 預定義了一組驅動介面,為應用開發人員提供了一個統一的 API 層,使得開發人員的大部分需求不用關心具體硬體差異。部分裝置抽象如下表:
| 類別 |
子類 |
樣本驅動 |
| 中斷控制器 |
|
cortex-m3, nvic |
| IO / 匯流排 |
GPIO |
stm32f10x, gpio |
| I²C |
stm32f10x, i2c |
| SPI |
stm32f10x, spi |
| 串列 |
stm32f10x, uart |
| SDIO |
stm32f10x, sdio |
| 儲存 |
EEPROM |
AT24C02 |
| Flash |
GD25Q128 |
| SD 記憶卡 |
SD V2.0 大型存放區 |
| 顯示 |
TFT LCD |
ILI9486L |
| 網路 |
乙太網路 |
ENC28J60 |
| 感應器 |
加速度計 |
ADXL345 |
Chino 目前支援 x86_64、Cortex-M3 架構,可運行在 PC 和 STM32 開發板上。未來計劃支援 ESP32、RISCV32/64 等更多架構。
Chino 在 STM32F103 開發板上的運行
程式碼範例1. GPIO 亮小燈
亮小燈可以說是嵌入式開發的 Hello World 了。
1 auto access = OA_Read | OA_Write; 2 auto gpio = g_ObjectMgr->GetDirectory(WKD_Device).Open("gpio3", access).MoveAs<GpioController>(); 3 auto pin0 = gpio->OpenPin(0, access); 4 pin0->SetDriveMode(GpioPinDriveMode::Output); 5 6 while (true) 7 { 8 pin0->Write(GpioPinValue::Low); 9 g_ProcessMgr->SleepCurrentThread(1s);10 pin0->Write(GpioPinValue::High);11 g_ProcessMgr->SleepCurrentThread(1s);12 }
程式申請讀寫權限開啟名為 “gpio3” 的 GPIO 控制器,然後開啟第 0 個 Pin,並設定該 Pin 的驅動模式為輸出。
然後間隔 1s 切換輸出高低電平。
這段程式碼後置了硬體具體細節,在任意具有 GPIO 的硬體上無需修改代碼便可運行。
2. 讀寫 Flash
1 auto flash1 = g_ObjectMgr->GetDirectory(WKD_Device).Open("flash1", access).MoveAs<FlashStorage>(); 2 { 3 gsl::span<const uint8_t> writeBuffers[] = { buffer }; 4 flash1->Write(0, { writeBuffers }); 5 } 6 { 7 gsl::span<uint8_t> readBuffers[] = { buffer }; 8 kassert(flash1->Read(0, { readBuffers }) == std::size(buffer)); 9 g_Logger->PutString("GD25Q128 Read:\n");10 g_Logger->DumpHex(buffer, std::size(buffer));11 }
這段代碼功能很簡單:開啟 flash 之後寫入資料然後讀取資料。唯一需要說明的是 Chino 的 IO 部分均使用 BufferList 來讀寫資料,因此可以支援多個非連續記憶體段讀寫,且沒有複製開銷。
3. TCP Echo
1 auto eth0 = g_NetworkMgr->InstallNetworkDevice(g_ObjectMgr->GetDirectory(WKD_Device).Open("eth0", OA_Read | OA_Write).MoveAs<EthernetController>()); 2 eth0->SetAsDefault(); 3 eth0->Setup(); 4 g_NetworkMgr->Run(); 5 6 auto bindAddr = std::make_shared<IPEndPoint>(IPAddress::IPv4Any, 80); 7 auto socket = MakeObject<Socket>(AddressFamily::IPv4, SocketType::Stream, ProtocolType::Tcp); 8 socket->Bind(bindAddr); 9 socket->Listen(1);10 11 auto client = socket->Accept();12 while (true)13 {14 const uint8_t text[] = "hello\n";15 gsl::span<const uint8_t> buffers[] = { {text,6} };16 17 try18 {19 client->Send({ buffers });20 g_ProcessMgr->SleepCurrentThread(1s);21 }22 catch (...)23 {24 break;25 }26 }
程式開啟名為 “eth0” 的乙太網路控制器,並註冊到網路子系統。
然後建立一個 Tcp Socket,並監聽 80 連接埠。
接受一個用戶端串連並迴圈輸出 hello,直到串連被關閉則退出迴圈。
系列說明
本文作為 Chino 作業系統開發日誌的第一篇,簡單概述了 Chino 目前的開發進度和基本的編程模型。今後的文章會詳細介紹各個子系統的設計和開發的最新進展。
歡迎大家提出批評和建議,協助 Chino 發展得更好。
最後再次附上 Chino 項目地址:github.com/chino-os/chino-os,希望大家多 star,多提 issue。