OpenHarmony 标准系统HDF框架之I2C驱动开发

news2025/1/17 15:18:52

OpenHarmony 标准系统HDF框架之I2C驱动开发

  • 主要内容
  • I2C 基础知识## I2C 基础知识 —— 概念和特性
    • I2C 基础知识 —— 协议、四种信号组合
  • I2C 调试手段## I2C 调试手段 —— 硬件
    • I2C 调试手段 —— 软件
  • HDF 框架下的 I2C 设备驱动## HDF 框架下的 I2C 设备驱动 —— 案例描述
    • HDF 框架下的 I2C 设备驱动 —— 用户态程序
    • HDF 框架下的 I2C 设备驱动 —— 驱动程序入口
    • HDF 框架下的 I2C 设备驱动 —— 设备初始化
    • HDF 框架下的 I2C 设备驱动 —— 驱动 Dispatch
    • HDF 框架下的 I2C 设备驱动 —— 驱动读写
  • 总结

主要内容

  • I2C 基础知识
  • I2C 调试手段
  • HDF 框架下的 I2C 设备驱动

I2C 基础知识## I2C 基础知识 —— 概念和特性

  • I2C(IIC、I2C)集成电路总线,由串行数据线 SDA 和串行时钟线 SCL 组成,对于一个 I2C 接口的器件,至少还需要电源和地线;
  • I2C 总线是双向、半双工传输
  • 支持多主机、多从机同时挂接在一条 I2C 总线上,多主机同时请求总线时,可以通过冲突检测和仲裁机制防止总线数据被破坏
  • 每一个从设备都有唯一的地址,从设备可被寻址(又称为被选中),只有被选中的从设备才能参与通信,每次通信只有一个主设备和一个从设备参与
  • 主设备发起一次通信,从设备响应:主从设备都可以发送和接收数据,SCL 时钟由主设备发出,在工程中常见 MCU 或 SOC 作为主设备,主从设备地位可以交换

在这里插入图片描述

I2C 是串行低速总线,常见传输速度如下:

  • 标准模式(standard-mode):速率高达 100kbit/s
  • 快速模式(fast-mode):速率 400kbit/s
  • 快速模式+(fast-mode plus):速率 1Mbit/s
  • 高速模式(high-speed mode):速率 3.4Mbit/s

工程中常见兼容标准模式和快速模式的 I2C 从设备

在这里插入图片描述

  • 一条 I2C 总线上的所有从设备都有一个唯一的设备地址,不能与总线上的其他设备地址重复;
  • 设备地址有 7 位和 10 位两种格式,常见 7 位格式
  • I2C 主设备对从设备可执行写操作和读操作,通过写地址和读地址区分写操作和读操作

设备地址 7 位:101000(0x50)写地址 8 位:设备地址左移一位i,末位补 0 :1010000 (0xA0)读地址 8 位:设备地址左移一位,末位补 1: 1010001 (0xA1)同一个 I2C 设备可能具有多个设备地址,通常可通过从设备的管脚配置,以 I2C 接口的 ROM 芯片 AT24C256 为例:

在这里插入图片描述

  • 如果 A1 和 A0 两个管脚接地,则 7 位设备地址为:1010000(0x50),8 位写地址:1010000(0xA0),8 位读地址:1010001(0xA1)
  • 片内地址、片内偏移、字地址:从设备内部寻址,如内部寄存器地址或 ROM 读写地址等
  • 同一个 I2C 总线上挂载的设备数量受限于总线上最大电容不超过 400pF

I2C 基础知识 —— 协议、四种信号组合

  • I2C 起始信号和停止信号由主设备发出
  • S:时钟信号 SCL 保持高电平、数据信号 SDA 由高到低跳变
  • P:时钟信号 SCL 保持高电平、数据信号 SDA 由低到高跳变
  • 写信号:主或从设备在时钟信号 SCL 为低电平时将数据写到数据线 SDA,即数据线只能在 SCL 为低电平时发生高低跳变
  • 读数据:数据线需要在 SCL 为高电平时保持稳定,同时从或主设备也会在此时从 SDA 上读取数据

在这里插入图片描述

I2C 调试手段## I2C 调试手段 —— 硬件

  • I2C 协议规定,在空闲状态下,总线为高电平:从设备工作电压 VDD,SDA 和 SCL 电压不低于 0.7VDD(低电平不高于 0.3VDD),常见的 VDD 有 1.8V、3.3V、5V 三种规格
  • 高电平通过外挂上拉电阻实现,需要确保上拉电阻有效

在这里插入图片描述

I2C 调试手段 —— 软件

  • 处理器支持多个 I2C 总线,确认 I2C 设备挂载的总线编号:Hi3516DV300 支持 8 路 I2C 总线,编号 0-7
    在这里插入图片描述

  • 开启内核选项:CONFIG_I2C_CHARDEV(make menuconfig)
    在这里插入图片描述

  • 使用 i2c_tools 工具包中的 i2c_detect 命令检测某条总线上挂载的所有设备
    在这里插入图片描述

HDF 框架下的 I2C 设备驱动## HDF 框架下的 I2C 设备驱动 —— 案例描述

  • I2C 从设备:AT24C256、EEPROM、256Kb
  • A1 和 A2 两条管脚均接地,则 7 位设备地址为:1010000(0x50),8 位写地址:1010000(0xA0),8 位读地址:1010001(0xA1)
  • 写操作:用户态程序将字地址和数据发送给驱动程序,驱动程序将数据写入设备的字地址
  • 读操作:用户程序将字地址发送给驱动程序,驱动程序从指定的设备字地址读取数据,并将数据返回给用户态程序

具体操作(写操作):

  • 写操作:32KByte 空间,按照字节寻址,需要 15bit 字地址(7bit 高位 + 8bit 低位),字地址占用两个字节

在这里插入图片描述

  • 起始信号、设备地址(bit0 = 0)、字地址(高字节)、字地址(低字节)、数据

具体操作(读操作):

  • 读操作:32KByte 空间,按照字节寻址,需要 15bit 字地址(7bit 高位 + 8bit 低位),字地址占用两个字节

在这里插入图片描述

  • 起始信号、设备地址(bit0 = 0)、字地址(高字节)、字地址(低字节)
  • 起始信号、设备地址(bit0 = 1)、接收数据
  • 读操作中包含写操作

HDF 框架下的 I2C 设备驱动 —— 用户态程序

  • 应用程序通过服务名绑定驱动程序,和驱动建立联系
#define SAMPLE_SERVICE_NAME "at24_service"

struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME);
if(serv == NULL){
    printf("fail to get service %s \n", SAMPLE_SERVICE_NAME);
    return HDF_FAILURE;
}

对应的 hcs 文件:

i2c_host :: host{
    hostName = "my_i2c_test";
    priority = 100;
    device_i2c :: device {
        device0 :: deviceNode {
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "at24_drv";
            serviceName = "at24_service";
            deviceMatchAttr = "at24_driver_attr";
        }
    }
}
  • 用户态程序对驱动或设备的所有操作都基于服务
  • 用户态程序以字节为单位将数据写入设备
#define I2C_RD_CMD    456
#define I2C_WR_CMD    789

static int write _data (struct HdfIoService *serv, uint16_t addr, uint8_t value)
{
    //用户态写操作
    struct HdfSBuf *data = HdfSBufObtainDefault Size();
    if(data == NULL){
        HDF_LOGE("fail to obtain sbuf data");
        ret = HDF_DEV_ERR_NO_MEMORY;
        goto out;
    }
    struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
    HdfSbufWriteUint16(data, addr);
    HdfSbufWriteUint8(data, value);
    serv->dispatcher->Dispatch(&serv->object, I2C_WR_CMD, data, reply);
    HdfSbufReadString(reply);
    printf("Get reply is : %s\n", str);
}
  • 获取两个缓冲区 data 和 reply
  • 将字地址(15bit)和数据(8bit)写入 data 缓冲区
  • 调用 Dispatch 将字地址和数据发送给驱动
  • 读取驱动的返回值

用户态程序读操作:

  • 用户态程序以字节为单位从设备读取数据
#define I2C_RD_CMD    456
#define I2C_WR_CMD    789

static int write _data (struct HdfIoService *serv, uint16_t addr, uint8_t value)
{
    //用户态读操作
    struct HdfSBuf *data = HdfSBufObtainDefault Size();
    if(data == NULL){
        HDF_LOGE("fail to obtain sbuf data");
        ret = HDF_DEV_ERR_NO_MEMORY;
        goto out;
    }
    struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
    HdfSbufWriteUint16(data, addr);
    serv->dispatcher->Dispatch(&serv->object,I2C_RD_CMD, data, reply);
    HdfSbufReadUint8(reply, pval);
    HdfSbufReadString(reply);
    printf("Get reply is : data 0x%hhx, str :%s\n", *pval,  str);
}
  • 获取两个缓冲区 data 和 reply
  • 将字地址(15bit)写入 data 缓冲区
  • 调用 Dispatch 将字地址发送到驱动
  • 读取驱动的返回值

HDF 框架下的 I2C 设备驱动 —— 驱动程序入口

  • 驱动程序入口:
struct HdfDriverEntry g_SensorDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "at24_drv",
    .Bind = HdfSensorDriverBind,
    .Init = HdfSensorDriverInit,
    .Release = HdfSensorDriverRelease,
}

HDF_INIT(g_SensorDriverEntry);
  • device_info.hcs 定义设备节点
i2c_host :: host{
    hostName = "my_i2c_test";
    priority = 100;
    device_i2c :: device {
        device0 :: deviceNode {
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "at24_drv";
            serviceName = "at24_service";
            deviceMatchAttr = "at24_driver_attr";
        }
    }
}

hcs 设备节点中定义了一个设备私有属性:deviceMatchAttr = “at24_driver_attr”;

static int32_t GetAT24ConfigData(const struct DeviceResourceNode *node)
{    struct DeviceResourceIface *parser = NULL;
    const struct DeviceResourceNode *at24 = NULL;
    parser = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
    at24 = parser->GetChildNode(node, "at24Attr");
    parser->GetUint16(at24, "busId", &(tpDevice.busId), 0);
    parser->GetUint16(at24, "addr", &(tpDevice.addr), 0);
    parser->GetUint16(at24, "regLen", &(tpDevice.regLen), 0);
    return HDF_SUCCESS;
}

int32_t HdfSensorDriverInit(struct HdfDeviceObject *deviceObject)
{
    if(GetAT24ConfigData(deviceObject->property) != HDF_SUCCESS){
        HDF_LOGE("%s: get at24 config fail!", __func__);
        return HDF_FAILURE;
    }

    if(at24_init() != HDF_SUCCESS){
        HDF_LOGE("i2c at24 driver init failed!");
        return -1;
    }

    HDF_LOGD("i2c at24 driver init success.");
    return 0;
}
  • 解析 hcs 配置文件中定义的属性 at24_driver_attr, 获取设备的私有属性的值
  • 初始化 i2c 从设备

设备私有属性(i2c_test_config.hcs)

root {
    match_attr = "at24_driver_attr";
    at24Attr {    //节点名字 at24Attr
        busId = 5;    //总线编号    5
        addr = 0x50;    //设备地址    0x50
        regLen = 2;        //地址宽度    2字节
    }
}

全局配置文件(device_info.hcs)

i2c_host :: host{
    hostName = "my_i2c_test";
    priority = 100;
    device_i2c :: device {
        device0 :: deviceNode {
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "at24_drv";
            serviceName = "at24_service";
            deviceMatchAttr = "at24_driver_attr";
        }
    }
}

HDF 框架下的 I2C 设备驱动 —— 设备初始化

  • 设备初始化
static int32_t at24_init(void)
{
    tpDevice.i2cHandle = i2cOpen(tpDevice.busId);
    return HDF_SUCCESS;
}
功能分类接口名描述
I2C 控制器管理接口I2cOpen打开 I2C 控制器
I2cClose关闭 I2C 控制器
i2c 消息传输接口I2cTransfer自定义传输

HDF 框架下的 I2C 设备驱动 —— 驱动 Dispatch

int32_t HdfSensorDriverDispatch(struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    uint16_t addr = 0;
    uint8_t value = 0;

    if(id == I2C_WR_CMD){
        HdfSbufReadUint16(data, &addr);
        HdfSbufReadUint8(data, &value);
        TpI2cWriteReg(&tpDevice, addr, &value, 1);
        HdfSbufWriteString(reply, "write success");
    }
    else if(id == I2C_RD_CMD){
       HdfSbufReadUint16(data, &addr);
        TpI2cWriteReg(&tpDevice, addr, &value, 1);
        HdfSbufWriteUint8(reply, value);
        HdfSbufWriteString(reply, "read success");
    }
}

写数据:

  • 读取两个字节的字地址
  • 读取要写到字地址的数据
  • 执行写操作,参数 1 表示写一个字节数据
  • 返回值给用户程序

读数据:

  • 读取两个字节的字地址
  • 执行读操作,参数 1 表示读一个字节数据
  • 返回值给用户程序

HDF 框架下的 I2C 设备驱动 —— 驱动读写

struct TpI2cDevice{
    uint16_t busId;
    uint16_t addr;
    uint16_t regLen;
    DevHandle i2cHandle;
}

struct I2cMsg{
    uin16_t addr;    //i2c 设备地址
    uintt8_t *buf;    //缓存区
    uint16_t len;        //数据传输长度
    uint16_t flags;        //传输模式 flags,区分读写。
}

static struct TpI2cDevice tpDevice;

static inline int TpI2cReadReg(struct TpI2cDevice *tpDevice, uint16_t regAddr, uint8_t *regData, uint32_t dataLen)
{
    return TpI2cReadWrite()tpDevice, regAddr, regData, dataLen, 1);
}

static inline int TpI2cWriteReg(struct TpI2cDevice *tpDevice, uint16_t regAddr, uint8_t *regData, uint32_t dataLen)
{
    return TpI2cReadWrite()tpDevice, regAddr, regData, dataLen, 0);
}

static int TpI2cReadWrite(struct TpI2cDevice *tpDevice, uint16_t regAddr, uint8_t *regData, uint32_t dataLen, uint8_t flaag)
{
    int index = 0;
    unsigned char regBuf[2] = {0};
    struct I2cMsg msgs[2] = {0};

    if(tpDevice->regLen == 1){
        regBuf[index++] = regAddr & 0xFF;
    }
    else {
        regBuf[index++] = (regAddr >> 8 ) & 0xFF;
        regBuf[index++] = regAddr & 0xFF;
    }

    msgs[0].addr = tpDevice->addr;
    msgs[0].flags = 0;
    msgs[0].len = tpDevice->regLen;
    msgs[0].buf = regBuf;

    msgs[1].addr = tpDevice->addr;
    msgs[1].flags = (flag == 1) ? I2C_FLAG_READ : 0;
    msgs[1].len = dataLen;
    msgs[1].buf = regData;

    if(I2cTransfer(tpDevice->i2cHandle, msgs, 2) != 2)
        return HDF_FAILURE;

    return HDF_SUCCESS;
}
  • 总线编号:busId = 5
  • 设备地址:addr = 0x50
  • 地址宽度:2 字节

读操作:

  • 1 为读标志
  • 设备地址最低有效位为 1

写操作:

  • 0 为写标志
  • 设备地址最低有效位为 0

其中参数说明:

  • regAddr 和 regBuf 存放两个字节的字地址
  • dataLen 表示读写数据的字节长度
  • 读写操作的字地址作为数据写到从设备
  • regData 存放读写的数据
  • flags 区分读写操作
  • I2cTransfer 的返回值表示成功发送的 i2cMsg 数据包数量

总结

  • I2C 基础知识:概念和特性、4 个地址(设备地址、读地址、写地址、字地址)、波形(起始、结束、数据发送、数据接收)
  • I2C 调试手段:电压、上拉电阻、/dev/i2c-x、i2c-tools
  • HDF 框架 I2C 驱动:AT24C256 芯片按照字节寻址方式读写(按照页 64 字节寻址、连续读写)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/144903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Shell 传递参数

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……实例以下实例我们向脚本传递三个参数&#xff…

ArcGIS Pro脚本工具(15)——按字段属性值分类导图

之前做了配合地图系列批量导图的脚本工具 ArcGIS Pro脚本工具(9)——配合地图系列批量导图_学学GIS的博客-CSDN博客_arcgispro批量导出地图pngPro的地图系列是批量制图的利器,但是有个不便的地方,就是设置完成地图系列后&#xf…

机器学习实战教程(13篇)

机器学习实战教程(13篇)这些网址非常适合想学习机器学习,却苦于没有项目(尤其缺少数据)的人。无意中看到,给自己做一个记录。机器学习实战教程(一):K-近邻算法(史诗级干货长文&#…

2022年度外设产品行业增长分析:鼠标、键盘同比增长27%

近年来,信息技术迅速发展,网民数量不断增长,深刻改变着社会的运作方式,对社会生产、商业运作模式等方面产生重大影响。广泛的行业应用为网络设备品牌商、制造商带来了差异化的细分市场,各类互联网设备接入数量也快速增…

混合空间增强

混合图像增强是一种图像处理技术,用于在不损失图像细节的情况下增强图像的对比度和亮度。它通常通过将图像拆分成多个通道,然后对每个通道进行独立处理来实现。 综合利用平滑滤波,锐化滤波,灰度拉伸等技术对图像进行处理&#xff…

【Linux】sudo给某条指令提权

sudo1.为什么要有sudo2.使用sudo提权的前提2. 在root下添加普通用户到信任列表3.验证4.总结1.为什么要有sudo 首先我们要知道,普通用户是受Linux的权限约束的。就比如普通用户对自己的家目录有完全管理的权限,但是不能随意查看其他人的家目录&#xff0c…

1.机器学习中的关键组件

1.机器学习中的关键组件 无论什么类型的机器学习问题,都会遇到这些组件: 可以用来学习的_数据_(data);如何转换数据的_模型_(model);一个_目标函数_(objective function…

JavaScript 输出

文章目录JavaScript 输出JavaScript 显示数据使用 window.alert()操作 HTML 元素在本教程中写到 HTML 文档写到控制台您知道吗?JavaScript 输出 JavaScript 没有任何打印或者输出的函数。 JavaScript 显示数据 JavaScript 可以通过不同的方式来输出数据: 使用 wi…

TiDB 6.5 LTS 发版

在 2023 伊始,我们很高兴向大家宣布,TiDB 6.5 LTS 版本已经发布了。这是 TiDB V6 的第二个长期支持版(上一个是 TiDB 6.1),除了携带了诸多备受期待的新特性,同时也将得到 TiDB 开发社区的长期维护&#xff…

设计模式(一):DDD领域驱动设计

DDD重架构,轻业务! – 从单体架构开始快速验证DDD设计模式 贫血模型充血模型防腐层 DDD四层架构规范DDD单体架构和微服务架构隔离 本地单体SPI 插拔式扩展业务模式Nacos 插拔式扩展业务模式 ---- 服务名作为服务service code DDD清晰架构 – 构建领域仓库…

加解密与HTTPS(5)

您好,我是湘王,这是我的CSDN博客,欢迎您来,欢迎您再来~咱们大学读完之后有毕业证书,并且这个证书可以在学信网查询。专业上有注会、CCIE、律师证等,可以在国家职业认证机构或委托机构的网站上查…

【UCIe】UCIe DLP/DLLP 介绍

🔥点击查看精选 UCIe 系列文章🔥 🔥点击进入【芯片设计验证】社区,查看更多精彩内容🔥 📢 声明: 🥭 作者主页:【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#xff0…

DNS协议——域名解析

DNS协议 DNS协议是一个应用层协议,作用是域名解析   使用DNS协议的原因,就是因为哪怕是点分十进制,对于我们都不太友好,因此使用域名来代替IP地址,比如www.baidu.com   最开始的时候,DNS系统只是一个普…

java基于ssm的个人博客系统个人博客网站个人博客项目源码

简介 Java ssm开发的个人博客系统,可以发布博客,照片,站长留言。 演示视频 https://www.bilibili.com/video/BV1sf4y1y7Ne/?share_sourcecopy_web&vd_sourceed0f04fbb713154db5cc611225d92156 技术 mysql,SSM,css,js,jq 功能 简单…

laravel5.5之laravel-admin的使用

目录 一、首先确保安装好了laravel,并且数据库连接设置正确 二、运行下面的命令来发布资源 三 、后台项目文件 四、静态文件 五、访问后台 六、参考文档 一、首先确保安装好了laravel,并且数据库连接设置正确 composer require encore/laravel-adm…

Verilog语法笔记(夏宇闻第三版)-简单的Verilog HDL模块

目录 简单的Verilog HDL程序介绍: 模块的结构 : 模块的端口定义: 模块内容: I/O说明的格式: 内部信号说明: 功能定义: 简单的Verilog HDL程序介绍: 下面先介绍几个简单的Veri…

高可用Keepalived在Linux中的应用

一、高可用 1.高可用介绍 (1)普通理解 两台服务器启动相同的业务系统,当有一台机器宕机,另外一台服务器快速接管服务,对于用户来讲是无感知的。 (2)专业理解 高可用是分布式系统架构设计中必…

Linux安装Docker与基本使用

Docker 常用于服务部署的一种方案 准备 1.云服务器或者虚拟机 2.Centos 系统 3.下载XFtp 和 XShell 安装Docker 首先删除系统中旧版本Docker或者残留文件 #卸载所有 yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-lo…

Spring从入门到精通 | 动力节点老杜

目录 一、Spring启示录 1、OCP开闭原则 2、依赖倒置原则(DIP原则) 3、控制反转(重点) 4、Spring框架(重点) 二、Spring概述 Spring的8大模块 ​编辑 Spring特点 1、轻量 2、控制反转IOC 3、面向…

Qt Mac阻止和启用休眠

阻止电脑休眠,在win下是使用win api实现的,用Qt跨平台开发,实现Mac的阻止休眠,也需要Mac本身的api来实现。经过网上查资料,找到了一个能用的,链接如下。原生mac开发用的是Object-c,所以该实现用…