美文网首页
GD32VF103 I2C 通讯

GD32VF103 I2C 通讯

作者: T_K_233 | 来源:发表于2021-01-05 10:13 被阅读0次

    通讯原理和接线参考 这篇文章。本文重点介绍代码,并以 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 <Wire.h>
    
    #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 通讯

          本文链接:https://www.haomeiwen.com/subject/ugjioktx.html