GD32VF103 I2C 通讯

通讯原理和接线参考 这篇文章。本文重点介绍代码,并以 I2C1 为例,I2C0 除了 GPIO 不同外功能完全一样。

首先打开 I2C 的 RCU

  rcu_periph_clock_enable(RCU_I2C1);

因为 I2C 是线与,所以它可以兼容 5V 和 3V3 设备,但我们需要配置 GPIO 口功能为 AF 开漏输出 (Open Drain)

  /* I2C0 GPIO 配置 */
  gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
  /* I2C1 GPIO 配置 */
  gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_10 | GPIO_PIN_11);

之后进行 I2C 的初始化
I2C 初始化后默认为 slave 模式,只有软件切换到发送数据时成为 master 模式
初始化后关闭 ACK,这样可以避免在没有准备接受消息时受到消息

  i2c_disable(I2C1);

/* I2C speed: regular speed 100000; fast mode 400000 */
const uint32_t SLAVE_SPEED = 100000;

/* 【注意!】 这里比较坑,I2C 的 slave 地址需要左移 1 位。the address need to sll by 1 */
const uint32_t SLAVE_ADDRESS = 0x01 << 1;

  /* configure the I2C clock and duty ratio */
  i2c_clock_config(I2C1, SLAVE_SPEED, I2C_DTCY_2);
  /* 设置 I2C 模式,地址模式为 7bit 模式 */
  i2c_mode_addr_config(I2C1, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, SLAVE_ADDRESS);

  /* acknowledge position */
  i2c_ackpos_config(I2C1, I2C_ACKPOS_CURRENT);
  i2c_ack_config(I2C1, I2C_ACK_DISABLE);
  
  /* enable I2C1 */
  i2c_enable(I2C1);

下面分为四个模式,分别定义对应的方法

具体的代码运行逻辑还是要参考 手册 以及 STM32 的 HAL 库


uint8_t I2C_requestMasterRead(uint32_t I2Cx, uint16_t device_addr, uint64_t timeout) {
  uint64_t timeout_tick = get_timer_value() + (TIMER_FREQ / 1000 * timeout);

  /* step 2 */
  /* generate START condition */
  I2C_CTL0(I2Cx) |= I2C_CTL0_START;

  /* step 3 */
  /* wait for hardware to set SBSEND bit */
  while (!i2c_flag_get(I2Cx, I2C_FLAG_SBSEND)) {
    if (get_timer_value() > timeout_tick) {
      printf("SBSEND timeout\n");
      return -1;
    }
  }

  if (I2C_SADDR0(I2Cx) & I2C_SADDR0_ADDFORMAT == I2C_ADDFORMAT_10BITS) {
    /* if in 10-bit mode */
    // TODO: implement 10-bit mode
  }
  else {
    /* if in 7-bit mode */
    i2c_data_transmit(I2Cx, device_addr | 0b1UL);
    i2c_flag_clear(I2Cx, I2C_FLAG_SBSEND);
  }
  

  /* step 4 */
  /* wait for hardware to set ADDSEND bit */
  while (!i2c_flag_get(I2Cx, I2C_FLAG_ADDSEND)) {
    if (get_timer_value() > timeout_tick) {
      printf("ADDSEND timeout\n");
      return -1;
    }
  }
  
  if (I2C_SADDR0(I2Cx) & I2C_SADDR0_ADDFORMAT == I2C_ADDFORMAT_10BITS) {
    /* if in 10-bit mode */
    // TODO: implement 10-bit mode
  }
}

/**
 * @brief Receive data from slave device in blocking mode
 * 
 * 
 * @param I2Cx 
 * @param device_addr 
 * @param buffer 
 * @param size 
 * @param timeout 
 * @return uint8_t 
 */
uint8_t HAL_I2C_masterReceive(uint32_t I2Cx, uint16_t device_addr, uint8_t *buffer, uint16_t size, uint64_t timeout) {
  /* this method implements the Solution B in the user manual for time independency. */

  uint64_t timeout_tick = get_timer_value() + (TIMER_FREQ / 1000 * timeout);

  /* disable POAP */
  I2C_CTL0(I2Cx) &= ~I2C_CTL0_POAP;

  /* enable acknowledge */
  I2C_CTL0(I2Cx) |= I2C_CTL0_ACKEN;

  /* if first transfer */
  /* step 2 for N=2: set POAP bit before START condition */
  if (size == 2) {
    I2C_CTL0(I2Cx) |= I2C_CTL0_POAP;
  }

  I2C_requestMasterRead(I2Cx, device_addr, timeout);

  /* continue step 4 */
  if (size == 0U) {
    /* clear ADDR flag */
    i2c_flag_clear(I2Cx, I2C_FLAG_ADDSEND);

    /* generate STOP condition */
    I2C_CTL0(I2Cx) |= I2C_CTL0_STOP;
  }
  else if (size == 1U) {
    /* step 4 for N=1 */
    /* disable ACK and clear ADDR flag */
    I2C_CTL0(I2Cx) &= ~(I2C_CTL0_ACKEN);
    i2c_flag_clear(I2Cx, I2C_FLAG_ADDSEND);

    /* generate STOP condition */
    I2C_CTL0(I2Cx) |= I2C_CTL0_STOP;
  }
  else if (size == 2U) {
    /* disable ACK and clear ADDR flag */
    I2C_CTL0(I2Cx) &= ~(I2C_CTL0_ACKEN);
    i2c_flag_clear(I2Cx, I2C_FLAG_ADDSEND);
  }
  else {
    /* clear ADDR flag */
    i2c_flag_clear(I2Cx, I2C_FLAG_ADDSEND);
  }

  while (size > 0U) {
    if (size <= 3U) {
      if (size == 1U) {
        /* step 5 for N=1 */
        /* wait for hardware to set RBNE bit */
        while (!i2c_flag_get(I2Cx, I2C_FLAG_RBNE)) {
          if (get_timer_value() > timeout_tick) {
            printf("RBNE timeout\n");
            return -1;
          }
        }

        /* reads DATA, RBNE is cleared automatically */
        *buffer = i2c_data_receive(I2Cx);

        /* update counter */
        buffer += sizeof(uint8_t);
        size -= 1;
      }
      else if (size == 2U) {
        /* step 5 for N=2 */
        while(!i2c_flag_get(I2Cx, I2C_FLAG_BTC)) {
          if (get_timer_value() > timeout_tick) {
            printf("BTC timeout\n");
            return -1;
          }
        }
        
        /* generate STOP condition */
        I2C_CTL0(I2Cx) |= I2C_CTL0_STOP;

        /* reads DATA twice */
        *buffer = i2c_data_receive(I2Cx);

        buffer += sizeof(uint8_t);
        size -= 1;
        
        *buffer = i2c_data_receive(I2Cx);
        
        buffer += sizeof(uint8_t);
        size -= 1;
      }
      else {
        /* N=3 (comes from normal receive which N>2, read the last N-2, N-1 & N byte here) */
        while(!i2c_flag_get(I2Cx, I2C_FLAG_BTC)) {
          if (get_timer_value() > timeout_tick) {
            printf("BTC timeout\n");
            return -1;
          }
        }
        
        /* disable ACK */
        I2C_CTL0(I2Cx) &= ~(I2C_CTL0_ACKEN);
        
        *buffer = i2c_data_receive(I2Cx);
        buffer += sizeof(uint8_t);
        size -= 1;

        while(!i2c_flag_get(I2Cx, I2C_FLAG_BTC)) {
          if (get_timer_value() > timeout_tick) {
            printf("BTC timeout\n");
            return -1;
          }
        }

        /* generate STOP condition */
        I2C_CTL0(I2Cx) |= I2C_CTL0_STOP;

        *buffer = i2c_data_receive(I2Cx);
        buffer += sizeof(uint8_t);
        size -= 1;

        *buffer = i2c_data_receive(I2Cx);
        buffer += sizeof(uint8_t);
        size -= 1;
      }
    }
    else {
      /* if size > 3 */
      while (!i2c_flag_get(I2Cx, I2C_FLAG_RBNE)) {
        if (get_timer_value() > timeout_tick) {
          printf("RBNE timeout\n");
          return -1;
        }
      }

      *buffer = i2c_data_receive(I2Cx);
      buffer += 1;
      size -= 1;

      if (i2c_flag_get(I2Cx, I2C_FLAG_BTC)) {
        *buffer = i2c_data_receive(I2Cx);
        buffer += 1;
        size -= 1;
      }
    }
  }
  return 0;
}

uint8_t HAL_I2C_masterTransmit(uint32_t I2Cx, uint16_t device_addr, uint8_t *buffer, uint16_t size, uint64_t timeout) {
  uint64_t timeout_tick = get_timer_value() + (TIMER_FREQ / 1000 * timeout);

  i2c_start_on_bus(I2Cx);

  while (!i2c_flag_get(I2Cx, I2C_FLAG_SBSEND)) {
    if (get_timer_value() > timeout_tick) {
      printf("SBSEND timeout\n");
      return -1;
    }
  }

  i2c_data_transmit(I2Cx, device_addr);
  i2c_flag_clear(I2Cx, I2C_FLAG_SBSEND);


  while (!i2c_flag_get(I2Cx, I2C_FLAG_ADDSEND)) {
    if (get_timer_value() > timeout_tick) {
      printf("ADDSEND timeout\n");
      return -1;
    }
  }
  i2c_flag_clear(I2Cx, I2C_FLAG_ADDSEND);

  
  /* transmit all the data */
  while (size > 0) {
    /* wait for TBE (transmit buffer empty) flag to be cleared */
    while (!i2c_flag_get(I2Cx, I2C_FLAG_TBE)) {
      if (get_timer_value() > timeout_tick) {
        printf("tbe timeout\n");
        i2c_stop_on_bus(I2Cx);

        return -1;
      }
    }

    /* write data to transmit register */
    i2c_data_transmit(I2Cx, *buffer);
    buffer += 1;
    size -= 1;

    /* if this is the first byte to be transmitted, ignore TBE status and transmit another one */
    if ((i2c_flag_get(I2Cx, I2C_FLAG_BTC)) && (size != 0U)) {
      /* write data to transmit register */
      i2c_data_transmit(I2Cx, *buffer);

      buffer += 1;
      size -= 1;
    }
  }

  while (!i2c_flag_get(I2Cx, I2C_FLAG_BTC)) {
    if (get_timer_value() > timeout_tick) {
      i2c_stop_on_bus(I2Cx);
      printf("BTC timeout\n");
      return -1;
    }
  }

  i2c_stop_on_bus(I2Cx);

  return 0;
}


uint8_t HAL_I2C_slaveTransmit(uint32_t I2Cx, uint8_t *buffer, uint16_t size, uint64_t timeout) {
  uint64_t timeout_tick = get_timer_value() + (TIMER_FREQ / 1000 * timeout);

  i2c_ack_config(I2Cx, I2C_ACK_ENABLE);

  /* TODO: If 10 bit addressing format
is selected, the I2C master should then send a repeated START(Sr) condition followed
by a header to the I2C bus. The slave sets ADDSEND bit again after it detects the
repeated START(Sr) condition and the following h eader. Software needs to clear the
ADDSEND bit again by reading I2C_STAT0 and then I2C_STAT1. */
  /* in 7-bit addr mode, only one ADDSEND need to be read and cleared */
  while (!i2c_flag_get(I2Cx, I2C_FLAG_ADDSEND)) {
    if (get_timer_value() > timeout_tick) {
      printf("addsend timeout %d\n", i2c_flag_get(I2Cx, I2C_FLAG_ADDSEND));
      return -1;
    }
  }
  i2c_flag_clear(I2Cx, I2C_FLAG_ADDSEND);

  /* transmit all the data */
  while (size > 0) {
    /* wait for TBE (transmit buffer empty) flag to be cleared */
    while (!i2c_flag_get(I2Cx, I2C_FLAG_TBE)) {
      if (get_timer_value() > timeout_tick) {
        printf("tbe timeout\n");
        return -1;
      }
    }

    /* write data to transmit register */
    i2c_data_transmit(I2Cx, *buffer);
    buffer += 1;
    size -= 1;

    /* if this is the first byte to be transmitted, ignore TBE status and transmit another one */
    if ((i2c_flag_get(I2Cx, I2C_FLAG_BTC)) && (size != 0U)) {
      /* write data to transmit register */
      i2c_data_transmit(I2Cx, *buffer);

      buffer += 1;
      size -= 1;
    }
  }

  while (!i2c_flag_get(I2Cx, I2C_FLAG_AERR)) {
    if (get_timer_value() > timeout_tick) {
      return -1;
      printf("aerr timeout\n");
    }
  }

  i2c_flag_clear(I2Cx, I2C_FLAG_AERR);

  i2c_ack_config(I2Cx, I2C_ACK_DISABLE);

  return 0;
}


/**
 * @brief
 *
 * example:
  '''
  uint8_t data[30];
  uint32_t size = 2;
  uint32_t timeout = 1000;
  uint8_t status = HAL_I2C_slaveReceive(&data, size, timeout);
  printf("%d  data: %d\t%d\n", status, data[0], data[1]);
  '''
 *
 * @param buffer
 * @param size
 * @param timeout
 * @return uint8_t
 */
uint8_t HAL_I2C_slaveReceive(uint32_t I2Cx, uint8_t *buffer, uint16_t size, uint64_t timeout) {
  uint64_t timeout_tick = get_timer_value() + (TIMER_FREQ / 1000 * timeout);

  i2c_ack_config(I2Cx, I2C_ACK_ENABLE);
  while (!i2c_flag_get(I2Cx, I2C_FLAG_ADDSEND)) {
    if (get_timer_value() > timeout_tick) {
      printf("addsend timeout %d\n", i2c_flag_get(I2Cx, I2C_FLAG_ADDSEND));
      return -1;
    }
  }

  i2c_flag_clear(I2Cx, I2C_FLAG_ADDSEND);

  while (size > 0) {
    while (!i2c_flag_get(I2Cx, I2C_FLAG_RBNE)) {
      if (get_timer_value() > timeout_tick) {
        return -1;
      }
    }
    /* read from income data register and clears flag for more income data */
    uint8_t c = i2c_data_receive(I2Cx);
    *buffer = c;
    buffer += 1;
    size -= 1;
  }

  while (!i2c_flag_get(I2Cx, I2C_FLAG_STPDET)) {
    if (get_timer_value() > timeout_tick) {
      return -1;
    }
  }

  /* resets all flags */
  i2c_flag_clear(I2Cx, I2C_FLAG_STPDET);
  i2c_ack_config(I2Cx, I2C_ACK_DISABLE);
  return 0;
}


实验效果,可以接另一个 STM32,但这里和 Arduino 通信,GD32 上实验最复杂的 Master Receive 功能

// main() { ... (initialization)

  while (1) {
    uint8_t statusR, statusT;
    uint8_t data[30];
    uint32_t size = 2;
    uint32_t timeout = 1000;
    
    data[0] = 1;
    data[1] = 2;

    // statusR = HAL_I2C_slaveReceive(I2C1, &data, size, timeout);
    // statusT = HAL_I2C_slaveTransmit(I2C1, data, 2, 1000);
    
    // statusT = HAL_I2C_masterTransmit(I2C1, SLAVE_ADDRESS, &data, size, timeout);
    statusR = HAL_I2C_masterReceive(I2C1, SLAVE_ADDRESS, &data, size, 1000);
    
    printf("%d %d data: %d\t%d\n", statusR, statusT, data[0], data[1]);
    HAL_delay(200);
  }
}  // end main()
/** ArduinoI2C.ino
 *
 * | Arduino | A4 | ---- SDA
 *           | A5 | ---- SCL
 *
 * | GD32V   | PB11 | ---- SDA
 *           | PB10 | ---- SCL
 */

#include 

#define MASTER_RECEIVER         0
#define MASTER_TRANSMITTER      1
#define SLAVE_RECEIVER          2
#define SLAVE_TRANSMITTER       3

#define MODE SLAVE_TRANSMITTER


#if MODE == MASTER_RECEIVER

void setup() {
  Serial.begin(115200);  // start serial for output
  Wire.setClock(100000);
  Wire.begin(0x01);        // join i2c bus as a master in 7-bit address mode
}
uint8_t slave_addr = 0x01;
void loop() {
  Serial.print("begin receving from slave: ");
  uint8_t n_bytes = 2;
  Wire.requestFrom(slave_addr, n_bytes);

  while (Wire.available() > 0) {
    uint8_t c = Wire.read();    // receive a byte as character
    Serial.print(c);         // print the character
    Serial.print(" ");
  }
  Serial.println("");
  delay(100);
}

#elif MODE == MASTER_TRANSMITTER

void setup() {
  Serial.begin(115200);  // start serial for output
  Wire.setClock(100000);
  Wire.begin(0x01);        // join i2c bus as a master in 7-bit address mode
}
byte x = 0;
uint8_t slave_addr = 0x01;
void loop() {
  Serial.print("begin transmission with x = ");
  Serial.println(x);
  Wire.beginTransmission(slave_addr);
  Wire.write(x);              // sends one byte
  //  Wire.write(x);              // sends one byte
  ////  delay(4);  
  Wire.write(255-x);              // sends one byte  
  Wire.endTransmission();    // stop transmitting

  x += 1;
  delay(100);
}



#elif MODE == SLAVE_RECEIVER
uint8_t device_addr = 0x01;
void setup() {
  Serial.begin(115200);           // start serial for output
  Wire.begin(device_addr);                // join i2c bus with address #4
  Wire.setClock(100000);
  Wire.onReceive(receiveEvent); // register event
}

void loop() {
  delay(100);
}

void receiveEvent(int howMany) {
  Serial.print("receive: ");
  Serial.print(howMany);
  Serial.print("\t");
  while(Wire.available() > 0) {
    uint8_t c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
    Serial.print(" ");
  }
  Serial.println("");
}


#elif MODE == SLAVE_TRANSMITTER
uint8_t device_addr = 0x01;
void setup() {
  Serial.begin(115200);
  Wire.begin(device_addr);                // join i2c bus with address #8
  Wire.setClock(100000);
  Wire.onRequest(requestEvent); // register event
  Serial.println("ready");
}

void loop() {
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Serial.println("replied 13..");

  Wire.write(10);
  Wire.write(15);
//  for (int i=0; i<2; i+=1){
//    Wire.write(13 + i);
//  }
}
#endif

示波器图形

GD32VF103 I2C 通讯_第1张图片
GD32VF103 I2C 通讯_第2张图片

你可能感兴趣的:(GD32VF103 I2C 通讯)