Wire 库函数详解 及 ESP8266和STM32 i2C通信_均在Arduino IDE环境下烧录

使用之前使用以下语句进行Wire库声明

#include <Wire.h>

1.主读从写案例

主设备读取请求从设备,

// 引入Wire库文件
#include <Wire.h>

void setup()
{
    // Wire初始化, 加入i2c总线
    // 如果未指定,则以主机身份加入总线。
    Wire.begin();
    // 初始化串口并设置波特率为9600
    Serial.begin(9600);
}

void loop()
{
    // 向从设备#8请求6个字节
    Wire.requestFrom(8, 6);  //Wire.requestFrom(从机地址, 字节数);
    // 当从从设备接收到信息时值为true
    while (Wire.available())
    {
        // 接受并读取从设备发来的一个字节的数据
        char c = Wire.read();
        // 向串口打印该字节
        Serial.print(c);
    }
    // 延时500毫秒
    delay(500);
}

从设备发送

// 引入Wire库文件
#include <Wire.h>

void setup()
{
    // Wire初始化, 并以从设备地址#8的身份加入i2c总线
    Wire.begin(8);
    // 注册请求响应事件
    Wire.onRequest(requestEvent);
}

void loop()
{
    delay(100);
}

// 每当接收到来自主机的数据时执行的事件函数
// 此函数被注册为事件,调用请见setup()
void requestEvent()
{
    // 用6个字节的消息进行响应
    Wire.write("hello ");
    // 以此回应主设备的请求
}

2.主写从读案例

主设备写入总线

// 引入Wire库文件
#include <Wire.h>
 
void setup()
{
    // Wire初始化, 加入i2c总线
    // 如果未指定,则以主机身份加入总线。
    Wire.begin();
}
 
// 定义一个byte变量以便串口调试
byte x = 0;
 
void loop()
{
    // 将数据传送到从设备#8
    Wire.beginTransmission(8);
    // 发送5个字节
    Wire.write("x is ");
    // 发送一个字节
    Wire.write(x);
    // 停止传送
    Wire.endTransmission();
 
    x++;
    delay(500);
}

从设备接受

// 引入Wire库文件
#include <Wire.h>
 
void setup()
{
    // Wire初始化, 并以从设备地址#8的身份加入i2c总线
    Wire.begin(8);
    // 注册接受事件函数
    Wire.onReceive(receiveEvent);
    // 初始化串口并设置波特率为9600
    Serial.begin(9600);
}
 
void loop()
{
    delay(100);
}
 
// 每当接收到来自主机的数据时执行的事件函数
// 此函数被注册为事件,调用请见setup()
void receiveEvent(int howMany)
{
    // 循环读取数据(除了最后一个字符)
    while (1 < Wire.available())
    {
        // 接收字节数据并赋值给变量c(char)
        char c = Wire.read();
        // 打印该字节
        //太极创客团队 / Taichi-Maker (www.taichi-maker.com)
        Serial.print(c);
    }
    // 以int整数的形式接受字节数据并赋值给x(int)
    int x = Wire.read();
    // 打印该int变量x
    Serial.println(x);
}

3.单独函数详解

3.1  begin()

初始化Wire库,并以主机从机身份加入I2C总线。

通常来说这个函数只调用一次。

Wire.begin()
Wire.begin(address)

参数
address(可选):7位从机的地址; 如果这个参数未指定,则默认以主机身份加入总线。
当填写了参数时,设备会以从机模式加入IIC总线,address可以设置为0~127中的任意地址。

注:地址从0到7 被保留了, 因此您在开发的时候请不要使用它们!!! 可以从8开始使用。

3.2  requestFrom()

由主设备用来向从设备请求字节使用。

请求发送之后可以使用available()read()来接受并读取数据。

从Arduino 1.0.1开始,requestFrom()接受一个布尔参数来适配某些I2C设备来达到兼容的目的。

如果为true,则requestFrom()在请求之后发送停止消息,从而释放I2C总线。

如果为false,则requestFrom()在请求之后发送重启消息。 总线不会释放,这个操作就阻止了另一个主设备在消息之间请求。 这样一来,一台主设备就可以在控制下发送多个请求。

默认值是true。

Wire.requestFrom(address, quantity)
Wire.requestFrom(address, quantity, stop)

参数
address: 设备的7位地址,用于请求字节
quantity: 请求的字节数
stop (bool): 值为true则在请求后发送停止消息,释放总线。值为 false则在请求后发送重启信息,
以保持连接处于活动状态。

返回值类型 :byte
返回从设备响应的字节数

3.3  beginTransmission()

使用指定的地址开始向I2C从设备进行传输。

在调用了Wire.beginTransmission(address)函数之后,使用write()函数对要传输的字节进行队列,并通过调用endTransmission()进行传输。

Wire.beginTransmission(address)

参数
address: 要传输数据的目的设备的7位地址

3.4  write()

对于从设备来说: write()用于响应来自主设备的请求,即从设备写入数据

对于主设备来说: write()将数据进行队列, 用以从主设备传输到从设备, 这个函数通常在beginTransmission()endTransmission()之间进行调用。

Wire.write(value)
Wire.write(string)
Wire.write(data, length)

参数
value: 一个要发送的单字节
string: 一系列要发送的字符串
data: 要作为字节发送的数组数据
length: 要传输的字节数

返回值类型 : byte
write()将返回写入的字节数,虽然读取这个返回值是不必要的

3.5  endTransmission()

停止与从机的数据传输。

Wire.endTransmission()
Wire.endTransmission(stop)

参数
stop(bool): 参数值为true时将在请求后发送停止指令并释放总线;
参数值为false时将在请求后发送重新启动的指令,保持连接状态。

返回值类型:byte
返回传输的状态值:
0: 成功
1: 数据量超过传送缓存容纳限制
2: 传送地址时收到 NACK
3: 传送数据时收到 NACK
4: 其它错误

3.6  available()

available() 函数可用于检查是否接收到数据。该函数将会返回等待读取的数据字节数。

应该在调用requestFrom()之后再在主设备上调用此函数,或者在从设备的onReceive()的事件处理函数内的调用此函数。

if(Wire.available()){

}

返回缓冲区中可读取的字节数。如果有字节就不为0

3.7  read()

读取在requestFrom()调用后从从设备响应发送到主设备的字节,或从主设备发送到从设备的字节。

Wire.read()

读取的下一个字符,返回值为接读取到的数据流中的1个字符。
如果没有数据时,返回值为-1

3.8  SetClock()

SetClock()函数用于修改I2C通信的时钟频率。

I2C从设备没有最低的工作时钟频率,但是通常以100KHz为基准。

Wire.setClock(clockFrequency)

clockFrequency:所需通信时钟的值(以赫兹为单位)。
可接受的值为100000(标准模式)和400000(快速模式)。一些处理器还支持10000(低速模式),
1000000(加快速模式)和3400000(高速模式)。

3.9  onReceive()

当从设备接收到来自主机的传输时,注册要调用的函数。

Wire.onReceive(handler)
 
handler:从机接收数据时要调用的函数;

这个函数应该使用一个int参数(用于从主设备读取字节数),并且这个函数不应返回任何内容

3.10   onRequest()

当主设备请求从从设备发送数据时,从设备通过onRequest设置调用的函数。

Wire.onRequest(handler)

handler:当主设备请求从从设备发送数据时,从设备要调用的函数。此函数不带参数,不返回任何值,

4.  Arduino 搭建其他开发板的烧录环境

4.1   Arduino IDE搭建ESP8266烧录环境

Step1、下载Arduino IDE 官网:Software | Arduino

如下图进入官网下载安装即可。

Step2、安装完成后,打开软件下载ESP8266的开发板,

Step3、插上开发板并在工具—>开发板选择esp8266,找一个闪灯程序烧录试试,在实例里找一个烧录上

4.2   Arduino IDE搭建STM32烧录环境

恭喜你上述4.1你已经安装了Arduino IDE,下面安装Arduino - STM32的烧录环境。

Step1、在文件—>首选项—>添加开发板地址,github的地址在国内不翻墙不一定能访问,所以添加上面的地址。

http://dan.drown.org/stm32duino/package_STM32duino_index.json
https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json

Step2、完了之后,在开发板管理器,输入STM32,你的开发板是F1系的就安装F1,F4系的就安装F4。

Step3、要烧录STM32,还需要安装32位的芯片处理器,不然烧录会报错的;在开发板输入SAM,然后选择如下图,-M3的安装即可。

Step4、选择烧录方式,一般用串口Serial、StLink烧录。

Step5、烧录一个闪灯程序,看能否烧录成功了,注意查看你的STM32开发板的板载LDE灯引脚是不是PC13,不是的话按照板载LED定义改引脚。

5.ESP8266-12F  与  STM32F103C8T6 的  i2C  通信

最终目标:用一块ESP8266作主机 与 一块或多块 STM32作从机  i2C通信。

5.1  ESP8266 wire库详解

当安装了ESP8266开发板后,可以在Arduino的AppData—>Arduino15—>packages—>ESP8266—>hardware找到开发板libraries文件夹所有内置库都在里面了,打开Wire后打开里面的.h和.cpp文件查看一下该库文件的函数方法和数据结构,这两个文件分别是该库的头文件和执行文件。

看代码用notepad++很方便哦,如下图,notepad资源打包放在后面了;.h文件里就能看到所有的函数方法及其里面的数据结构。

5.1.1  示例程序

  1.将ESP8266作为主机角色并向从机写入数据

//将ESP8266作为主机角色并向从机写入数据

#include <Wire.h>
#include <PolledTimeout.h>

#define SDA_PIN 4     //数据线
#define SCL_PIN 5     //时钟线
const int16_t I2C_MASTER = 0x42;    //主机地址
const int16_t I2C_SLAVE = 0x08;     //从机地址

void setup() {
  Wire.begin(SDA_PIN, SCL_PIN, I2C_MASTER); //初始化总线,不填入参数以主机身份初始化总线
}

byte x = 0;

void loop() {
  using periodic = esp8266::polledTimeout::periodicMs;   //设置响应超时时间
  static periodic nextPing(1000);

  if (nextPing) {
    Wire.beginTransmission(I2C_SLAVE); // 开始向地址为0x08的从机设备写入数据,呼叫8号
    Wire.write("x is ");        // 写入5字节数据,空格是一个字节
    Wire.write(x);              // 写入1字节数据
    Wire.endTransmission();    // 传输完成
    x++;
  }
}

打开头文件看看,初始化begin,要定义数据线和时钟线。

2.  将ESP8266作为主机角色读取从机数据

// 将ESP8266作为主机角色并向从机写入数据

#include <Wire.h>
#include <PolledTimeout.h>

#define SDA_PIN 4     //数据线
#define SCL_PIN 5     //时钟线
const int16_t I2C_MASTER = 0x42;    //主机地址
const int16_t I2C_SLAVE = 0x08;     //从机地址

void setup() {
  Serial.begin(115200);  // 初始化串口波特率115200
  Wire.begin(SDA_PIN, SCL_PIN, I2C_MASTER);        //初始化总线,不填入参数以主机身份初始化总线
}

void loop() {
  using periodic = esp8266::polledTimeout::periodicMs; //设置响应超时时间
  static periodic nextPing(1000);

  if (nextPing) {
    Wire.requestFrom(I2C_SLAVE, 6);    // 向从属设备#8请求6个字节

    while (Wire.available()) { // 判断总线是否有数据
      char c = Wire.read(); // 读取一个字符
      Serial.print(c);         // 串口打印出字符
    }
  }
}

3. 将ESP8266作为从机角色读取数据

// 将ESP8266作为从机角色读取数据

#include <Wire.h>

#define SDA_PIN 4
#define SCL_PIN 5

const int16_t I2C_MASTER = 0x42;
const int16_t I2C_SLAVE = 0x08;

void setup() {
  Serial.begin(115200);           // 初始化串口波特率115200
  Wire.begin(SDA_PIN, SCL_PIN, I2C_SLAVE); // 初始化总线,从机必须设置地址
  Wire.onReceive(receiveEvent); // 申请加入到总线
}

void loop() {
}

// 每当从主服务器接收到数据时执行的函数
// 此函数已注册为事件
void receiveEvent(size_t howMany) {

  (void) howMany;
  while (1 < Wire.available()) { // 循环遍历除所有内容
    char c = Wire.read(); // 读取字符
    Serial.print(c);         // 打印字符
  }
  int x = Wire.read();    // 读取为整形字符
  Serial.println(x);         // 打印字符
}

4. 将ESP8266作为从机角色写入数据

// 将ESP8266作为从机角色写入数据

#include <Wire.h>

#define SDA_PIN 4
#define SCL_PIN 5
const int16_t I2C_MASTER = 0x42;
const int16_t I2C_SLAVE = 0x08;

void setup() {
  Wire.begin(SDA_PIN, SCL_PIN, I2C_SLAVE);                // 初始化总线,从机必须设置地址
  Wire.onRequest(requestEvent); // 申请加入到总线
}

void loop() {
}

//每当主服务器请求数据时执行的函数
//此函数已注册为事件
void requestEvent() {
  Wire.write("hello\n"); // 用6字节的消息进行响应
}

5.2  STM32 wire库详解

5.2.1  示例程序

1.  将STM32作为从机读取数据

// 将STM32作为从机读取数据

#include <Wire_slave.h>

void setup(){
  Serial.begin(115200);         // 初始化串口波特率115200

  Wire.begin(4);                // 使用地址#4加入i2c总线
  Wire.onReceive(receiveEvent); // 申请加入到总线
}

void loop(){
  delay(100);
}

//每当从主服务器接收到数据时执行的函数
//此函数已注册为事件
void receiveEvent(int howMany){
  while(1 < Wire.available()){ // 循环判断总线是否有数据
    char c = Wire.read(); // 读取一个字符
    Serial.print(c);         // 打印字符
  }
  int x = Wire.read();    // 读取一个整数
  Serial.println(x);         // 打印字符
}

2. 将STM32作为从机写入数据

// 将STM32作为从机写入数据


#include <Wire_slave.h>

void setup()
{
  Wire.begin(8);                // 使用地址#8加入i2c总线
  Wire.onRequest(requestEvent); // 申请加入总线
}

void loop()
{
  delay(100);
}

//当主机请求时回复
void requestEvent()
{
  Wire.write("hello "); // 响应6个字节
}

3.  STM32 作主机扫描总线上的从设备

//  STM32 扫描总线上的设备

#include <Wire_slave.h>

void setup() {
  Serial.begin(115200);  // 初始化串口波特率115200
  Wire.begin();          // 初始化总线,不填入地址作为主机
  Serial.println("\nI2C Scanner");
}


void loop() {
  byte error, address;
  int nDevices = 0;  //响应设备计数变量
  Serial.println("Scanning...");

  for (address = 1; address < 127; address++) {  //向1~127地址尝试发送数据,尝试呼叫
    Wire.beginTransmission(address);
    error = Wire.endTransmission();  //返回发送状态,0成功,1数据量过大,2呼叫成功,3发送成功,4未知错误

    if (error == 0) {  //如果发送成功,打印出从机地址,计数变量+1
      Serial.print("I2C device found at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.println(address, HEX);
      nDevices++;
    } else if (error == 4) {  //如果遇到错误,打印从机地址并报错
      Serial.print("Unknown error at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.println(address, HEX);
    }
  }

  if (nDevices == 0)
    Serial.println("No I2C devices found");
  else
    Serial.println("done");
  delay(5000);
}

4.  STM32作为主机SoftWire扫描总线上的所有设备地址 

// STM32扫描总线上的所有设备地址 

#include <SoftWire.h>

SoftWire SWire(PB6, PB7, SOFT_FAST);


void setup() {
  Serial.begin(115200);
  SWire.begin();
  Serial.println("\nSoftware I2C.. Scanner");
}


void loop() {
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++) {

    SWire.beginTransmission(address);
    error = SWire.endTransmission();
    
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) 
        Serial.print("0");
      Serial.println(address, HEX);

      nDevices++;
    }
    else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16) 
        Serial.print("0");
      Serial.println(address, HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found");
  else
    Serial.println("done");

  delay(5000);
}

5.3  ESP8266 和 STM32 i2C测试

ESP8266主机,STM32从机,读取搞定了。

如下现场施工图,有点杂乱.......

ESP8266 和 STM32 接线图:

电池接线示意供电,ESP8266直接用microUSB插电脑供电和监视串口输出。

特多扩展

参考或源码出处声明:

1.太极创客网:Arduino – Wire 库 – 太极创客

2.CSDN:Arduino中Wire类库介绍_arduino wire库-CSDN博客

3.CSDN:使用Arduino IDE来编写上传STM32以及STM8代码,STM32Duino教程

4.文心快码:文心快码-STM32 i2C 引脚

下章预告:

 Arduino IDE下Arduino、ESP32、STM32测试Lora模块(亿佰特Lora E22-400T30-10公里通信模块-EByte-LoRa-E22-library-1库详解)

Wire.hWire.c是Arduino中用于I2C通信的库文件,其中Wire.h是头文件,包含了I2C通信所需要的常量函数的声明,而Wire.c是源文件,包含了I2C通信所需要的具体实现代码。 以下是Wire.h的代码示例: ``` #ifndef TwoWire_h #define TwoWire_h #include <inttypes.h> #define BUFFER_LENGTH 32 class TwoWire { public: TwoWire(); void begin(); void begin(uint8_t); void begin(int); void end(); void setClock(uint32_t); void setClockStretchLimit(uint32_t); void beginTransmission(uint8_t); void beginTransmission(int); uint8_t endTransmission(void); uint8_t endTransmission(uint8_t); uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint8_t); uint8_t requestFrom(int, int); uint8_t requestFrom(int, int, int); virtual size_t write(uint8_t); virtual size_t write(const uint8_t *, size_t); virtual int available(void); virtual int read(void); virtual int peek(void); virtual void flush(void); void onReceive(void(*)(int)); void onRequest(void(*)(void)); void onService(void); private: static uint8_t rxBuffer[]; static uint8_t rxBufferIndex; static uint8_t rxBufferLength; static uint8_t txAddress; static uint8_t txBuffer[]; static uint8_t txBufferIndex; static uint8_t txBufferLength; static uint8_t transmitting; static void (*user_onRequest)(void); static void (*user_onReceive)(int); static void onRequestService(void); static void onReceiveService(uint8_t*, int); }; extern TwoWire Wire; #endif ``` 以下是Wire.c的代码示例: ``` #include "Wire.h" #include <util/twi.h> TwoWire Wire = TwoWire(); static void (*user_onRequest)(void); static void (*user_onReceive)(int); void onRequestService(void); void onReceiveService(uint8_t*, int); TwoWire::TwoWire() { rxBufferIndex = 0; rxBufferLength = 0; txBufferIndex = 0; txBufferLength = 0; transmitting = 0; user_onRequest = NULL; user_onReceive = NULL; } void TwoWire::begin() { twi_init(); } void TwoWire::begin(uint8_t address) { twi_setAddress(address); twi_attachSlaveTxEvent(onRequestService); twi_attachSlaveRxEvent(onReceiveService); twi_init(); } void TwoWire::begin(int address) { begin((uint8_t)address); } void TwoWire::end() { twi_disable(); } void TwoWire::setClock(uint32_t frequency) { twi_setFrequency(frequency); } void TwoWire::setClockStretchLimit(uint32_t limit) { twi_setClockStretchLimit(limit); } uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) { if(quantity > BUFFER_LENGTH){ quantity = BUFFER_LENGTH; } rxBufferLength = quantity; rxBufferIndex = 0; twi_readFrom(address, rxBuffer, quantity); return quantity; } uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) { twi_setStop(sendStop); return requestFrom(address, quantity); } uint8_t TwoWire::requestFrom(int address, int quantity) { return requestFrom((uint8_t)address, (uint8_t)quantity); } uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) { return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop); } void TwoWire::beginTransmission(uint8_t address) { txAddress = address; txBufferLength = 0; transmitting = 1; } void TwoWire::beginTransmission(int address) { beginTransmission((uint8_t)address); } uint8_t TwoWire::endTransmission() { return endTransmission(true); } uint8_t TwoWire::endTransmission(uint8_t sendStop) { uint8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop); txBufferLength = 0; transmitting = 0; return ret; } size_t TwoWire::write(uint8_t data) { if(transmitting){ if(txBufferLength >= BUFFER_LENGTH){ return 0; } txBuffer[txBufferLength++] = data; return 1; }else{ return 0; } } size_t TwoWire::write(const uint8_t *data, size_t quantity) { if(transmitting){ for(size_t i = 0; i < quantity; ++i){ if(txBufferLength >= BUFFER_LENGTH){ return i; } txBuffer[txBufferLength++] = data[i]; } return quantity; }else{ return 0; } } int TwoWire::available(void) { return rxBufferLength - rxBufferIndex; } int TwoWire::read(void) { int value = -1; if(rxBufferIndex < rxBufferLength){ value = rxBuffer[rxBufferIndex]; ++rxBufferIndex; } return value; } int TwoWire::peek(void) { int value = -1; if(rxBufferIndex < rxBufferLength){ value = rxBuffer[rxBufferIndex]; } return value; } void TwoWire::flush(void) { // XXX: to be implemented. } void TwoWire::onReceive(void(*function)(int)) { user_onReceive = function; } void TwoWire::onRequest(void(*function)(void)) { user_onRequest = function; } void TwoWire::onService(void) { if(twi_isAddressed()){ // onRequest callback if(twi_isMasterWrite()){ if(user_onRequest){ user_onRequest(); } } // onReceive callback else { if(user_onReceive){ user_onReceive(twi_receiveByte()); } } } } void onRequestService(void) { if(Wire.user_onRequest){ Wire.user_onRequest(); } } void onReceiveService(uint8_t* inBytes, int numBytes) { Wire.rxBuffer = inBytes; Wire.rxBufferIndex = 0; Wire.rxBufferLength = numBytes; if(Wire.user_onReceive){ Wire.user_onReceive(numBytes); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mike小朋友

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值