一、前言
? ? ? ? 最近需要用到GD32的I2C通信,雖然是第一次做I2C通信,但是GD32完整的標準庫有現存的I2C通信示例,雖然示例是EEPROM的通信,但是調用的函數應該是大差不差,所以上手比較簡單,這里簡單記錄一下筆記,方便下次使用。
二、GD32與SD2068 的連接
-
SDA:GD32的I2C數據線(我選用的PB10)。
-
SCL:GD32的I2C時鐘線(我選用的PB11)。
三、I2C初始化
宏定義和頭文件:
#include "gd32e23x.h" //已經包含了gd32e23x_i2c.h#define I2C_PERIPH I2C1
#define I2C_SPEED 100000
#define I2C_BUS_ADDRESS 0x32
#define I2C_RCUX RCU_I2C1
#define I2C_RCU_GPIOX RCU_GPIOB
#define I2C_SCL_PIN GPIO_PIN_10
#define I2C_SDA_PIN GPIO_PIN_11
1、GPIO初始化
void i2c_gpio_config(void)
{/* 時鐘初始化 */rcu_periph_clock_enable(I2C_RCU_GPIOX);/* I2C_SCL 引腳復用 */gpio_af_set(GPIOB, GPIO_AF_1, I2C_SCL_PIN);/* I2C_SDA 引腳復用 */gpio_af_set(GPIOB, GPIO_AF_1, I2C_SDA_PIN);/* 初始化GPIO復用功能模式 */gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN);gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN);gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
}
?2、I2C功能初始化
void i2c_config(void)
{// 啟用 I2C 外設的時鐘rcu_periph_clock_enable(I2C_RCUX);// 配置 I2C 的時鐘參數:// I2C_PERIPH:這里使用的I2C1// I2C_SPEED:通信速率(單位為 Hz,常用為 100000 或 400000),這里使用100000i2c_clock_config(I2C_PERIPH, I2C_SPEED, I2C_DTCY_2);// 配置 I2C 工作模式和地址:// I2C_ADDFORMAT_7BITS:使用 7 位地址模式// I2C_BUS_ADDRESS:SD2068器件代碼0110010 = 0x32i2c_mode_addr_config(I2C_PERIPH, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, I2C_BUS_ADDRESS);// 啟用 I2C 外設i2c_enable(I2C_PERIPH);// 使能 ACK 應答功能,確保在接收數據后自動發送 ACKi2c_ack_config(I2C_PERIPH, I2C_ACK_ENABLE);
}
四、I2C寫實現
? ? ? ? 主要過程就是:空閑->發送START 信號->發送設備、寄存器地址和方向->發送數據。封裝函數如下:
void i2c_write_multi(uint8_t reg_addr, uint8_t *data, uint8_t dataSize)
{// 等待 I2C 總線空閑,防止沖突while(i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY));// 發送 START 信號,起始 I2C 通信i2c_start_on_bus(I2C_PERIPH);while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_SBSEND));// 發送設備地址和寫入方向(最低位0)i2c_master_addressing(I2C_PERIPH, I2C_BUS_ADDRESS << 1, I2C_TRANSMITTER);//i2c_data_transmit(I2C_PERIPH, I2C_BUS_ADDRESS << 1);while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_ADDSEND));// 清除地址發送標志位i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);// 發送寄存器地址(子地址)i2c_data_transmit(I2C_PERIPH, reg_addr);while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_TBE));// 開始循環寫入多個數據字節while(dataSize--) {delay_1ms(5);i2c_data_transmit(I2C_PERIPH, *data++);while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_TBE));}// 發送 STOP 信號,結束通信i2c_stop_on_bus(I2C_PERIPH);while(I2C_CTL0(I2C_PERIPH) & I2C_CTL0_STOP);
}
五、I2C讀實現
? ? ? ? 通信過程與寫類似,直接上實現代碼:
void i2c_read_multi(uint8_t reg_addr, uint8_t *data, uint8_t dataSize)
{// 等待 I2C 總線空閑while(i2c_flag_get(I2C_PERIPH, I2C_FLAG_I2CBSY));// 發送起始信號i2c_start_on_bus(I2C_PERIPH);while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_SBSEND));// 發送設備地址i2c_master_addressing(I2C_PERIPH, I2C_BUS_ADDRESS << 1, I2C_TRANSMITTER);while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_ADDSEND));//清地址標志i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);// 寫入要讀取的寄存器地址(子地址)i2c_data_transmit(I2C_PERIPH, reg_addr);while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_TBE));while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_BTC));// 第二次起始信號,重新啟動為“讀模式”i2c_start_on_bus(I2C_PERIPH);while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_SBSEND));// 發送設備地址,讀方向(1)i2c_master_addressing(I2C_PERIPH, I2C_BUS_ADDRESS << 1, I2C_RECEIVER);while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_ADDSEND));i2c_flag_clear(I2C_PERIPH, I2C_FLAG_ADDSEND);// 如果只讀取1個字節,提前關閉ACK,準備發送STOPif (dataSize == 1) {i2c_ack_config(I2C_PERIPH, I2C_ACK_DISABLE);i2c_stop_on_bus(I2C_PERIPH);}// 讀取多個字節循環for (uint8_t i = 0; i < dataSize; i++) {delay_1ms(5);if (i == dataSize - 2) {// 讀取即將完成,關閉ACK,準備發送STOPi2c_ack_config(I2C_PERIPH, I2C_ACK_DISABLE);i2c_stop_on_bus(I2C_PERIPH);}while(!i2c_flag_get(I2C_PERIPH, I2C_FLAG_RBNE));data[i] = i2c_data_receive(I2C_PERIPH);}// 等待 STOP 傳輸完畢while(I2C_CTL0(I2C_PERIPH) & I2C_CTL0_STOP);// 恢復 ACK 設置(便于下次通信)i2c_ack_config(I2C_PERIPH, I2C_ACK_ENABLE);i2c_ackpos_config(I2C_PERIPH, I2C_ACKPOS_CURRENT);
}