引言 很多人都聽說過 IoT (物聯網)這個詞,越來越多的人在裝修時開始選擇智能家居,很多人也購買智能音箱做智能家居控制,想必未來一定是 AI + 物聯網的時代。 一種技術要發展並走向成熟必須要降低門檻,提高迭代速度。傳統的嵌入式開發太碎片化,很多時候還在使用代碼複製、粘貼、修改的開發方法。如果不提 ...
引言
很多人都聽說過 IoT (物聯網)這個詞,越來越多的人在裝修時開始選擇智能家居,很多人也購買智能音箱做智能家居控制,想必未來一定是 AI + 物聯網的時代。
一種技術要發展並走向成熟必須要降低門檻,提高迭代速度。傳統的嵌入式開發太碎片化,很多時候還在使用代碼複製、粘貼、修改的開發方法。如果不提供一個開箱即用的開發平臺,讓磚瓦能一層層疊上去,是很難開發更高級的應用的。
因此需要一個這樣的平臺:
- 提供足夠的硬體抽象能力
- 提供友好的編程介面和調試介面
同時結合 IoT 的特點還要:
- 靈活控制非標準的定製硬體
- 適應有限資源的運行設備
因此 IoT 操作系統應運而生。
目前很多公司已經開始佈局 IoT 操作系統,如 RT-Thread,華為的 LiteOS 等。
Chino OS
Chino OS 由包括我在內的開發團隊設計開發的一款開源 IoT 操作系統。GitHub 地址:https://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 try 18 { 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 項目地址:https://github.com/chino-os/chino-os,希望大家多 star,多提 issue。