GD32F103硬件I2C0通讯

news2024/12/24 21:47:11

GD32F103的I2C模块有I2C0和I2C1;本程序使用I2C0功能模块;

I2C0引脚复用和重映射:
当I2C0_REMAP=0时,I2C0引脚复用功能,I2C0_SCL映射到PB6引脚,I2C0_SDA映射到PB7引脚;
当I2C0_REMAP=1时,I2C0引脚重映射,I2C0_SCL映射到PB8引脚,I2C0_SDA映射到PB9引脚;

I2C1引脚只有复用引脚:
I2C1_SCL映射到PB10引脚,I2C1_SDA映射到PB11引脚;

I2C标速模式:最快100kbit/s;
I2C快速模式:最快400kbit/s;
当clkspeed<=100000位/秒,配置I2C为标速模式;
当100000位/秒<clkspeed<=400000位/秒,配置I2C为快速模式;

系统复位以后,I2C默认工作在从机模式下;
主机和从机模式切换如下:
通过软件设置START位置1,使I2C在总线上发送一个START起始位之后,I2C变为主机模式;
通过软件设置STOP位置1,使I2C在总线上发送一个STOP结束位后,I2C就会自动变为从机模式;

I2C接口:
串行数据SDA和串行时钟SCL;
START起始位定义:在SCL为高时,SDA线上出现一个从高到低的电平转换;
STOP结束位定义:在SCL为高时,SDA线上出现一个从低到高的电平转换;
I2C主机:负责产生START起始位和STOP结束位,并且负责产生SCL时钟;

#include "24LC256.h"

/*
GD32F103的I2C模块有I2C0和I2C1;本程序使用I2C0功能模块;

I2C0引脚复用和重映射:
当I2C0_REMAP=0时,I2C0引脚复用功能,I2C0_SCL映射到PB6引脚,I2C0_SDA映射到PB7引脚;
当I2C0_REMAP=1时,I2C0引脚重映射,I2C0_SCL映射到PB8引脚,I2C0_SDA映射到PB9引脚;

I2C1引脚只有复用引脚:
I2C1_SCL映射到PB10引脚,I2C1_SDA映射到PB11引脚;

I2C标速模式:最快100kbit/s;
I2C快速模式:最快400kbit/s;
当clkspeed<=100000位/秒,配置I2C为标速模式;
当100000位/秒<clkspeed<=400000位/秒,配置I2C为快速模式;

系统复位以后,I2C默认工作在从机模式下;
主机和从机模式切换如下:
通过软件设置START位置1,使I2C在总线上发送一个START起始位之后,I2C变为主机模式;
通过软件设置STOP位置1,使I2C在总线上发送一个STOP结束位后,I2C就会自动变为从机模式;

I2C接口:
串行数据SDA和串行时钟SCL;
START起始位定义:在SCL为高时,SDA线上出现一个从高到低的电平转换;
STOP结束位定义:在SCL为高时,SDA线上出现一个从低到高的电平转换;
I2C主机:负责产生START起始位和STOP结束位,并且负责产生SCL时钟;
*/

/*
7位地址的I2C通讯流程:
START(1位) + "7位地址+读命令(1位)" + 从机应答ACK(1位)
+ 从机发送数据1(8位) + 主机应答ACK(1位) +... 从机发送数据N(8位) + 主机应答NACK(1位) + STOP(1位)

START(1位) + "7位地址+写命令(1位)" + 从机应答ACK(1位)
+ 主机发送数据1(8位) + 从机应答ACK(1位) +... 主机发送数据N(8位) + 从机应答ACK(1位) + STOP(1位)
*/
/*
10位地址的I2C通讯流程:
START(1位) + "11110+从机地址最高2位+写命令(1位)" + 从机应答ACK(1位)
+ "从机地址低8位" + 从机应答ACK(1位) + 主机发送数据1(8位) + 从机应答ACK(1位)
+... 主机发送数据N(8位) + 从机应答ACK(1位) + STOP(1位)

START(1位) + "11110+从机地址最高2位+写命令(1位)" + 从机应答ACK(1位)+ "从机地址低8位" + 从机应答ACK(1位)
+START(1位) + "11110+从机地址最高2位+读命令(1位)" + 从机应答ACK(1位)
+ 从机发送数据1(8位) + 主机应答ACK(1位)+... 从机发送数据N(8位) + 主机应答ACK(1位) + STOP(1位)
*/

/*
设置I2C发送或接收CRC校验值:
I2C模块中有一个PEC模块,它使用CRC-8计算器来执行I2C数据的报文校验,
CRC多项式为x8+ x2+ x + 1,和SMBus协议兼容;
将PECEN位置1就可以使能PEC功能,PEC将会计算所有通过I2C总线发送的数据(包括地址);
在非DMA模式下,设置PECTRANS位置1,可以控制I2C在最后一个字节发送完毕后发送PEC值,
或者在接收完成后检查接收到的PEC值是否正确;
在DMA模式下,如果PECEN位置1,I2CI将自动发送或者检查PEC值;
*/
//SMBus:系统管理总线;
//PMBus:电源管理总线;

void EEPROM_PIN_Init(void);
void EEPROM_U8_Data_Write(u8 x,u16 addr);
void EEPROM_Buffer_Data_Write(u8 *p_buffer,u8 number_of_byte,u16 addr);
u8 EEPROM_U8_Data_Read1(u16 addr);
void EEPROM_Buffer_Data_Read(u8 *p_buffer,u8 number_of_byte,u16 addr);

//函数功能:I2C0_SCL映射到PB8引脚,I2C0_SDA映射到PB9引脚,配置I2C为快速模式400KHz
void EEPROM_PIN_Init(void)
{
	rcu_periph_clock_enable(RCU_AF);//使能映射功能的时钟
  rcu_periph_clock_enable(RCU_GPIOB);//使能GPIOB端口的外设时钟

//	gpio_pin_remap_config(GPIO_I2C0_REMAP,DISABLE);
	//设置AFIO_PCF0寄存器的I2C0_REMAP=0;
//  gpio_init(GPIOB,GPIO_MODE_AF_OD,GPIO_OSPEED_50MHZ,GPIO_PIN_6|GPIO_PIN_7);
	//I2C0复用功能:I2C0_SCL映射到PB6引脚,I2C0_SDA映射到PB7引脚;
	//GPIO_MODE_AF_OD配置PB6和PB7引脚为复用功能IO开漏极输出

	gpio_pin_remap_config(GPIO_I2C0_REMAP,ENABLE);
	//使能I2C0重映射到GPIO;
	//设置AFIO_PCF0寄存器的I2C0_REMAP=1;
  gpio_init(GPIOB,GPIO_MODE_AF_OD,GPIO_OSPEED_50MHZ,GPIO_PIN_8|GPIO_PIN_9);
	//I2C0_SCL映射到PB8引脚,I2C0_SDA映射到PB9引脚;
	//GPIO_MODE_AF_OD配置PB8和PB9引脚为复用功能IO开漏极输出

	rcu_periph_clock_enable(RCU_I2C0);//使能RCU_I2C0时钟
	i2c_clock_config(I2C0,I2C0_SPEED,I2C_DTCY_2);
	//I2C0_SPEED=400000位/秒,配置I2C为快速模式,Tlow/Thigh=2;
	//clkspeed<=100000位/秒,配置I2C为标速模式;
	//100000位/秒<clkspeed<=400000位/秒,配置I2C为快速模式;
	i2c_mode_addr_config(I2C0,I2C_I2CMODE_ENABLE,I2C_ADDFORMAT_7BITS,I2C0_SLAVE_ADDRESS7);
//配置为I2C模式,I2C从机设备使用7位地址为I2C0_SLAVE_ADDRESS7(0xA0)
	i2c_enable(I2C0);//使能I2C外设
	i2c_ack_config(I2C0,I2C_ACK_ENABLE);//I2C_ACK_ENABLE:ACKEN=1,允许发送ACK应答
}

//函数功能:将p_buffer[]中number_of_byte个数据写入首地址为addr的EEPROM中
void eeprom_page_write(uint8_t* p_buffer, uint8_t addr, uint8_t number_of_byte)
{
	FlagStatus flag;
	uint8_t x;
	union EEPROM_Addr_TYPE  temp;

	temp.Address=addr;

	do
	{
	  flag=i2c_flag_get(I2C0, I2C_FLAG_I2CBSY);
		//I2C忙标志位I2CBSY=1表示I2C正在通讯
	}while(flag==SET);

发送I2C启动条件开始/
	i2c_start_on_bus(I2C0);//主机发送I2C启动条件
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_SBSEND);
		//读取主机"I2C启动条件"已发送标志
	}while(flag==RESET );//等待SBSEND=1,进入主机模式
发送I2C启动条件结束/

发送I2C从机写器件地址开始/
	i2c_master_addressing(I2C0, I2C0_SLAVE_ADDRESS7, I2C_TRANSMITTER);
	//发送"I2C从机地址0xA0+0",设置I2C为发送方,SBSEND=0
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);
		//读取"主机已发送从机地址标志"
	}while(flag==RESET );//等待ADDSEND=1

	i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);
	//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0;
	//clear the ADDSEND bit
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_TBE);
		//读取"I2C_DATA发送寄存器空标志"
	}while( flag==RESET );//等待TBE=1,I2C_DATA发送寄存器空
发送I2C从机写器件地址结束/

发送I2C从机器件子地址开始/
	i2c_data_transmit(I2C0, temp.b[1]);
	//将read_address的值写入"I2C传送缓冲区寄存器"
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		//读取"I2C数据发送完成标志"
	}while(flag==RESET);//等待BTC=1,I2C数据发送完成

	i2c_data_transmit(I2C0, temp.b[0]);
	//将read_address的值写入"I2C传送缓冲区寄存器"
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		//读取"I2C数据发送完成标志"
	}while(flag==RESET);//等待BTC=1,I2C数据发送完成
发送I2C从机器件子地址结束/

发送x的值给I2C从机开始/    
//while there is data to be written
	while(number_of_byte--)
	{
		x=*p_buffer;
	  i2c_data_transmit(I2C0, x);
	  //将x的值写入"I2C传送缓冲区寄存器"
	  do
	  {
		  flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
			//读取"I2C数据发送完成标志"
	  }while(flag==RESET);//等待BTC=1,I2C数据发送完成
		p_buffer++;//修改指针,为发送数据做准备
	}
发送x的值给I2C从机结束/

发送I2C结束条件开始/
	i2c_stop_on_bus(I2C0); //主机发送I2C停止条件
	while( I2C_CTL0(I2C0)&0x0200 );//等待STOP=1,主机发送I2C停止条件完成
发送I2C结束条件结束/
}

//函数功能:等待"I2C从机响应"
void eeprom_wait_standby_state(void)
{
	FlagStatus flag;
	__IO uint32_t val = 0;

	while(1)//若I2C从机响应,则退出循环
	{
	  do
	  {
	    flag=i2c_flag_get(I2C0, I2C_FLAG_I2CBSY);
		  //I2C忙标志位I2CBSY=1表示I2C正在通讯
	  }while(flag==SET);//wait until I2C bus is idle

发送I2C启动条件开始/
	  i2c_start_on_bus(I2C0);//主机发送I2C启动条件
	  do
	  {
		  flag=i2c_flag_get(I2C0, I2C_FLAG_SBSEND);
		  //读取主机"I2C启动条件"已发送标志
	  }while(flag==RESET );//等待SBSEND=1,进入主机模式
发送I2C启动条件结束/

发送I2C从机写器件地址开始/
	  i2c_master_addressing(I2C0, I2C0_SLAVE_ADDRESS7, I2C_TRANSMITTER);
	  //发送"I2C从机地址0xA0+0",设置I2C为发送方,SBSEND=0
	  do
	  {//get the current value of the I2C_STAT0 register
		  val = I2C_STAT0(I2C0);
	  }while(0 == (val & (I2C_STAT0_ADDSEND | I2C_STAT0_AERR)));
		//等待ADDSEND=1
	  //I2C_STAT0_ADDSEND主机模式下成功发送了地址标志
	  //I2C_STAT0_AERR应答错误

	  if(val & I2C_STAT0_ADDSEND)//ADDSEND=1
	  {//clear ADDSEND flag
		  i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);
		  //通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0
		  i2c_stop_on_bus(I2C0);//主机发送I2C停止条件
		  return ;//I2C从机响应,则立即返回
	  }
	  else//AERR位被置1
	  {
		  i2c_flag_clear(I2C0,I2C_FLAG_AERR);//清除"I2C应答错误标志"
	  }
发送I2C从机写器件地址结束/

发送I2C结束条件开始/
	  i2c_stop_on_bus(I2C0); //主机发送I2C停止条件
	  while( I2C_CTL0(I2C0)&0x0200 );//等待STOP=1,主机发送I2C停止条件完成
发送I2C结束条件结束/
	}
}

//函数功能:将p_buffer[]中number_of_byte个数据写入首地址为addr的EEPROM中
void EEPROM_Buffer_Data_Write(u8 *p_buffer,u8 number_of_byte,u16 addr)
{
	uint8_t number_of_page = 0, number_of_single = 0, address = 0, count = 0;

	address = addr % I2C_PAGE_SIZE; //计算addr在"待写首页"内的偏移地址
	count = I2C_PAGE_SIZE - address;//计算"待写首页"的剩余字节数
	number_of_page =  number_of_byte / I2C_PAGE_SIZE;//计算"待写数据"可以写满多少个整页
	number_of_single = number_of_byte % I2C_PAGE_SIZE;//计算"待写数据"超出整页有多少个字节数

	if(0 == address)//addr位于"某个页的首地址"
	{
		while(number_of_page--)//写number_of_page个页数据
		{
			eeprom_page_write(p_buffer, addr, I2C_PAGE_SIZE);
      //将p_buffer[]中I2C_PAGE_SIZE个数据写入首地址为addr的EEPROM中			
			eeprom_wait_standby_state();//等待"I2C从机响应"
			addr +=  I2C_PAGE_SIZE;    //修改EEPROM的存储地址
			p_buffer += I2C_PAGE_SIZE; //修改指针
		}
		if(0 != number_of_single)//将"超出整页的number_of_single个字节数据"写入EEPROM
		{
			eeprom_page_write(p_buffer, addr, number_of_single);
			//将p_buffer[]中number_of_single个数据写入首地址为addr的EEPROM中	
			eeprom_wait_standby_state();//等待"I2C从机响应"
		}
	}
	else//addr位于"某个页的页内"
	{/* if addr is not I2C_PAGE_SIZE aligned */
		if(number_of_byte < count)//所写字节数小于"待写首页"空间
		{
			eeprom_page_write(p_buffer, addr, number_of_byte);
			//将p_buffer[]中number_of_byte个数据写入首地址为addr的EEPROM中	
			eeprom_wait_standby_state();//等待"I2C从机响应"
		}
		else//所写字节数超过"待写首页"空间
		{
			number_of_byte -= count;//计算去掉"待写首页"空间的字节数,还有多少个字节需要写入EEPROM
			number_of_page =  number_of_byte / I2C_PAGE_SIZE;//计算"待写数据"可以写满多少个整页
			number_of_single = number_of_byte % I2C_PAGE_SIZE;//计算"待写数据"超出整页有多少个字节数

			if(0 != count)//写count个字节到"待写首页"
			{
				eeprom_page_write(p_buffer, addr, count);
				//将p_buffer[]中count个数据写入首地址为addr的EEPROM中
				eeprom_wait_standby_state();//等待"I2C从机响应"

				addr += count;     //修改EEPROM的存储地址
				p_buffer += count; //修改指针
			}

			while(number_of_page--)//写number_of_page个页数据
			{
				eeprom_page_write(p_buffer, addr, I2C_PAGE_SIZE);
				//将p_buffer[]中I2C_PAGE_SIZE个数据写入首地址为addr的EEPROM中
				eeprom_wait_standby_state();//等待"I2C从机响应"

				addr +=  I2C_PAGE_SIZE;    //修改EEPROM的存储地址
				p_buffer += I2C_PAGE_SIZE; //修改指针
			}

			if(0 != number_of_single)//写剩余数据
			{
				eeprom_page_write(p_buffer, addr, number_of_single);
				//将p_buffer[]中number_of_single个数据写入首地址为addr的EEPROM中
				eeprom_wait_standby_state();//等待"I2C从机响应"
			}
		}
	}
}

//函数功能:将p_buffer[0]发送给从机设备0xA0,器件子地址为addr
void EEPROM_U8_Data_Write(u8 x,u16 addr)
{
	FlagStatus flag;
	union EEPROM_Addr_TYPE  temp;

	temp.Address=addr;

	do
	{
	  flag=i2c_flag_get(I2C0, I2C_FLAG_I2CBSY);
		//I2C忙标志位I2CBSY=1表示I2C正在通讯
	}while(flag==SET);

发送I2C启动条件开始/
	i2c_start_on_bus(I2C0);//主机发送I2C启动条件
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_SBSEND);
		//读取主机"I2C启动条件"已发送标志
	}while(flag==RESET );//等待SBSEND=1,进入主机模式
发送I2C启动条件结束/

发送I2C从机写器件地址开始/
	i2c_master_addressing(I2C0, I2C0_SLAVE_ADDRESS7, I2C_TRANSMITTER);
	//发送"I2C从机地址0xA0+0",设置I2C为发送方,SBSEND=0
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);
		//读取"主机已发送从机地址标志"
	}while(flag==RESET );//等待ADDSEND=1

	i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);
	//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0;
	//clear the ADDSEND bit
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_TBE);
		//读取"I2C_DATA发送寄存器空标志"
	}while( flag==RESET );//等待TBE=1,I2C_DATA发送寄存器空
发送I2C从机写器件地址结束/

发送I2C从机器件子地址开始/
	i2c_data_transmit(I2C0, temp.b[1]);
	//将read_address的值写入"I2C传送缓冲区寄存器"
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		//读取"I2C数据发送完成标志"
	}while(flag==RESET);//等待BTC=1,I2C数据发送完成

	i2c_data_transmit(I2C0, temp.b[0]);
	//将read_address的值写入"I2C传送缓冲区寄存器"
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		//读取"I2C数据发送完成标志"
	}while(flag==RESET);//等待BTC=1,I2C数据发送完成
发送I2C从机器件子地址结束/

发送x的值给I2C从机开始/    
//while there is data to be written
	i2c_data_transmit(I2C0, x);
	//将x的值写入"I2C传送缓冲区寄存器"
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		//读取"I2C数据发送完成标志"
	}while(flag==RESET);//等待BTC=1,I2C数据发送完成
发送x的值给I2C从机结束/

发送I2C结束条件开始/
	i2c_stop_on_bus(I2C0); //主机发送I2C停止条件
	while( I2C_CTL0(I2C0)&0x0200 );//等待STOP=1,主机发送I2C停止条件完成
发送I2C结束条件结束/

	eeprom_wait_standby_state();//等待"I2C从机响应"
}

//函数功能:从EEPROM地址为addr中读取一个字节数据
u8 EEPROM_U8_Data_Read1(u16 addr)
{
	FlagStatus flag;
	uint8_t rerurn_value;
	union EEPROM_Addr_TYPE  temp;

	temp.Address=addr;

	do
	{
	  flag=i2c_flag_get(I2C0, I2C_FLAG_I2CBSY);
		//I2C忙标志位I2CBSY=1表示I2C正在通讯
	}while(flag==SET);//wait until I2C bus is idle

发送I2C启动条件开始/
	i2c_start_on_bus(I2C0);//主机发送I2C启动条件
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_SBSEND);
		//读取主机"I2C启动条件"已发送标志
	}while(flag==RESET );//等待SBSEND=1,进入主机模式
发送I2C启动条件结束/

发送I2C从机写器件地址开始/
	i2c_master_addressing(I2C0, I2C0_SLAVE_ADDRESS7, I2C_TRANSMITTER);
	//发送"I2C从机地址0xA0+0",设置I2C为发送方,设置I2C为发送方,SBSEND=0
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);
		//读取"主机已发送从机地址标志"
	}while(flag==RESET );//等待ADDSEND=1
	i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);
	//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0
	//clear the ADDSEND bit

	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_TBE);
		//读取"I2C_DATA发送寄存器空标志"
	}while( flag==RESET );//等待TBE=1,I2C_DATA发送寄存器空
发送I2C从机写器件地址结束/

  i2c_enable(I2C0);
	//使能I2C外设
	//很奇怪,怎么要添加这条语句

发送I2C从机器件子地址开始/
	i2c_data_transmit(I2C0, temp.b[1]);
	//将read_address的值写入"I2C传送缓冲区寄存器"
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		//读取"I2C数据发送完成标志"
	}while(flag==RESET);//等待BTC=1,I2C数据发送完成

	i2c_data_transmit(I2C0, temp.b[0]);
	//将read_address的值写入"I2C传送缓冲区寄存器"
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		//读取"I2C数据发送完成标志"
	}while(flag==RESET);//等待BTC=1,I2C数据发送完成
发送I2C从机器件子地址结束/

发送I2C重启条件开始/
	i2c_start_on_bus(I2C0);//主机发送I2C启动条件
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_SBSEND);
		//读取主机"I2C启动条件"已发送标志
	}while(flag==RESET );//等待SBSEND=1,进入主机模式
发送I2C重启条件结束/

发送I2C从机读器件地址开始/
	i2c_master_addressing(I2C0, I2C0_SLAVE_ADDRESS7, I2C_RECEIVER);
	//发送"I2C从机地址0xA0+1",设置I2C为接收方,SBSEND=0

	i2c_ack_config(I2C0,I2C_ACK_DISABLE);
	//ACKEN=0,不允许发送ACK应答
  //注意:I2C接收最后一个字节,主机不需要发送应答

	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);
		//读取"主机已发送从机地址标志"
	}while(flag==RESET );//等待ADDSEND=1
	i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);
	//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0
	//clear the ADDSEND bit
发送I2C从机读器件地址结束/

	i2c_stop_on_bus(I2C0);//主机发送I2C停止条件

	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_RBNE);
		//读取"I2C_DATA接收寄存器非空标志"
	}while(flag==RESET );
	if(flag)//RBNE=1,"I2C_DATA接收寄存器"有新数据待读
	{
		rerurn_value = i2c_data_receive(I2C0);//读"I2C传送缓冲区寄存器"
	}

	while(I2C_CTL0(I2C0)&0x0200);
	//等待STOP=1,主机发送I2C停止条件完成
	//wait until the stop condition is finished
	
	i2c_ack_config(I2C0,I2C_ACK_ENABLE);//I2C_ACK_ENABLE:ACKEN=1,允许发送ACK应答
	i2c_ackpos_config(I2C0,I2C_ACKPOS_CURRENT);//I2C_ACKPOS_CURRENT:POAP=0
	//POAP=0且ACKEN=1表示对当前正在接收的字节发送ACK应答

	return(rerurn_value);
}

//函数功能:从EEPROM首地址为addr中,恋雪读取number_of_byte个字节数据,保存到p_buffer[]中
void EEPROM_Buffer_Data_Read(u8 *p_buffer,u8 number_of_byte,u16 addr)
{
	FlagStatus flag;
	union EEPROM_Addr_TYPE  temp;

	temp.Address=addr;

	do
	{
	  flag=i2c_flag_get(I2C0, I2C_FLAG_I2CBSY);
		//I2C忙标志位I2CBSY=1表示I2C正在通讯
	}while(flag==SET);//wait until I2C bus is idle

	if(2 == number_of_byte)
	{
		i2c_ackpos_config(I2C0,I2C_ACKPOS_NEXT);
		//I2C_ACKPOS_NEXT:POAP=1
    //POAP=1且ACKEN=1对下一个字节发送ACK应答;
	}

发送I2C启动条件开始/
	i2c_start_on_bus(I2C0);//主机发送I2C启动条件
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_SBSEND);
		//读取主机"I2C启动条件"已发送标志
	}while(flag==RESET );//等待SBSEND=1,进入主机模式
发送I2C启动条件结束/

发送I2C从机写器件地址开始/
	i2c_master_addressing(I2C0, I2C0_SLAVE_ADDRESS7, I2C_TRANSMITTER);
	//发送"I2C从机地址0xA0+0",设置I2C为发送方,SBSEND=0
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);
		//读取"主机已发送从机地址标志"
	}while(flag==RESET );//等待ADDSEND=1

	i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);
	//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0
	//clear the ADDSEND bit
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_TBE);
		//读取"I2C_DATA发送寄存器空标志"
	}while( flag==RESET );//等待TBE=1,I2C_DATA发送寄存器空
发送I2C从机写器件地址结束/

	i2c_enable(I2C0);//使能I2C外设

发送I2C从机器件子地址开始/
	i2c_data_transmit(I2C0, temp.b[1]);
	//将read_address的值写入"I2C传送缓冲区寄存器"
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		//读取"I2C数据发送完成标志"
	}while(flag==RESET);//等待BTC=1,I2C数据发送完成

	i2c_data_transmit(I2C0, temp.b[0]);
	//将read_address的值写入"I2C传送缓冲区寄存器"
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		//读取"I2C数据发送完成标志"
	}while(flag==RESET);//等待BTC=1,I2C数据发送完成
发送I2C从机器件子地址结束/

发送I2C重启条件开始/
	i2c_start_on_bus(I2C0);//主机发送I2C启动条件
	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_SBSEND);
		//读取主机"I2C启动条件"已发送标志
	}while(flag==RESET );//等待SBSEND=1,进入主机模式
发送I2C重启条件结束/

发送I2C从机读器件地址开始/
	i2c_master_addressing(I2C0, I2C0_SLAVE_ADDRESS7, I2C_RECEIVER);
	//发送"I2C从机地址0xA0+1",设置I2C为接收方,SBSEND=0
	if(number_of_byte < 3)
	{// disable acknowledge
		i2c_ack_config(I2C0,I2C_ACK_DISABLE);
		//ACKEN=0,不允许发送ACK应答
		//注意:I2C接收最后一个字节,主机不需要发送应答
	}

	do
	{
		flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);
		//读取"主机已发送从机地址标志"
	}while(flag==RESET );//等待ADDSEND=1
	i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);
	//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0;
	//clear the ADDSEND bit
发送I2C从机读器件地址结束/

	if(1 == number_of_byte)//I2C接收完成,立即发送停止条件
	{//send a stop condition to I2C bus
		i2c_stop_on_bus(I2C0);//主机发送I2C停止条件
	}

	while(number_of_byte)
	{
		if(3 == number_of_byte)//读取第(number_of_byte-3)字节数据,清除ACKEN
		{
			do
			{
				flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		    //读取"I2C数据发送完成标志"
	    }while(flag==RESET);//等待BTC=1,"I2C接收数据移位时钟"发送完成
			i2c_ack_config(I2C0,I2C_ACK_DISABLE);
			//ACKEN=0,不允许发送ACK应答
			//注意:I2C接收最后一个字节,主机不需要发送应答
		}
		if(2 == number_of_byte)//读取第(number_of_byte-2)字节数据,设置STOP
		{
			do
			{
				flag=i2c_flag_get(I2C0, I2C_FLAG_BTC);
		    //读取"I2C数据发送完成标志"
	    }while(flag==RESET);//等待BTC=1,"I2C接收数据移位时钟"发送完成
			i2c_stop_on_bus(I2C0); //主机发送I2C停止条件
		}

		flag=i2c_flag_get(I2C0, I2C_FLAG_RBNE);//读取"I2C_DATA接收寄存器非空标志"
		if(flag)//RBNE=1,"I2C_DATA接收寄存器"有新数据待读
		{
			*p_buffer = i2c_data_receive(I2C0);//读"I2C传送缓冲区寄存器"
			p_buffer++;
			number_of_byte--;
		}
	}

	while(I2C_CTL0(I2C0)&0x0200);
	//等待STOP=1,主机发送I2C停止条件完成
	//wait until the stop condition is finished
	
	i2c_ack_config(I2C0,I2C_ACK_ENABLE);//I2C_ACK_ENABLE:ACKEN=1,允许发送ACK应答
	i2c_ackpos_config(I2C0,I2C_ACKPOS_CURRENT);//I2C_ACKPOS_CURRENT:POAP=0
	//POAP=0且ACKEN=1表示对当前正在接收的字节发送ACK应答
}

main.c如下:

#include "gd32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t,bool
#include "delay.h"
//#include "IWDG.h"
//#include "WWDGT.h"
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "UART3.h"
#include "24LC256.h"

const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
const char CPU_Is_Running_REG[]="\r\nCPU is running!\r\n";

int main(void)
{
	uint8_t d;
	uint16_t i;
	u8 buf[11];

	//NVIC_PRIGROUP_PRE4_SUB0:抢占优先级为4bit(取值为0~15),子优先级为0bit(没有响应优先级)
	//NVIC_PRIGROUP_PRE3_SUB1:抢占优先级为3bit(取值为0~7),子优先级为1bit(取值为0~1)
	//NVIC_PRIGROUP_PRE2_SUB2:抢占优先级为2bit(取值为0~3),子优先级为2bit(取值为0~3)
	//NVIC_PRIGROUP_PRE1_SUB3:抢占优先级为1bit(取值为0~1),子优先级为3bit(取值为0~7)
	//NVIC_PRIGROUP_PRE0_SUB4:抢占优先级为0bit(没有抢占优先级),子优先级为3bit(取值为0~15)
	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);//设置系统中断优先级"抢占优先级为4bit,子优先级为0bit"
  INTX_ENABLE();//开启所有中断
	GD32F103_UART3_Init(115200);
	printf("%s",CPU_Reset_REG);//调试串口输出"\r\nCPU reset!\r\n"

	delay_init();
	EEPROM_PIN_Init();
	//I2C0_SCL映射到PB8引脚,I2C0_SDA映射到PB9引脚,配置I2C为快速模式400KHz

	strcpy((char*)buf,"9876543210");
	EEPROM_Buffer_Data_Write(buf,10,0);
	memset(buf,0,sizeof(buf));//清除buf[]
	EEPROM_Buffer_Data_Read(buf,10,0);
	printf("\r\nbuf1[]=%s",buf);
	for(i=0;i<10;i++)
	{
	  d=EEPROM_U8_Data_Read1(i);
	  printf("\r\nd=%c",d);
	}

	EEPROM_U8_Data_Write('0',0);
	EEPROM_U8_Data_Write('1',1);
	EEPROM_U8_Data_Write('2',2);
	EEPROM_U8_Data_Write('3',3);
	EEPROM_U8_Data_Write('4',4);
	EEPROM_U8_Data_Write('5',5);
	EEPROM_U8_Data_Write('6',6);
	EEPROM_U8_Data_Write('7',7);
	EEPROM_U8_Data_Write('8',8);
	EEPROM_U8_Data_Write('9',9);
	memset(buf,0,sizeof(buf));//清除buf[]
	EEPROM_Buffer_Data_Read(buf,10,0);
	printf("\r\nbuf2[]=%s",buf);
	for(i=0;i<10;i++)
	{
	  d=EEPROM_U8_Data_Read1(i);
	  printf("\r\nd=%c",d);
	}
	while(1)
	{
	}
}

 

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

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

相关文章

Nevron Vision for .NET Crack

Nevron Vision for .NET Crack NET Vision是一个用于创建具有数据可视化功能的强大数据表示应用程序的套件。该套件具有用于.NET的Nevron Chart、用于.NET的Nevron Diagram和用于.NET的Nevron User Interface。精心设计的对象模型、众多功能和高质量的演示使复杂数据的可视化变…

观察级水下机器人使用系列之四二维前视声纳

本文主要讲观察级水下机器人Valor配套的二维前视声纳&#xff0c;它与超短基线定位、摄像头都是水下机器人导航的重要部件。二维声纳是Tritech公司生产的&#xff0c;型号为Gemini 720is。 ​编辑​ Gemini 720is主要技术参数见下表。 Gemini 720is通过一条蓝色的缆与机器人的…

Linux命令200例:wc用于统计文本文件中的字数、行数和字符数(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

字节C++后端面试总结

字节的面经,技术栈是 C++ 后端。 计算机网络 UDP和TCP区别 先说了概念一个是面向连接的基于字节流的可靠连接,一个是不需要连接的基于数据报的不可靠传输 然后说了几个小点,比如首部长度、应用场景、服务对象什么的。 补充: 还有一个很重要的点:UDP 的实时性比 TCP 好…

【培训】第一届深圳AI视觉项目研发与部署免费线下公开课启动!2023年8月底与我们相约,开启AI视觉之旅!...

人工智能&#xff08;AI&#xff09;时代来临&#xff0c;随着技术更新迭代&#xff0c;各行各业都因为深度学习技术而发生变革&#xff0c;技术人员的能力也需要不断更新。面向AI入门者&#xff0c;我们将在2023年下半年开启定期的线下教学&#xff0c;提供深度学习核心方法与…

网络安全进阶学习第十五课——Oracle SQL注入

文章目录 一、Oracle数据库介绍二、Oracle和MySQL的语法差异&#xff1a;三、Oracle的数据库结构四、Oracle的重点系统表五、Oracle权限分类1、系统权限2、实体权限3、管理角色 六、oracle常用信息查询方法七、联合查询注入1、order by 猜字段数量2、查数据库版本和用户名3、查…

MySQL之深入InnoDB存储引擎——redo日志

文章目录 一、为什么需要redo日志二、redo日志的类型1&#xff09;简单的redo日志类型2&#xff09;复杂的redo日志类型 三、Mini-Transaction四、redo日志的写入过程五、redo日志文件1、刷盘时机2、redo日志文件组 六、log sequence number1、lsn的引入2、flushed_to_disk_lsn…

MQTT消息传输过程,网络断开后,断线重连及订阅消息恢复

1&#xff0c;首先要在网络失败的地方进行client重连 2&#xff0c;一定要设置发送超时&#xff0c;默认是-1&#xff0c;是不断进行发送的&#xff0c;会被长阻塞在这里&#xff0c;单位是ms 3&#xff0c;参考链接 https://vimsky.com/examples/detail/java-method-org.ec…

实现 cpolar 内网穿透

简介 cpolar是一种安全的内网穿透云服务&#xff0c;可以将内网下的本地服务器通过安全隧道暴露至公网&#xff0c;使得公网用户可以正常访问内网服务。它是一款内网穿透软件&#xff0c;使用简单&#xff0c;只需一行命令即可将内网站点发布至公网&#xff0c;方便给客户演示…

【100天精通python】Day29:文件与IO操作_JSON文件处理

目录 专栏导读 一、XML文件概述 1. 标签和元素 2. 嵌套结构 3. 属性 4. 命名空间 5. CDATA节 6. 注释 7. 验证与验证语言 8. 扩展性 二、XML文件处理常见操作 1. 解析XML文件 2. 创建和编辑XML文件 3. 修改XML文件 4. 查询XML元素 5 遍历XML元素 6. 删除XML元…

嵌入式虚拟仿真实验教学平台之串口发送数据

嵌入式虚拟仿真实验教学平台课程系列 串口发送数据实验 课程内容 本实验使用 STM32 的串口发送数据。开始仿真后,打开串口监视器&#xff0c;串口监视器会打印出要发送的数据。 课程目标 学习配置使用GPIO功能学习配置使用复用功能学习配置使用UART功能 硬件设计 本课程…

扩散模型(DDPM)介绍

文章目录 扩散模型扩散过程前向过程逆向过程 网络结构 文章主要介绍了DDPM扩散模型&#xff0c;包含了详细的数学推导&#xff0c;可能公式有点多&#xff0c;但是只要能够耐心看完&#xff0c;相信会有一些收获的。好了下面进入正题。 扩散模型 扩散模型是一种新的生成模型&a…

FPGA纯verilog实现 LZMA 数据压缩,提供工程源码和技术支持

目录 1、前言2、我这儿已有的FPGA压缩算法方案3、FPGA LZMA数据压缩功能和性能4、FPGA LZMA 数据压缩设计方案输入输出接口描述数据处理流程LZ检索器数据同步LZMA 压缩器 为输出LZMA压缩流添加文件头 5、vivado仿真6、福利&#xff1a;工程代码的获取 1、前言 说到FPGA的应用&…

stm32项目(4)——基于stm32的厨房安全检测系统

目录 1.功能介绍 2.硬件方案 1.单片机的选择 2.显示屏的选择 3.温湿度传感器 4.烟雾传感器 5.甲醛传感器 6.排气风扇 3.程序设计 1.接线方式 2.大致思路 1.功能介绍 本次设计的厨房安全检测系统的功能如下&#xff1a; 利用单片机、烟雾传感器、甲醛传感器、温湿度传感器…

LeetCode-Java(03)

9. 回文数 class Solution {public boolean isPalindrome(int x) {if (x < 0 || (x % 10 0 && x ! 0)) {return false;}int revertedNumber 0;while (x > revertedNumber) {revertedNumber revertedNumber * 10 x % 10;x / 10;}// 当长度为奇数时通过reverte…

现代C++中的从头开始深度学习:【5/8】卷积

一、说明 在上一个故事中&#xff0c;我们介绍了机器学习的一些最相关的编码方面&#xff0c;例如 functional 规划、矢量化和线性代数规划。 现在&#xff0c;让我们通过使用 2D 卷积实现实际编码深度学习模型来开始我们的道路。让我们开始吧。 二、关于本系列 我们将学习如何…

【从零学习python 】04. Python编程基础:变量、数据类型与标识符

文章目录 变量以及数据类型一、变量的定义二、变量的类型三、查看数据类型 标识符和关键字标识符命名规则命名规范 关键字进阶案例 变量以及数据类型 一、变量的定义 对于重复使用&#xff0c;并且经常需要修改的数据&#xff0c;可以定义为变量&#xff0c;来提高编程效率。…

kubeasz在线安装K8S集群单master

1.基础系统配置 确保在干净的系统上开始安装&#xff0c;不能使用曾经装过kubeadm或其他k8s发行版的环境 系统是Ubuntu 或者CentOS 7 2.下载文件 2.1 下载工具脚本ezdown&#xff0c;举例使用kubeasz版本3.5.0 #此版本默认安装的是 K8S v1.26.0 export release3.5.0 wget h…

初遇C++之语法篇(完结)

&#x1f9c3;博客主页:阿博历练记 &#x1f4d6;文章专栏:c &#x1f68d;代码仓库:阿博编程日记 &#x1f361;欢迎关注:欢迎友友们点赞收藏关注哦&#x1f339; 文章目录 &#x1f36d;1.函数重载&#x1f4dc;1.1函数重载的概念&#x1f4e2;1.2函数重载三大误区&#x1f3…