Chino 操作系统开发日志 (1) - 为 IoT 而生

时间:2024-01-14 20:35:32

引言

很多人都听说过 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 操作系统开发日志 (1) - 为 IoT 而生

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。