引言
很多人都听说过 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 了。
auto access = OA_Read | OA_Write;
auto gpio = g_ObjectMgr->GetDirectory(WKD_Device).Open("gpio3", access).MoveAs<GpioController>();
auto pin0 = gpio->OpenPin(, access);
pin0->SetDriveMode(GpioPinDriveMode::Output); while (true)
{
pin0->Write(GpioPinValue::Low);
g_ProcessMgr->SleepCurrentThread(1s);
pin0->Write(GpioPinValue::High);
g_ProcessMgr->SleepCurrentThread(1s);
}
程序申请读写权限打开名为 “gpio3” 的 GPIO 控制器,然后打开第 0 个 Pin,并设置该 Pin 的驱动模式为输出。
然后间隔 1s 切换输出高低电平。
这段代码隐藏了硬件具体细节,在任意具有 GPIO 的硬件上无需修改代码便可运行。
2. 读写 Flash
auto flash1 = g_ObjectMgr->GetDirectory(WKD_Device).Open("flash1", access).MoveAs<FlashStorage>();
{
gsl::span<const uint8_t> writeBuffers[] = { buffer };
flash1->Write(, { writeBuffers });
}
{
gsl::span<uint8_t> readBuffers[] = { buffer };
kassert(flash1->Read(, { readBuffers }) == std::size(buffer));
g_Logger->PutString("GD25Q128 Read:\n");
g_Logger->DumpHex(buffer, std::size(buffer));
}
这段代码功能很简单:打开 flash 之后写入数据然后读取数据。唯一需要说明的是 Chino 的 IO 部分均使用 BufferList 来读写数据,因此可以支持多个非连续内存段读写,且没有复制开销。
3. TCP Echo
auto eth0 = g_NetworkMgr->InstallNetworkDevice(g_ObjectMgr->GetDirectory(WKD_Device).Open("eth0", OA_Read | OA_Write).MoveAs<EthernetController>());
eth0->SetAsDefault();
eth0->Setup();
g_NetworkMgr->Run(); auto bindAddr = std::make_shared<IPEndPoint>(IPAddress::IPv4Any, );
auto socket = MakeObject<Socket>(AddressFamily::IPv4, SocketType::Stream, ProtocolType::Tcp);
socket->Bind(bindAddr);
socket->Listen(); auto client = socket->Accept();
while (true)
{
const uint8_t text[] = "hello\n";
gsl::span<const uint8_t> buffers[] = { {text,} }; try
{
client->Send({ buffers });
g_ProcessMgr->SleepCurrentThread(1s);
}
catch (...)
{
break;
}
}
程序打开名为 “eth0” 的以太网控制器,并注册到网络子系统。
然后创建一个 Tcp Socket,并监听 80 端口。
接受一个客户端连接并循环输出 hello,直到连接被关闭则退出循环。
系列说明
本文作为 Chino 操作系统开发日志的第一篇,简单概述了 Chino 目前的开发进度和基本的编程模型。今后的文章会详细介绍各个子系统的设计和开发的最新进展。
欢迎大家提出批评和建议,帮助 Chino 发展得更好。
最后再次附上 Chino 项目地址:https://github.com/chino-os/chino-os,希望大家多 star,多提 issue。