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