首页 > 其他分享 >嵌入式硬件库的基本操作方式与分析

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

时间:2023-10-30 11:00:48浏览次数:41  
标签:嵌入式 硬件 API periphery 模块 串口 基本操作 serial


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

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

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

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

我们可以用它来读写 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:

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

比较有意思的几个 API:

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

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

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

c-periphery 的实现

关键数据

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

Serial:

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

I2C:

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

GPIO:

嵌入式硬件库的基本操作方式与分析_串口_05

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

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

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

几个关键 API 的实现

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

分配与释放:

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

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

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

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

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

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

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

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

为 c-periphery 添加新的硬件模块
学以致用,我们按照 c-periphery 的框架,添加背光 Backlight 功能。

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

先定义 backlight_t:

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

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

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

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

总结

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

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

标签:嵌入式,硬件,API,periphery,模块,串口,基本操作,serial
From: https://blog.51cto.com/u_11947739/8086594

相关文章

  • 嵌入式linux SD读取数据导致死机问题
    一、碰到的问题通过ssh命令将文件写入到SD卡中,发现有一张SD卡(金士顿)可以成功写入,而另一张SD(闪迪)一直写入失败。应用层读取文件时,有一张SD卡(金士顿)可以成功读取数据;另一张SD卡(闪迪)有很大的概率会导致司机。二、SD卡驱动硬件电路图1.SD卡驱动硬件电路三、调试过程查看......
  • 基于32位Cortex™-M4的STM32F446RET7、STM32F429IGH6、STM32F423VHH6嵌入式微控制器(M
    描述STM32F432位Cortex™-M4微控制器(MCU)打开了进入数字信号控制器(DSC)市场的大门。这一系列器件与STM32F2系列引脚对引脚、软件相容,但是具有更好的性能、DSP性能、更多的SRAM,并改进了外设,如全双工I²S、低于1μA的RTC、2.4MSPS的ADC。意法半导体STM32F4MCU内含......
  • 基于高性能Cortex®-M7内核STM32F765VGT7、STM32F745IET6嵌入式微控制器
    STM32F732位MCU+FPU基于高性能的ARM®Cortex-M732位RISC内核®,工作频率高达216MHz。Cortex®-M7内核具有单浮点单元(SFPU)精度,支持所有ARM®单精度数据处理指令与数据类型。同时执行全套DSP指令和存储保护单元(MPU),增强应用安全性。1、STM32F765VGT7ICMCU32BIT1MB......
  • GCC嵌入式开发
    1.编译器和IDE介绍最早刚入门单片机开发的时候,用的最多的就是KEIL开发,但是随着现在的编辑软件不断丰富,类似于KEIL这种偏上世纪的界面编写代码的时候已经十分不优雅了。而仔细刨析下KEIL可以发现,KEIL主要是由一个名为ARMCC的编译器搭建起来的IDE(以下KEIL主要已MDK-ARM说明,C51版本......
  • 物联网嵌入式
    一、物联网嵌入式的介绍 嵌入式是以计算机技术为基础,以应用为中心,软硬件可剪裁,适应应用系统对功能,体积,功耗,成本,可靠性等严格要求的专用计算机系统。二、物联网的层次划分万物互联——internetofthings1.应用层:手机APP(几乎所有的平台层都会提供配套的)    ......
  • 这场研讨会硬件工程师不要错过
    在高速PCB设计中如何保障高频信号的传输和接收,以及保证信号完整性和稳定性?如何解决EMI抑制、时钟分配和功率供应的问题?如何使用开源EDA工具KiCad?如何使用DFM软件高质量提升pcb产品制造,优化制造成本?影响PCB可制造性有哪些关键因素?在PCBA生产加工制造过程焊接工艺可靠性保证的......
  • t507嵌入式linux经典蓝牙通讯demo
    // /*开启蓝牙echo1>/sys/class/rfkill/rfkill0/statertk_hciattach-n-s115200/dev/ttySAC1rtk_h5&hciconfig-ahciconfighci0up打开hciconfighci0piscan使自身可以被发现#hciconfighci0sspmode1设置蓝牙适......
  • 【示波器的原理,使用方法和基本操作步骤】
    简介:示波器入门非常简单,使用AutoScale(自动定标)功能,能轻易的捕捉波形。入门级的AutoScale所采用的“边沿触发”,通过查找波形上的指定沿(上升沿或下降沿等)和电压电平来识别触发。TriggerLevel(触发电平)示波器作用:用来观察和分析电信号的各种特性(包括频率、幅度、相位、波形......
  • 数据库的基本操作2
    今日内容详细外键"""缺陷1.表的重点不清晰 可以忽略 到底是员工表还是部门表2.表中相关字段一直在重复存储 可以忽略 浪费存储空间3.表的扩展性极差,牵一发而动全身不能忽略""" 解决方式 将上述一张表拆分成两张表 emp与dep #上述三个缺陷全......
  • 2.9 深入GPU硬件架构及运行机制
    五、GPU技术要点1.SMID和SIMTSIMD(SingleInstructionMultipleData)是单指令多数据,在GPU的ALU(在Core内)单元内,一条指令可以处理多维向量(一般是4D)的数据。比如,有以下shader指令:float4c=a+b;//a,b都是float4类型对于没有SIMD的处理单元,需要4条指令将4个float数值相加,汇编伪代码......