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


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

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

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

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

    我们可以基于它优秀的代码框架,不断地扩展出更多的功能模块,最终形成自己产品适用的 Linux 硬件抽象层。


    源文件:

    1. $ tree .
    2. ├── src
    3. │   ├── gpio.c
    4. │   ├── gpio.h
    5. │   ├── i2c.c
    6. │   ├── i2c.h
    7. │   ├── led.c
    8. │   ├── led.h
    9. │   ├── mmio.c
    10. │   ├── mmio.h
    11. │   ├── pwm.c
    12. │   ├── pwm.h
    13. │   ├── serial.c
    14. │   ├── serial.h
    15. │   ├── spi.c
    16. │   ├── spi.h
    17. │   ├── version.c
    18. │   └── version.h

    约 4500 行代码,每个硬件模块的代码都是相对独立,上手难度小。
     

    能收获什么?
    1、降低硬件编程的门槛;

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

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

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

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

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

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

    c-periphery 的用法

    简单例子

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

    1. int main(void)
    2. {
    3.     serial_t *serial;
    4.     uint8_t s[] = "Hello World!";
    5.     uint8_t buf[128];
    6.     int ret;
    7.     serial = serial_new();
    8.     /* Open /dev/ttyUSB0 with baudrate 115200and defaults of 8N1no flow control */
    9.     if (serial_open(serial, "/dev/ttyUSB0"115200< 0) {
    10.         fprintf(stderr, "serial_open(): %s\n", serial_errmsg(serial));
    11.         exit(1);
    12.     }
    13.     /* Write to the serial port */
    14.     if (serial_write(serial, s, sizeof(s)) < 0) {
    15.         fprintf(stderr, "serial_write(): %s\n", serial_errmsg(serial));
    16.         exit(1);
    17.     }
    18.     /* Read up to buf size or 2000ms timeout */
    19.     if ((ret = serial_read(serial, buf, sizeof(buf), 2000)) < 0) {
    20.         fprintf(stderr, "serial_read(): %s\n", serial_errmsg(serial));
    21.         exit(1);
    22.     }
    23.     printf("read %d bytes: _%s_\n", ret, buf);
    24.     serial_close(serial);
    25.     serial_free(serial);
    26.     return 0;
    27. }

    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 硬件抽象层,绝对的嵌入式开发的利器。

  • 相关阅读:
    CSS @符规则(@font-face、@keyframes、@media、@scope等)
    第十一章《Java实战常用类》第6节:Object类
    《深度学习》深度学习 框架、流程解析、动态展示及推导
    优雅的springboot参数校验(二)
    计算机毕业设计Java咖啡销售平台(源码+mysql数据库+系统+lw文档)
    linux内核定时器的使用
    FastRCNN
    内卷下的智能投影行业,未来何去何从?
    在Python中识别时间,并根据时间来做出相应的回应
    [MATLAB]:基础知识学习
  • 原文地址:https://blog.csdn.net/weixin_41114301/article/details/133961648