用Arduino中Wire库写I2C驱动-入门篇

用Arduino中的Wire库网上已经有很多文章了,但是今天我要写的是稍微不一样的内容。

很早以前我就在汇编,用C来写I2C的驱动,但是因为工作性质的原因(我的工作不是这个行业),只是出于业余爱好在断断续续的玩这些东西,最近自己又在折腾Arduino玩ESP32,要驱动一颗I2C接口芯片。

Arduino没有这个芯片的库,所以不得不自己写一个。刚刚开始研究,逐步的记下来,今天是第一篇入门。

关于I2C总线上SDA和SCL的时序问题我不想讲太深入,网上资料能够搜索到很多。

一、I2C读写的大致流程

所以我要说的第一个问题是:读写原理。这个对Wire库的应用来说很重要,我会把I2C的读写原理和Wire的应用结合起来讲,把这个Wire库为什么这样用讲得更明白。

我们知道,I2C只有SDA和SCL线。时钟线上的脉冲始终是由master设备产生的,数据线是master和slave都可以拉高或拉低的。

master想与slave通信,大概流程是:

(1)master发起一个开始信号->

(2)master发送地址码,发送完毕后释放SDA->

(3)所有slave检查地址码,如果是自己的话就拉低SDA进行回应,然后释放掉SDA->

(4)发送方(可以master或者Slave)往SDA写想发送的数据,发送完一个字节8bit后,释放掉SDA,接收方拉低SDA给发送方一个回应ACK;如果发送方没有收到ACK,则表示接收方没有接收成功。

(e)数据传输结束后,master发送一个STOP信号。

以下分两种I2C器件类型,来更深入的讲读写上面的区别。

二、单寄存器简单器件,Addr+data

这是一类最为简单的器件,如I2C接口的IO扩展器件PCF8574。

这种器件内部只有一个寄存器,所以不存在写寄存器地址的问题。master想向它写数据不用先写寄存器地址,而是直接把数据发送给它,总线操作就是直接Addr+data即可。

Wire库对这种器件的操作:

写数据:

Wire.beginTransmission(I2C_ADDR_SLAVE);//启动总线,往总线上写Slave的地址,并等待ACK回应
Wire.printf(byte);//往总线上传输写一个字节的数据
Wire.endTransmission(true);//结束总线传输

注意最后一行endTransmission(true)中的true参数,这个是决定master是否发送停止信号的。如果是true,那master就会往总线发送一个停止信号,结束数据传输。如果为false,则master可以继续发送数据,后面会讲到。

读数据:

uint8_t temp;
uint8_t bytesReceived=Wire.requestFrom(I2C_ADDR_SLAVE, 1);//从slave读取一个字节到master的数据缓冲区
Wire.readBytes(temp, bytesReceived);//把数据从缓冲区读到变量里来

要注意的是,requestFrom一句其实里面也包括了启动传输信号,发送slave addr过程的,只是发送slave addr后就是slave器件把数据发送给master。master接收完1个字节后,就会往总线上写停止信号。

然后,这一句其实已经把数据从slave读回来了,放在Wire类所定义的缓冲区中的,需要再调用一个readBytes,把数据从缓冲区中读到自己定义的变量里。

三、多寄存器器件,Addr+regAddr+Data

大多数I2C器件都属于这种器件,里面有多个寄存器,Datasheet上会给你说明,有哪些寄存器,地址是什么。

我们拿VL53L0X这个TOF器件举简单例子。下面是该器件手册上说明的几个测试寄存器。

现在我们需要从地址为0x51寄存器读出其数值,怎么操作呢?

读数据:


  int32_t ack;
  uint8_t bytesReceived=0;
  uint8_t temp[2];
 
  Wire.beginTransmission(I2C_SLAVE_ADDRESS);//启动传输,写Slave地址
  Wire.write(0x51);//写寄存器地址
  ack=Wire.endTransmission(true);    //结束传输,写结束信号

  bytesReceived=Wire.requestFrom(I2C_SLAVE_ADDRESS,2);//再次启动传输,读取两个字节
  if(bytesReceived==2)
  {
    temp[0]=_ptrI2C_Master->ptrI2CBus->read();
    temp[1]=_ptrI2C_Master->ptrI2CBus->read();
    *adc_code=temp[0]*256+temp[1];///究竟LSB在前还是MSB在前,需要实际测试
  }
  return(ack);  

注意代码中的注释。

master先对器件写一次,写的内容是寄存器地址regAddr。器件收到后就明白了,master这是要让我把regAddr这个寄存器地址的数值传送给它。

master然后使用requestFrom,再次发送启动信号和slave addr,紧接着是slave发送该寄存器的2字节数据给master。然后master往总线写停止信号,结束传输。

Wire.endTransmission(true); 这行很多器件true和false都支持(比如VL53L0x),也就是你写完寄存器地址后可以停止总线,也可以不停止总线。这是因为后面requestFrom会再次发送启动信号,相当于复位了总线。但有的器件不支持,可能会出错,需要实际看数据手册和测试。

后来的补充:LTC2944这个芯片在读寄存器时,Wire.endTransmission(true)参数就不能是true,必须是false,然而这个参数不写的话默认值就是true!为此被坑了一晚上。。。

写数据:

写数据就相对简单了。

  int32_t ack;
 
  Wire.beginTransmission(I2C_SLAVE_ADDRESS);//启动传输,写Slave地址
  Wire.write(0x51);//写寄存器地址
  Wire.write(0x00);//写数据
  Wire.write(0x99);//写数据
  ack=Wire.endTransmission(true);    //结束传输,写结束信号

先写器件地址,再紧接着写数据即可。

最后,希望这篇文章对你用Wire库编写I2C器件驱动有所帮助。

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值