GD32F103硬件I2C0通讯

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=0do{flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);//读取"主机已发送从机地址标志"}while(flag==RESET );//等待ADDSEND=1i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0;//clear the ADDSEND bitdo{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 writtenwhile(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=0do{//get the current value of the I2C_STAT0 registerval = 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 flagi2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0i2c_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;//计算去掉"待写首页"空间的字节数,还有多少个字节需要写入EEPROMnumber_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=0do{flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);//读取"主机已发送从机地址标志"}while(flag==RESET );//等待ADDSEND=1i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0;//clear the ADDSEND bitdo{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 writteni2c_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=0do{flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);//读取"主机已发送从机地址标志"}while(flag==RESET );//等待ADDSEND=1i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0//clear the ADDSEND bitdo{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=0i2c_ack_config(I2C0,I2C_ACK_DISABLE);//ACKEN=0,不允许发送ACK应答//注意:I2C接收最后一个字节,主机不需要发送应答do{flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);//读取"主机已发送从机地址标志"}while(flag==RESET );//等待ADDSEND=1i2c_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 finishedi2c_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 idleif(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=0do{flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);//读取"主机已发送从机地址标志"}while(flag==RESET );//等待ADDSEND=1i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND);//通过读取I2C_STAT0和I2C_STAT1寄存器,令ADDSEND=0//clear the ADDSEND bitdo{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=0if(number_of_byte < 3){// disable acknowledgei2c_ack_config(I2C0,I2C_ACK_DISABLE);//ACKEN=0,不允许发送ACK应答//注意:I2C接收最后一个字节,主机不需要发送应答}do{flag=i2c_flag_get(I2C0, I2C_FLAG_ADDSEND);//读取"主机已发送从机地址标志"}while(flag==RESET );//等待ADDSEND=1i2c_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 busi2c_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 finishedi2c_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为快速模式400KHzstrcpy((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.mzph.cn/news/33331.shtml

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

相关文章

学习笔记-JVM-对象结构及生命周期

申明&#xff1a;文章内容是本人学习极客时间课程所写&#xff0c;文字和图片基本来源于课程资料&#xff0c;在某些地方会插入一点自己的理解&#xff0c;未用于商业用途&#xff0c;侵删。 原资料地址&#xff1a;课程资料 对象的创建流程 常量池检查:检查new指令是否能在常…

【2023最新美团笔试题目分析】“求最多出现数字及次数“、坦克大战在线对战游戏(问题描述 + 示例代码 + 时间复杂度分析)

弃幼少嬉戏堕慢之心,而衎衎于进德修业之志。 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3] 2022年度博客之星人工智能领域TOP4🌟 🏅[4] 阿里云社区特邀专家博主

构建Docker容器监控系统(Cadvisor +InfluxDB+Grafana)

目录 案例概述 Cadvisor InfluxDBGrafana 1.1、 Cadvisor 1.2、InfluxDB 1.3、Grafana 1.4、监控组件架构 1.5、开始部署 安装docker-ce 阿里云镜像加速器 创建自定义网络 创建influxdb容器 案例概述 Docker作为目前十分出色的容器管理技术&#xff0c;得到大量企业…

如何使用webpack打包一个库library,使用webpack打包sdk.

如何使用webpack打包一个库library 如果你需要自己封装一些包给别人使用,那么可以参考以下方法 初始化库 mkdir library cd library npm init -y经过以上步骤后会生成一个library文件夹&#xff0c;里面包含一个package.json文件。然后简单修改为如下所示&#xff1a; {&qu…

MongoDB:Unrecognized option: storage

MongoDB一直显示 Unrecognized option: storage try ‘mongod --help’ for more information 意思是我们配置的config文件出了问题。 说明&#xff1a;MongoDB采用的是YAML格式&#xff0c;所以我们只需要稍微改改就好。 在storage前面&#xff1a;没有空格 下面两行最前面…

docker 安装mongodb 虚拟机安装mongodb

生产环境直接安装比较好&#xff0c;以及使用集群环境&#xff0c;本文仅测试交流使用&#xff0c;我用来写分布式im测试使用&#xff1a; nami-im: 分布式im, 集群 zookeeper netty kafka nacos rpc主要为gate&#xff08;长连接服务&#xff09; logic &#xff08;业务&…

x11 gtk qt gnome kde 之间的区别和联系

Linux 下的图形库介绍 一、Linux 图形领域的基础设施 1.1 X Window X Window从逻辑上分为三层&#xff1a;X Server、X Client和X协议。 最底层的X Server&#xff08;X服务器&#xff09;主要处理输入/输出信息并维护相关资源&#xff0c;它接受来自键盘、鼠标的操作并将…

JavaScript版本ES5/ES6及后续版本

JavaScript简史 1995&#xff1a; Brendan Eich在短短10天内创建了JavaScript的第一个版本。它被称为摩卡&#xff0c;但已经具备了现代JavaScript的许多基本特性! 1996&#xff1a; 为了吸引Java开发人员&#xff0c;Mocha先是更改为LiveScript&#xff0c;然后又更改为Ja…

pycharm的Terminal中如何设置打开anaconda3的虚拟环境

在pycharm的File -> Settings -> Tools -> Terminal下面&#xff0c;如下图所示 修改为红框中内容&#xff0c;然后关闭终端在重新打开终端&#xff0c;即可看到anaconda3的虚拟环境就已经会被更新

使用 AndroidX 增强 WebView 的能力

在App开发过程中&#xff0c;为了在多个平台上保持一致的用户体验和提高开发效率&#xff0c;许多应用程序选择使用 H5 技术。在 Android 平台上&#xff0c;通常使用 WebView 组件来承载 H5 内容以供展示。 一.WebView 存在的问题 自 Android Lollipop 起&#xff0c;WebVie…

java之junit Test

JUnit测试简介 1.什么是单元测试 单元测试是针对最小的功能单元编写测试代码Java程序最小的功能单元是方法单元测试就是针对单个Java方法的测试 2.测试驱动开发 3.单元测试的好处 确保单个方法运行正常如果修改了方法代码&#xff0c;只需确保其对应的单元测试通过测试代码…

锁定Mac的内置键盘,防止外接键盘时的误触

场景&#xff1a;把你的外接键盘放在mac上&#xff0c;然后打字时&#xff0c;发现外接键盘误触mac键盘&#xff0c;导致使用体验极差 解决方案&#xff1a;下载Karabiner-Elements这款软件&#xff0c;并给它开启相关权限。 地址&#xff1a;https://github.com/pqrs-org/Ka…

3.2 Tomcat基础

1. Tomcat概述 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器。 Tomcat版本&#xff1a;apache-tomcat-8.5.76。 2.IDEA集成Tomcat 第一步 第二步 第三步 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff0…

[保研/考研机试] KY80 进制转换 北京大学复试上机题 C++实现

题目链接&#xff1a; KY80 进制转换https://www.nowcoder.com/share/jump/437195121691735660774 描述 写出一个程序&#xff0c;接受一个十六进制的数值字符串&#xff0c;输出该数值的十进制字符串(注意可能存在的一个测试用例里的多组数据)。 输入描述&#xff1a; 输…

个人对智能家居平台选择的思考

本人之前开发过不少MicroPython程序&#xff0c;其中涉及到自动化以及局域网控制思路&#xff0c;也可以作为智能家居的实现方式。而NodeMCUESPHome的方案具有方便添加硬件、容易更新程序和容量占用小的优势&#xff0c;本人也查看过相关教程后感觉部署ESPHome和编译固件的步骤…

access怎么做进销存?借助access开发进销存管理应用

我不太推荐使用Access&#xff0c;因为他的缺点还是比较明显的&#xff1a; 1、软件自身限制 不能用于互联网&#xff1a;使用Access制作好的管理软件&#xff0c;访问页只能在局域网中使用&#xff1b;只能在Windows上运行&#xff1a;Access仅支持windows的运行环境&#x…

写一个函数返回参数二进制中 1 的个数(c语言三种实现方法)

&#xff08;本文旨在自己做题时的总结&#xff0c;我会给出不同的解法&#xff0c;后面如果碰到新的题目还会加入其中&#xff0c;等于是我自己的题库。 1.写一个函数返回参数二进制中 1 的个数。 比如&#xff1a; 15 0000 1111 4 个 1 方法一&#xff1a; #include…

【AI理论学习】手把手利用PyTorch实现扩散模型DDPM

手把手利用PyTorch实现扩散模型DDPM DDPM代码实现神经网络定义辅助函数位置嵌入ResNet block注意力模块分组归一化Conditional U-Net 定义前向扩散过程定义PyTorch数据集DataLoader采样训练模型采样后续阅读 参考链接 上一篇博文已经手把手推导了扩散模型DDPM&#xff0c;本文利…

【mysql】—— 表的约束

目录 序言 &#xff08;一&#xff09;空属性 &#xff08;二&#xff09;默认值 &#xff08;三&#xff09;列描述 &#xff08;四&#xff09;zerofill &#xff08;五&#xff09;主键 &#xff08;六&#xff09;自增长 &#xff08;七&#xff09;唯一键 &#…

Angular安全专辑 —— CSP防止XSS攻击

什么是 CSP&#xff08;Content Security Policy&#xff09; CSP&#xff08;Content Security Policy&#xff09;是一种Web安全策略&#xff0c;用于减轻和防止跨站脚本攻击&#xff08;XSS&#xff09;等安全漏洞。它通过允许网站管理员定义哪些资源可以加载到网页中&#…