GD32VF103|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("%ddata: %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; }

【GD32VF103|GD32VF103 I2C 通讯】实验效果,可以接另一个 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_RECEIVER0 #define MASTER_TRANSMITTER1 #define SLAVE_RECEIVER2 #define SLAVE_TRANSMITTER3#define MODE SLAVE_TRANSMITTER#if MODE == MASTER_RECEIVERvoid 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_TRANSMITTERvoid 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 transmittingx += 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|GD32VF103 I2C 通讯
文章图片
GD32VF103|GD32VF103 I2C 通讯
文章图片

    推荐阅读