四、GD32 MCU 常见外设介绍 (7) 7.I2C 模块介绍

news2024/9/22 19:31:14

7.1.I2C 基础知识

I2C(Inter-Integrated Circuit)总线是一种由Philips公司开发的两线式串行总线,用于内部IC控制的具有多端控制能力的双线双向串行数据总线系统,能够用于替代标准的并行总线,连接各种集成 电路和功能模块。I2C器件能够减少电路间的连接,减少电路板的尺寸,降低硬件成本并提高系统的可靠性。I2C总线传输模式具有向下兼容性,传输速率标准模式下可达100kbps,快速模式下可 达400kbps,高速模式下可达3.4Mbps。

为了清楚起见,在此对I2C通信中关于设备的基本概念进行简要讲解。

① 发送设备:发送数据到总线上的设备。

② 接收设备:从总线上接收数据的设备。

③ 主设备:启动数据传输并产生时钟信号的设备。

④ 从设备:被主设备寻址的设备。

多主:多个主设备可以尝试在不破坏信息的前提下同时控制线。

同步:同步两个或更多设备之间的时钟信号的过程。

仲裁:如果超过一个主设备同时试图控制总线,只有一个主设备被允许,且获胜主设备的信息不被破坏。

(1)I2C设备连接原理 I2C设备连接示意图如设备连接示意图所示。I2C总线是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。在GD32 MCU与被控IC(集成电路)之间、IC与IC之间进行双向传送,最高传送速率1Mbps。各种设备均并联在总线上,两条总线都被上拉电阻上拉到VCC,所有设备地位对等,都可作为主机或从机,就像电话机一样只要拨通各自的号码就能正常工作,所以,每个设备都有唯一的地址。在信息的传输过程中,I2C总线上并接的每个设备既是主设备(或从设备),又是发送设备(或接收设备),这取决于它所要完成的功能。每个设备都可以把总线接地拉低,却不允许把总线电平直接连到VCC上置高。把总线电平拉低称为占用总线,总线电平为高等待被拉低则称为总线被释放。

I2C 设备连接示意图

由于SDA和SCL均为双向I/O线,都是开漏极端(输出1时,为高阻状态),因此I2C总线上的所有设备的SDA和SCL引脚都要外接上拉电阻。

(2)I2C数据通信协议

I2C数据通信时序图如I2C数据通信时序图所示。下面首先介绍起始位和停止位,起始位和停止位都是由主设备产生的,如图中虚线所示。当SCL时钟线为高电平时,SDA数据线上由高到低的跳变,产生一个开始信号,即起始位。当SCL时钟线为高电平时,SDA数据线上由低到高的跳变,将产生一个停止信号,即停止位。起始位之后,总线被认为忙,即有数据在传输,传输的第一个字节,即7位从地址和R/ ̄W 位。当R/ ̄W位为0时,主机向从机发送数据;当R/ ̄W位为1时,主机接收来自从机的数据。在每个字节后的第九个SCL时钟上,接收机发送ACK位。停止位之后,总线被认为闲,空闲状态时,SDA和SCL都是高电平。

注意:当SCL位为高电平时,SDA的数据必须保持稳定,否则,由于起始位和停止位的电气边沿特性,SDA上数据发生改变将被识别为起始位或停止位。所以,只有当SCL为低电平时才允许SDA上的数据改变。

I2C 数据通信时序图

 I2C总线上每位数据传输的示意图

(3)I2C的寻址方式 GD32 MCU的I2C模块支持7位和10位两种寻址模式,7位寻址模式最多寻址128个设备,10位寻址模式最多寻址1024个设备。I2C总线理论上可以允许的最大设备数是以总线上所有器件的电容总和不超过400pF为限(其中,包括连线本身的电容和其连接端的引出等效电容),总线上所有器件要依靠SDA发送的地址信号寻址,不需要片选信号。

① 7位寻址模式

如图下图所示为7位地址方式下的I2C数据传输格式,第一个字节由7位从地址和R/ ̄W读/写位组成。不论总线上传送的是地址还是数据信息,每个字节传输完毕,接收设备都会发送响应位(ACK)。地址类信息传输之后是数据信息,直到接收到停止信息。

7 位寻址模式数据格式

② 10位寻址模式

如下图所示为10位地址方式下的I2C数据传输格式。第一个字节由二进制位11110、从地址的最高两位及R/ ̄W读/写控制位组成。第一个字节传输完毕后是ACK响应位。第二个字节就是10位从地址的低8位,后面是响应位和数据。

10 位寻址模式数据格式

③ 二次发送从地址模式(重复产生起始条件)

主机可以在不停止数据传输的情况下,通过产生重复的起始条件,改变SDA上数据流的方向,这称为RESTART。再次发送起始信号后,需重新发送从地址和R/ ̄W读/写控制位。重新产生起始条件数据传输格式如图所示。

7.2.GD32 I2C 外设原理简介

因篇幅有限,本文无法详细介绍GD32所有系列I2C外设接口,下面以GD32F30x为列,着重介绍下GD32F30x的I2C外设简介和结构框图,后介绍下各个系列的差异。

GD32 I2C 主要特性

GD32F30X系列I2C 接口模块实现了 I2C 协议的标速模式,快速模式以及快速+ 模式,具备CRC 计算和校验功能、支持 SMBus(系统管理总线) 和 PMBus(电源管理总线),此外还支持多主机 I2C 总线架构。 I2C 接口模块也支持 DMA 模式,可有效减轻 CPU 的负担。

GD32 MCU I2C模块主要特性描述如下:

◼ 并行总线至 I2C 总线协议的转换及接口;

◼ 同一接口既可实现主机功能又可实现从机功能;

◼ 主从机之间的双向数据传输;

◼ 支持 7 位和 10 位的地址模式和广播寻址;

◼ 支持 I2C 多主机模式;

◼ 支持标速(最高 100 KHz),快速(最高 400 KHz) 和快速+ 模式(最高 1MHz);

◼ 从机模式下可配置的 SCL 主动拉低;

◼ 支持 DMA 模式;

◼ 兼容 SMBus 2.0 和 PMBus;

◼ 两个中断:字节成功发送中断和错误事件中断;

◼ 可选择的 PEC(报文错误校验) 生成和校验;

I2C 结构框图介绍

I2C内部结构框图如下图所示,该结构框图可分为五个部分:1、用于产生I2C通信时序;2、用于收发I2C数据,当有数据需要发送时,会首先将数据填充到数据寄存器,然后数据被自动移位到移位寄存器,通过SDA引脚发送出去,当有数据需要接受时,首先会根据SCL选择的时钟边沿在移位寄存器中锁存SDA数据,当数据接受到后,数据被移到数据缓冲寄存器,并置位接受缓冲区非空标志;3、用于收发数据CRC计算;4、用于I2C模块控制及相关标志位查询;5、系统通过APB总线对I2C数据寄存器及控制寄存器进行操作。

 

各系列 I2C 功能差异

GD32各系列MCU有关IIC功能差异如各系列I2C功能差异表所示。

7.3.硬件连接说明

如AT24C02C EEPROM IIC接口参考电路图所示,AT24C02C为IIC接口的EEPROM,该电路图为其典型参考电路,其中5脚为I2C SDA引脚,6脚为I2C SCL引脚,I2C总线需要通过4.7K欧姆电阻上拉。

 

7.4.软件配置说明

本小节讲解I2C_Example下的I2C0主机历程,本例程讲解IIC作为主机情况下对从机的读写,并引入超时恢复机制。

IIC 初始化配置

IIC初始化配置代码如代码清单I2C初始化配置所示,首先进行GPIO初始化,然后对IIC外设进行初始化。注意本例程仅讲解IIC0的外设引脚及模块初始化,若其他IIC模块可参考修改。

void I2C_init(uint32_t I2Cx)
{ 
 GPIO_Configuration_I2C(I2Cx);
 i2c_clock_config(I2Cx, 400000, I2C_DTCY_2);
 /* I2C address configure */
 i2c_mode_addr_config(I2Cx, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0);
 /* enable acknowledge */
 i2c_ack_config(I2Cx, I2C_ACK_DISABLE);
 /* enable I2Cx */
 i2c_enable(I2Cx);
}

时钟及 GPIO 引脚配置

时钟及GPIO引脚配置如代码清单I2C时钟及GPIO引脚配置所示,在例程中PB6、PB7引脚需要配置为复用开漏模式。

void GPIO_Configuration_I2C(uint32_t I2Cx)
{ 
uint32_t GPIO_SDA;
uint32_t GPIO_SCL;
 uint32_t GPIO_Pin_SDA,GPIO_Pin_SCL;
rcu_periph_reset_enable(RCU_I2C0RST);
rcu_periph_reset_disable(RCU_I2C0RST);
#if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X || GD32F1X0 || GD32F4XX || GD32F3X0 || 
GD32E23X
 /* enable GPIOB clock */
 rcu_periph_clock_enable(RCU_GPIOB);
 /* enable I2C0 clock */
 rcu_periph_clock_enable(RCU_I2C0);
#if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X
rcu_periph_clock_enable(RCU_AF);
#elif defined GD32F1X0 || GD32F4XX || GD32F3X0 || GD32E23X
#endif
 GPIO_SCL=GPIOB;
GPIO_Pin_SCL=GPIO_PIN_6;
GPIO_SDA=GPIOB;
GPIO_Pin_SDA=GPIO_PIN_7;
#endif
 /* Reset I2C1 IP */
// I2C_DeInit(I2Cx);
#if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X
 /* I2C0 GPIO ports */
 /* connect PB6 to I2C0_SCL */
 gpio_init(GPIO_SCL, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SCL);
 /* connect PB7 to I2C0_SDA */
 gpio_init(GPIO_SDA, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SDA);
#elif defined GD32F1X0 || GD32F4XX || GD32F3X0 || GD32E23X
#if defined GD32F1X0 || GD32F3X0 || GD32E23X
 /* I2C GPIO ports */
 /* connect I2C_SCL_GPIO_PIN to I2C_SCL */
 gpio_af_set(GPIO_SCL, GPIO_AF_1, GPIO_Pin_SCL);
 /* connect I2C_SDA_GPIO_PIN to I2C_SDA */
 gpio_af_set(GPIO_SDA, GPIO_AF_1, GPIO_Pin_SDA);
#elif defined GD32F4XX
 gpio_af_set(GPIO_SCL, GPIO_AF_4, GPIO_Pin_SCL);
 gpio_af_set(GPIO_SDA, GPIO_AF_4, GPIO_Pin_SDA);
#endif
 gpio_mode_set(GPIO_SCL, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_Pin_SCL);
 gpio_output_options_set(GPIO_SCL, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SCL);
 gpio_mode_set(GPIO_SDA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_Pin_SDA);
 gpio_output_options_set(GPIO_SDA, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SDA);
#endif 
}

I2C 多字节写操作

I2C多字节写操作如代码清单IIC写多字节操作所示,该函数接口实现IIC外设对IIC从机的多字节写操作。

/*!
 \brief I2Cx Write NBytes 
 \param[in] i2c_periph : I2Cx(x=0,1)
 \param[in] addr : slave address 
 \param[in] start_Addr : reg
 \param[in] number_Bytes: number to Write
 \param[in] ADDR_Length : number of the addr
*/
I2C_Status I2Cx_Write_NBytes(uint32_t I2Cx,uint8_t driver_Addr, uint16_t start_Addr, uint8_t number_Bytes, uint8_t 
*write_Buffer,uint8_t ADDR_Length)
{
 uint32_t I2C_Timeout = I2C_SHORT_TIMEOUT;
i2c_ack_config(I2Cx,I2C_ACK_ENABLE);
 while(i2c_flag_get(I2Cx, I2C_FLAG_I2CBSY))
 {
 if((I2C_Timeout--) == 0)
 {
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
i2c_start_on_bus(I2Cx);
I2C_Timeout = I2C_SHORT_TIMEOUT;
 while(!i2c_flag_get(I2Cx, I2C_FLAG_SBSEND))
 {
 if((I2C_Timeout--) == 0)
 {
 Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
 i2c_master_addressing(I2Cx, driver_Addr, I2C_TRANSMITTER);
I2C_Timeout = I2C_SHORT_TIMEOUT;
 while(!i2c_flag_get(I2Cx, I2C_FLAG_ADDSEND))
 {
 if((I2C_Timeout--) == 0)
 {
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
 i2c_flag_clear(I2Cx,I2C_FLAG_ADDSEND);
 I2C_Timeout = I2C_SHORT_TIMEOUT;
 while(SET != i2c_flag_get( I2Cx , I2C_FLAG_TBE ))
 {
 if((I2C_Timeout--) == 0)
 {
 Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
 i2c_enable(I2Cx);
if(ADDR_Length)//Á½×Ö½ÚµØÖ·
{
i2c_data_transmit(I2Cx, (uint8_t)((start_Addr & 0xFF00) >> 8)); 
I2C_Timeout = I2C_SHORT_TIMEOUT;
while(!i2c_flag_get(I2Cx, I2C_FLAG_BTC))
{
if((I2C_Timeout--) == 0)
{
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
return I2C_FAIL;
}
}
i2c_data_transmit(I2Cx, (uint8_t)(start_Addr & 0x00FF)); 
 }
else
{
i2c_data_transmit(I2Cx, start_Addr);
}
I2C_Timeout = I2C_SHORT_TIMEOUT;
 while(!i2c_flag_get(I2Cx, I2C_FLAG_BTC))
 {
 if((I2C_Timeout--) == 0)
 {
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
while(number_Bytes)
 {
 i2c_data_transmit(I2Cx, *write_Buffer); 
 I2C_Timeout = I2C_SHORT_TIMEOUT;
 //while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//5
// while(!i2c_flag_get(I2Cx, I2C_BTC))//
 while(!i2c_flag_get(I2Cx, I2C_FLAG_TBE))
{
if((I2C_Timeout--) == 0)
{
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
return I2C_FAIL;
}
}

 /* point to the next location where the byte read will be saved */
 write_Buffer++; 

 /* decrement the read bytes counter */
 number_Bytes--;
 } 

// while(!i2c_flag_get(I2C1, I2C_BTC))
// {
// if((I2C_Timeout--) == 0)
// {
// Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
// return I2C_FAIL;
// }
// }
/* send a stop condition to I2C bus */
 i2c_stop_on_bus(I2Cx);
 I2C_Timeout = I2C_SHORT_TIMEOUT;
 while (I2C_CTL0(I2Cx) & 0x0200)
 {
 if((I2C_Timeout--) == 0)
 {
 Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }

i2c_ack_config(I2Cx,I2C_ACK_ENABLE);
 return I2C_OK;
}

IIC 多字节读操作

IIC多字节读操作如代码清单IIC多字节读操作所示,该函数接口可实现对IIC从机的多字节读功能。

I2C_Status I2Cx_Read_NBytes(uint32_t I2Cx,uint8_t driver_Addr, uint16_t start_Addr, uint8_t number_Bytes, uint8_t 
*read_Buffer,uint8_t ADDR_Length)
{
 uint32_t I2C_Timeout = I2C_SHORT_TIMEOUT;
i2c_ack_config(I2Cx,I2C_ACK_ENABLE);
 while(i2c_flag_get(I2Cx, I2C_FLAG_I2CBSY))
 {
 if((I2C_Timeout--) == 0)
 {
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
 if(number_Bytes==2)
{
 i2c_ackpos_config(I2Cx,I2C_ACKPOS_NEXT);
 }
 i2c_start_on_bus(I2Cx);
I2C_Timeout = I2C_SHORT_TIMEOUT;
 while(!i2c_flag_get(I2Cx, I2C_FLAG_SBSEND))
 {
 if((I2C_Timeout--) == 0)
 {
 Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
i2c_master_addressing(I2Cx, driver_Addr, I2C_TRANSMITTER);
I2C_Timeout = I2C_SHORT_TIMEOUT;
 while(!i2c_flag_get(I2Cx, I2C_FLAG_ADDSEND))
 {
 if((I2C_Timeout--) == 0)
 {
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
 /* clear the ADDSEND bit */
 i2c_flag_clear(I2Cx,I2C_FLAG_ADDSEND);
 I2C_Timeout = I2C_SHORT_TIMEOUT;
 while(SET != i2c_flag_get( I2Cx , I2C_FLAG_TBE ))
 {
 if((I2C_Timeout--) == 0)
 {
 Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
 i2c_enable(I2Cx);
if(ADDR_Length)//Á½×Ö½ÚµØÖ·
{

i2c_data_transmit(I2Cx, (uint8_t)((start_Addr & 0xFF00) >> 8)); 
I2C_Timeout = I2C_SHORT_TIMEOUT;
//while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING))
while(!i2c_flag_get(I2Cx, I2C_FLAG_BTC)) 
{
if((I2C_Timeout--) == 0)
{
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
return I2C_FAIL;
}
}
i2c_data_transmit(I2Cx, (uint8_t)(start_Addr & 0x00FF)); 
 }
else
{
i2c_data_transmit(I2Cx, start_Addr);
}
I2C_Timeout = I2C_SHORT_TIMEOUT;
 while(!i2c_flag_get(I2Cx, I2C_FLAG_BTC))
 {
 if((I2C_Timeout--) == 0)
 {
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
 i2c_start_on_bus(I2Cx);
I2C_Timeout = I2C_SHORT_TIMEOUT;
 while(!i2c_flag_get(I2Cx, I2C_FLAG_SBSEND))
 {
 if((I2C_Timeout--) == 0)
 {
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
i2c_master_addressing(I2Cx, driver_Addr, I2C_RECEIVER);
I2C_Timeout = I2C_SHORT_TIMEOUT;
if(number_Bytes<3)
{
 i2c_ack_config(I2Cx,I2C_ACK_DISABLE);
 }
 while(!i2c_flag_get(I2Cx, I2C_FLAG_ADDSEND))
 {
 if((I2C_Timeout--) == 0)
 {
Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
return I2C_FAIL;
 }
 }
 /* clear the ADDSEND bit */
 i2c_flag_clear(I2Cx,I2C_FLAG_ADDSEND);
 if(number_Bytes==1)
{
i2c_stop_on_bus(I2Cx);
 }
while(number_Bytes)
 {
 if(3 == number_Bytes){
 /* wait until BTC bit is set */

 I2C_Timeout = I2C_LONG_TIMEOUT;
 while(!i2c_flag_get(I2Cx, I2C_FLAG_BTC))
 {
 if((I2C_Timeout--) == 0)
 {
 Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }

 /* disable acknowledge */
 /* disable acknowledge */
 i2c_ack_config(I2Cx,I2C_ACK_DISABLE);
 }
 if(2 == number_Bytes){
 /* wait until BTC bit is set */
 I2C_Timeout = I2C_LONG_TIMEOUT;
 while(!i2c_flag_get(I2Cx, I2C_FLAG_BTC))
 {
 if((I2C_Timeout--) == 0)
 {
 Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }

 /* send a stop condition to I2C bus */
 i2c_stop_on_bus(I2Cx);
 I2C_Timeout = I2C_SHORT_TIMEOUT;
 while (I2C_CTL0(I2Cx) & 0x0200)
 {
 if((I2C_Timeout--) == 0)
 {
 Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }
 }

 /* wait until the RBNE bit is set and clear it */
 if(i2c_flag_get(I2Cx, I2C_FLAG_RBNE)){
 /* read a byte from the EEPROM */
 *read_Buffer = i2c_data_receive(I2Cx);
/* point to the next location where the byte read will be saved */
 read_Buffer++; 

 /* decrement the read bytes counter */
 number_Bytes--;
 } 
 }
 while(I2C_CTL0(I2Cx)&0x0200)
 {
 if((I2C_Timeout--) == 0)
 {
 Resume_IIC(I2C_LONG_TIMEOUT,I2Cx);
 return I2C_FAIL;
 }
 }

/* enable acknowledge */
 i2c_ack_config(I2Cx,I2C_ACK_ENABLE);

 i2c_ackpos_config(I2Cx,I2C_ACKPOS_CURRENT);
 return I2C_OK;
}

IIC 超时恢复机制

IIC超时恢复机制实现如代码清单IIC超时恢复机制所示。

uint32_t I2C_Timeout;
void Delay_I2C(uint32_t i)
{
 while(i--);
}
void Resume_IIC(uint32_t Timeout,uint32_t I2Cx )
{
uint32_t GPIO_SDA;
uint32_t GPIO_SCL;
 uint32_t GPIO_Pin_SDA,GPIO_Pin_SCL;
#if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X || GD32F1X0 || GD32F4XX || GD32F3X0 || 
GD32E23X
 /* enable GPIOB clock */
 rcu_periph_clock_enable(RCU_GPIOB);
 /* enable I2C0 clock */
 rcu_periph_clock_enable(RCU_I2C0);
#if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X
rcu_periph_clock_enable(RCU_AF);
#elif defined GD32F1X0 || GD32F4XX || GD32F3X0 || GD32E23X
#endif
#endif
GPIO_SCL=GPIOB;
GPIO_Pin_SCL=GPIO_PIN_6;
GPIO_SDA=GPIOB;
GPIO_Pin_SDA=GPIO_PIN_7;
 do{
#if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X
/* I2C0 GPIO ports */
/* connect PB6 to I2C0_SCL */
gpio_init(GPIO_SCL, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SCL);
/* connect PB7 to I2C0_SDA */
gpio_init(GPIO_SDA, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SDA);
#elif defined GD32F1X0 || GD32F4XX || GD32F3X0 || GD32E23X
#if defined GD32F1X0 || GD32F3X0 || GD32E23X
/* I2C GPIO ports */
/* connect I2C_SCL_GPIO_PIN to I2C_SCL */
gpio_af_set(GPIO_SCL, GPIO_AF_1, GPIO_Pin_SCL);
/* connect I2C_SDA_GPIO_PIN to I2C_SDA */
gpio_af_set(GPIO_SDA, GPIO_AF_1, GPIO_Pin_SDA);
#elif defined GD32F4XX
gpio_af_set(GPIO_SCL, GPIO_AF_4, GPIO_Pin_SCL);
gpio_af_set(GPIO_SDA, GPIO_AF_4, GPIO_Pin_SDA);
#endif
gpio_mode_set(GPIO_SCL, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_Pin_SCL);
gpio_output_options_set(GPIO_SCL, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SCL);
gpio_mode_set(GPIO_SDA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_Pin_SDA);
gpio_output_options_set(GPIO_SDA, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SDA);
#endif 
gpio_bit_reset(GPIO_SCL, GPIO_Pin_SCL);
 Delay_I2C(20);
gpio_bit_reset(GPIO_SDA, GPIO_Pin_SDA);
Delay_I2C(20);
gpio_bit_set(GPIO_SCL, GPIO_Pin_SCL);
Delay_I2C(20);
gpio_bit_set(GPIO_SDA, GPIO_Pin_SDA);
 Delay_I2C(20);
 if(Timeout-- == 0) return;
}while((!gpio_input_bit_get(GPIO_SDA, GPIO_Pin_SDA))&(!gpio_input_bit_get(GPIO_SCL, GPIO_Pin_SCL)));
I2C_init(I2Cx);
}

主函数说明

本例程主函数如代码清单I2C例程主函数所示。

int main(void)
{
 I2C_init(I2C0);
I2Cx_Write_NBytes(I2C0,0xA0, 0,8, Write_Buf,0);
 I2Cx_Read_NBytes(I2C0,0xA0, 0,8, Read_Buf,0);
 while (1)
 { 
 }

7.5.I2C 使用注意事项

  1. I2C总线需要上拉;

  2. I2C引脚需要配置为复用开漏模式;

  3. 若采用查询方式进行I2C数据传输,有可能会由于总线干扰,导致I2C卡死,可以在查询方式上增加超时机制,如果超时重配IIC恢复总线通信(注意重配IIC时,建议先将I2C模块Deinit,然后 在调用Init函数进行初始化)。

  4. 若采用软件模拟IIC的方式,在移植过程中出现问题,可能是由于代码执行效率的问题,可以排查软件延迟时间和其他芯片上的软件延迟时间是否相同,可以通过调整软件延迟时间进行测试;或者有可能是由于初始化配置IO端口的时候可能会引入干扰,可以先配置IO口输出高,然后再配置为推挽或开漏模式。

本章内容每日持续更新,如有兴趣,请关注收藏

更多GD32 MCU相关咨询:https://www.gd32bbs.com/ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1946833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Optima: 一个用于 Tapestri 平台的单细胞多组学数据分析的开源 R 包

分子条形码技术的最新进展使得在单细胞水平进行下一代转录组测序成为可能&#xff0c;例如10 Genomics Chromium和DropSeq。此外&#xff0c;CITE-seq 的出现使得可以在对单个细胞进行转录组分析的基础上同时对表面蛋白进行分析。同时&#xff0c;为了表征 DNA 和蛋白质谱&…

Harmony Next -- 图片选择库:宫格展示、全屏预览

hm_image_select_view OpenHarmony三方库中心仓&#xff1a;https://ohpm.openharmony.cn/#/cn/detail/image_select_view 介绍 Harmony Next 图片选择库&#xff0c;可设置最大选择数量、单行显示数量、横向竖向间隔&#xff1b;点击图片后全屏预览 软件架构 Harmony nex…

云计算复习--虚拟化技术

文章目录 虚拟化技术定义与原理虚拟机监视器&#xff08;VMM&#xff09;虚拟化技术服务器虚拟化存储虚拟化网络虚拟化应用虚拟化 关键技术新型虚拟化技术发展进展作业 虚拟化技术定义与原理 定义&#xff1a;虚拟化技术是一种将计算机物理实体&#xff08;如服务器、存储设备…

NOIP图论 最小生成树——Prim算法(详细图解)

最小生成树的概念 经典题目 prim算法简介 prim算法解析 &#xff08;详细图解&#xff09; 代码实现 代码实战 最小生成树的概念 在一给定的无向图G (V, E) 中&#xff0c;(u, v) 代表连接顶点 u 与顶点 v 的边&#xff0c;而 w(u, v) 代表此的边权重&#xff0c;若存在 …

AI绘画进阶工具 ComfyUI 新版来啦!操作界面详解!取消悬浮面板,自带工作流管理功能!(附安装包)

大家好&#xff0c;我是画画的小强 在 7 月初的一次更新中&#xff0c;ComfyUI 官方推出了 Beta 版 UI&#xff0c;取消了原本的悬浮面板&#xff0c;还新增了工作流管理功能&#xff0c;整体使用体验比之前好了很多。今天就为大家详细介绍一些新版 UI 的特点和用法。 一、启…

GraphRAG + GPT-4o mini 低成本构建 AI 图谱知识库

更好的效果&#xff0c;更低的价格&#xff0c;听起来是不是像梦呓&#xff1f; 限制 首先&#xff0c;让我们来介绍一个词&#xff1a;RAG。 简单来说&#xff0c;RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09; 的工作原理是将大型文档…

每日一题 二叉树的中序遍历

1.题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 2.题目思路 遇到二叉树问题,首先考虑用递归来实现,首先它是中序遍历.我们可以拆分成子问题来解决,即先遍历二叉树的左子树,在遍历自身,在遍历右子树即可 3.代码书写 List<Integer> list …

免杀笔记 -->API的整理Shellcode加密(过DeFender)

最近更新频率明显下降我懒&#xff0c;那么今天就来记录一下我们的一些常用的API的整理以及ShellCode的加密。 1.WinAPI整理 问我为什么要整理&#xff1f; 就是用起来的时候要左翻右翻 &#xff1a;&#xff1a; 烦死了 1.VirtualAlloc VirtualAlloc(NULL,sizeof(buf),MEM_…

声音克隆一键本地化部署 GPT-SoVITS

文章目录 GPT-SoVITS 介绍1:GPT-SoVITS安装2:GPT-SoVITS使用2.1 人声伴奏分离,去混响去延时工具2.2 语音切分工具2.3 语音降噪工具2.4 中文批量离线ASR工具2.5 语音文本校对标注工具GPT-SoVITS 介绍 GPT-SoVITS: 是一个由RVC变声器创始人“花儿不哭”推出的免费开源项目。…

php 做一个mqtt按钮,发布触发信号

在之前博客php 做一个文件下载服务器&#xff0c;得避免跨路径工具&#xff0c;安全很重要 中加了一个按钮&#xff0c;触发物联网设备返回数据。基于mqtt开发&#xff0c;如果想知道mqtt如何搭建&#xff0c;可以看我的博客【MQTT&#xff08;1&#xff09;】服务端的搭建 效…

JavaEE - HTTP状态码

Web服务器 浏览器和服务器两端进行数据交互&#xff0c;使用的就是HTTP协议&#xff08;HTTP客⼾端和 HTTP服务器之间的交互数据 的格式&#xff09;。 Web服务器就是对HTTP协议进⾏封装,程序员不需要直接对协议进⾏操作(⾃⼰写代码去解析http协议 规则)&#xff0c;让Web开发更…

2 YOLO8的使用

1 介绍 YOLOv8是YOLO (You Only Look Once) 目标检测模型系列的最新版本&#xff0c;由Ultralytics公司开发和维护。YOLOv8是在先前版本的基础上进行的重大更新&#xff0c;不仅提升了性能&#xff0c;还增加了更多的功能&#xff0c;它不仅能够进行目标检测&#xff0c;还能完…

构建智慧水利系统,优化水资源管理:结合物联网、云计算等先进技术,打造全方位、高效的水利管理系统,实现水资源的最大化利用

本文关键词&#xff1a;智慧水利、智慧水利工程、智慧水利发展前景、智慧水利技术、智慧水利信息化系统、智慧水利解决方案、数字水利和智慧水利、数字水利工程、数字水利建设、数字水利概念、人水和协、智慧水库、智慧水库管理平台、智慧水库建设方案、智慧水库解决方案、智慧…

RBAC权限管理设置

RBAC权限管理设置 RBAC&#xff08;Role-Based Access Control&#xff0c;基于角色的访问控制&#xff09;是一种常用的访问控制机制&#xff0c;用于管理系统中的用户权限。RBAC通过将用户分配给角色&#xff0c;并将权限授予角色&#xff0c;从而控制用户对资源的访问。 基…

BUU [BSidesCF 2020]Cards

BUU [BSidesCF 2020]Cards 开题&#xff1a; 做题目之前先了解一下21点的规则&#xff1a; 二十一点玩法规则和概率在二十一点游戏中&#xff0c;拥有最高点数的玩家获胜&#xff0c;其点数必须等于或低于21点&#xff1b;超过21点的玩家称为爆牌。 2点至10点的牌以牌面的点数…

谷歌新的网站索引策略:将来可能不再为您的网站编制索引

10 年前&#xff0c;在 WordPress 上推出新博客的同时&#xff0c;Google 上的内容几乎是即时索引。 搜索引擎试图尽快向用户提供所有信息&#xff0c;这对内容创作者也有利。 然而&#xff0c;随着时间的推移&#xff0c;情况发生了变化&#xff0c;现在谷歌在索引方面变得极…

RPA软件-影刀使用

流程自动化 影刀将操作进行抽象&#xff0c;分为一下几个对象&#xff1a; 网页自动化 &#xff08;1&#xff09; 网页自动化应用场景&#xff1a;网页操作、数据抓取 &#xff08;2&#xff09; 网页操作&#xff1a;基础操作-指令操作&#xff0c;智能操作-关联元素&#…

Redis-数据的极速之旅(一)

Redis基础篇 Redis的自我介绍我的核心数据结构1.字符串&#xff08;String&#xff09;2.哈希&#xff08;Hash&#xff09;3.列表&#xff08;List&#xff09;4.集合&#xff08;Set&#xff09;5.有序集合&#xff08;Sorted Set&#xff09; 高性能原理1.Redis为什么快&…

C# VS2019 Form 图标的修改

一、 窗口的左上角图标 1、Form的属性中找到Icon项直接选择图标&#xff08;.ico&#xff09;路径,窗口左上角会自动更新选择更新的图标。 Form属性 2.发布后的exe图标

日常开发记录分享-SQL中的partition分区功能使用

文章目录 需求来源实现思路实施SQL 语句结构内部查询&#xff08;子查询&#xff09;外部查询 结果 partition的升级使用解释 验证一下看看分区 分区的一些操作1. 普通查询2. 分区表上的查询优化3. 插入数据4. 删除分区中的数据5. 分区维护操作添加新的分区删除分区重组分区 6.…