嵌入式硬件库的基本操作方式与分析

简介: 嵌入式硬件库的基本操作方式与分析

本次要介绍的开源软件是 c-periphery:

https://github.com/vsergeev/c-periphery

一个用 C 语言编写的硬件外设访问库。

我们可以用它来读写 Serial、SPI、I2C 等,非常适合在嵌入式产品上使用。

我们可以基于它优秀的代码框架,不断地扩展出更多的功能模块,最终形成自己产品适用的 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 行代码,每个硬件模块的代码都是相对独立,上手难度小。

 

能收获什么?

1、降低硬件编程的门槛;

2、了解 Linux 应用层如何访问 GPIO / I2C / SPI / PWM 等硬件;

3、了解如何对硬件外设进行封装,并提供良好的 API;

4、了解如何将代码封装成库;

5、了解如何为代码编写单元测试程序;

c-periphery 很好地示范了如何在 Linux 平台上进行硬件编程,定义出来的接口即丰富又实用。

另外,它最终输出的是静态库 libperiphery.a,并且为每一个硬件模块功能都编写了单元测试代码,代码质量有保障。

 

c-periphery 的用法

简单例子

我们以最常见的串口读写为例:

int main(void)
{
    serial_t *serial;
    uint8_t s[] = "Hello World!";
    uint8_t buf[128];
    int ret;
    serial = serial_new();
    /* Open /dev/ttyUSB0 with baudrate 115200, and defaults of 8N1, no flow control */
    if (serial_open(serial, "/dev/ttyUSB0", 115200) < 0) {
        fprintf(stderr, "serial_open(): %s\n", serial_errmsg(serial));
        exit(1);
    }
    /* Write to the serial port */
    if (serial_write(serial, s, sizeof(s)) < 0) {
        fprintf(stderr, "serial_write(): %s\n", 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\n", serial_errmsg(serial));
        exit(1);
    }
    printf("read %d bytes: _%s_\n", ret, buf);
    serial_close(serial);
    serial_free(serial);
    return 0;
}

serial_t 是对串口设备的抽象;

serial_new() 用于创建一个串口设备, 这里只是申请了数据,使用完毕后, 要通过 serial_free() 将其释放掉。

serial_open() 用于初始化串口,设置设备节点、波特率等; 相应地,用 serial_close() 可以关闭串口。

serial_write() 用于给串口发数据,模仿了系统调用 write()。

serial_read() 用于从串口读数据,比系统调用 read() 多了一个 timeout_ms 的参数,有了超时机制后,至少可以避免程序一直阻塞。

这就是一个最简单的基于 c-periphery 的串口示例。即便是嵌入式初学者,基于这些接口,也能轻松地读写串口了。

 

另外,这里只用到了最常用的几个 API。对于串口模块,c-periphery 还有很多实用的 API:

比较有意思的几个 API:

serial_poll() 类似 select(),用于监控串口是否有数据,避免死等;

serial_get/set_xxx() 用于读写串口的属性;

serial_fd() 用于获取文件描述符,有了 fd 就意味这所有 Linux 应用编程的机制都可以使用了。例如我们可以将这个 fd 传递给 libev,然后就能进行事件驱动编程了。

 

c-periphery 的实现

关键数据

c-periphery 里对每个硬件模块封装的方法都是类似,用一个结构体来保存模块所有相关的信息,看下面这几个例子。

Serial:

I2C:

GPIO:

它们的成员变量大多都有文件描述符 fd、用于记录错误状态的 errno / error string,然后再加上一些硬件模块特有的成员变量。

最终库的调用者只会看到 serial_t、i2c_t、gpio_t 这种类似描述符的数据类型,使用时不需要关心内部细节。

后续我们要添加自己的硬件模块时,可以依葫芦画瓢,模仿着定义出属于该硬件的 xxx_t 结构体,然后一步步地为 c-periphery 扩展出新的功能模块。

 

几个关键 API 的实现

我们以 Serial 为例,看下其核心 API 的实现。

分配与释放:

就是在申请分配和释放 serial_t 的内存。

写数据 serial_write() 就是调用 write(),读数据 serial_read() 则是利用 select() 实现了超时的功能:

serial_poll() 则是使用 poll() 来完成 io 监控。

其他硬件模块的实现都是类似的。

到此,c-periphery 的核心实现代码就拆解完毕了。

为 c-periphery 添加新的硬件模块

学以致用,我们按照 c-periphery 的框架,添加背光 Backlight 功能。

Backlight 的控制方法可以参考这篇文章:一个控制背光的命令行小工具

 

先定义 backlight_t:

然后再实现好下面这些 API:

API 的具体实现代码就不再这里展示了,因为控制背光无非就是读写 /sys/class/backlight/ 内的文件节点,难度不大。

总结

c-periphery 是一个 C 语言编写的硬件访问库,已支持 Serial、I2C、SPI、MMIO、PWM、GPIO 等硬件。约 4500 行代码,每个硬件模块的代码都是相对独立,上手难度小,非常使用在嵌入式 Linux 平台上使用。

另外,我们可以基于它优秀的代码框架,不断地扩展出自己需要的功能模块,最终形成自己产品专用的 Linux 硬件抽象层,绝对的嵌入式开发的利器。

目录
相关文章
|
1月前
|
数据可视化 测试技术 Linux
基于Python后端构建多种不同的系统终端界面研究
【10月更文挑战第10天】本研究探讨了利用 Python 后端技术构建多样化系统终端界面的方法,涵盖命令行界面(CLI)、图形用户界面(GUI)及 Web 界面。通过分析各种界面的特点、适用场景及关键技术,展示了如何使用 Python 标准库和第三方库(如 `argparse`、`click`、`Tkinter` 和 `PyQt`)实现高效、灵活的界面设计。旨在提升用户体验并满足不同应用场景的需求。
|
2月前
|
大数据 数据挖掘 数据处理
Python数据流转的秘密武器:文件系统操作与高效I/O管理实战
【9月更文挑战第12天】在大数据时代,Python凭借其丰富的库和简洁的语法,成为数据处理的首选工具。本文通过实战案例,介绍如何利用Python的`pathlib`模块遍历复杂文件系统,以及通过高效I/O管理(如使用`with`语句和多线程)提升文本文件处理性能。通过这些技巧,你可以轻松从大量分散的文本文件中提取关键信息并整合成新的数据集,从而更好地支持数据分析工作。掌握这些技术,将助你在数据处理领域游刃有余。
25 4
|
2月前
|
存储 Java 数据处理
深入骨髓的Python系统编程:文件系统操作与I/O管理,揭秘底层奥秘
【9月更文挑战第9天】本文通过问答形式深入探讨Python中文件系统操作与I/O管理的核心技巧,涵盖高效遍历文件系统、理解I/O缓冲机制、并行处理文件I/O以及关键异常处理方法。通过具体示例代码,展示了如何利用os和pathlib模块简化文件遍历,控制I/O缓冲,使用多进程提高处理效率,并妥善处理各种I/O异常,助你提升Python系统编程能力。
38 3
|
4月前
|
设计模式 安全 关系型数据库
PHP开发涉及一系列步骤和技术
【7月更文挑战第2天】PHP开发涉及一系列步骤和技术
137 57
|
4月前
|
数据挖掘 数据处理 Python
🔍深入Python系统编程腹地:文件系统操作与I/O管理,打造高效数据处理流水线
【7月更文挑战第29天】深入Python系统编程腹地:文件系统操作与I/O管理,打造高效数据处理流水线
38 3
|
4月前
|
存储 数据格式 运维
开发与运维C++问题之更改数据模型为通用数据结构如何解决
开发与运维C++问题之更改数据模型为通用数据结构如何解决
29 1
|
Java Maven 数据格式
设计一款可扩展和基于windows系统的一键处理表格小工具思路
设计一款可扩展和基于windows系统的一键处理表格小工具思路
54 0
|
存储 数据可视化 Ubuntu
bcftools学习笔记丨软件简介、安装方式、使用方法、核心功能、参数解释等一文速览
bcftools学习笔记丨软件简介、安装方式、使用方法、核心功能、参数解释等一文速览
|
存储 JSON NoSQL
如何使用 Tair 增强数据结构构建丰富在线实时场景
Redis 数据结构模块是 Redis 提供的一种扩展数据结构功能的方式。Redis 提供了很多内置的数据结构,但是如何通过自己想要的方式来扩展自定义数据结构? Redis 在 4.0 引入 Redis Modules, 用户可以通过调用 Redis Modules API 来实现自定义的数据结构。
1344 0
如何使用 Tair 增强数据结构构建丰富在线实时场景
|
消息中间件 canal 存储
数据的异构实战(二)手写迷你版同步工程
数据的异构实战(二)手写迷你版同步工程
159 0