STM32G474硬件I2C接口:英文Inter-integrated circuit简写为I2C。STM32G474是M4核,在使用硬件I2C时,配置方法和M3核相差较大。通过阅读参考手册和HAL,总算了解了其配置原理。
1、I2C工作模式
I2C标准模式:最高时钟频率为100KHz;
I2C快速模式:最高时钟频率为400KHz;
I2C快速模式+:最高时钟频率为1MHz,通常用500KHz;
2、SCL时钟频率配置
I2C_TIMINGR寄存器bit31:28(PRESC[3:0]),PRESC[3:0]表示I2C时钟分频器值,分频后的周期:
tPRESC = (PRESC[3:0]+1) x tI2CCLK;
I2C_TIMINGR寄存器bit23:20(SCLDEL[3:0]),SCLDEL[3:0]表示SDA边沿和SCL上升沿之间的时间,是指数据建立时间:
tSCLDEL = (SCLDEL[3:0]+1) x tPRESC;
I2C_TIMINGR寄存器bit19:16(SDADEL[3:0]),SDADEL[3:0]表示SCL下降沿和SDA边沿之间的时间,是指数据保持时间:tSDADEL= SDADEL[3:0] x tPRESC;
I2C_TIMINGR寄存器bit15:8(SCLH[7:0]),SCLH[7:0]表示SCL高电平的时间:tSCLH = (SCLH[7:0]+1) x tPRESC;
I2C_TIMINGR寄存器bit7:0(SCLL[7:0]),SCLL[7:0]表示SCL低电平的时间:tSCLL = (SCLL[7:0]+1) x tPRESC;
主时钟周期:
tSCL = tSYNC1 + tSYNC2 + { [ (SCLH[7:0]+1) + (SCLL[7:0]+1) ] * (PRESC[3:0]+1) * tI2CCLK }
注意:tSYNC1 + tSYNC2 >= “4 * tI2CCLK”
I2C时钟要求,I2C内核时钟来自I2CCLK时钟,I2CCLK时钟必须满足下面的条件:
tI2CCLK < (tLOW - tfilters) / 4 且 tI2CCLK < tHIGH
tLOW是指SCL低电平时间;
tHIGH是指SCL高电平时间;
tfilters是指模拟滤波器延时时间和数据滤波器的延时时间之和;
模拟滤波器最大延时时间为260ns;
数字滤波器的延时时间为:I2C_CR1寄存器的DNF[3:0] * tI2CCLK;
1)、当tI2CCLK=170MHz时,计算“I2C快速模式+”主时钟周期:
tSYNC1 + tSYNC2 = 4 x tI2CCLK = (4 / 170) * 1000 =23.529ns
I2C时钟分频器值:PRESC[3:0]=0
SCL高电平的时间:tSCLH = (SCLH[7:0]+1) x tPRESC = 0x85+1 = 134
SCL低电平的时间:tSCLL = (SCLL[7:0]+1) x tPRESC = 0xC8+1 = 201
{ [ (SCLH[7:0]+1) + (SCLL[7:0]+1) ] * (PRESC[3:0]+1) * tI2CCLK } =[ (134+201) * (0+1) / 170 ] *1000 = 1970.588ns
因此,SCL时钟周期:tSCL = 23.529 + 1970.588 = 1996.08ns,fSCL = 500.98KHz
2)、当tI2CCLK=170MHz时,计算“I2C快速模式”主时钟周期:
tSYNC1 + tSYNC2 = 4 x tI2CCLK = (4 / 170) * 1000 =23.529ns
I2C时钟分频器值:PRESC[3:0]=0
SCL高电平的时间:tSCLH = (SCLH[7:0]+1) x tPRESC = 0xA7+1 = 168
SCL低电平的时间:tSCLL = (SCLL[7:0]+1) x tPRESC = 0xFB+1 = 252
{ [ (SCLH[7:0]+1) + (SCLL[7:0]+1) ] * (PRESC[3:0]+1) * tI2CCLK } =[ (168+252) * (0+1) / 170 ] *1000 = 2470.588ns
因此,SCL时钟周期:tSCL = 23.529 + 2476.471 = 2500.117ns,fSCL = 399.981KHz
3)、当tI2CCLK=170MHz时,计算“I2C标准模式”主时钟周期:
tSYNC1 + tSYNC2 = 4 x tI2CCLK = (4 / 170) * 1000 =23.529ns
I2C时钟分频器值:PRESC[3:0]=3
SCL高电平的时间:tSCLH = (SCLH[7:0]+1) x tPRESC = 0xA8+1 = 169
SCL低电平的时间:tSCLL = (SCLL[7:0]+1) x tPRESC = 0xFD+1 = 254
{ [ (SCLH[7:0]+1) + (SCLL[7:0]+1) ] * (PRESC[3:0]+1) * tI2CCLK } =[ (169+254) * (3+1) / 170 ] *1000 = 9952.941ns
因此,SCL时钟周期:tSCL = 23.529 + 9952.941 = 9976.47ns,fSCL = 100.23585KHz
3、I2C主从切换
I2C_CR2寄存器bit13(START),令START=1产生“启动条件”,清除I2C_ISR寄存器bit6(TC)
I2C_CR2寄存器bit14(STOP),令STOP=1产生“停止条件”,清除I2C_ISR寄存器bit6(TC)
默认情况下,硬件I2C工作在从机模式。
当硬件I2C产生启动条件时,它就自动从“从机模式”切换到“主机机模式”;
当产生“仲裁丢失”或“停止条件”时,自动从“主机机模式”切换到“从机模式”;
因此,I2C总线上可以有多个主机和多个从机存在。
4、I2C主机的主要任务
I2C主机负责产生“启动条件”,“时钟信号”,“传输数据”和“停止条件”。
“I2C串行数据传输”始终以“启动条件”开始,并以“停止条件”结束。
I2C从机负责识别它自己的“器件地址”(7位或10位)和“普通的访问地址”。
“普通的访问地址检测”由软件启用或禁用。
“保留的SMBus地址”也可以通过软件来启用。
5、初步认识I2C波形图
I2C传输的“数据和地址”均以“8位字节”的形式进行传输,首先传送的是MSB位。
“启动条件”产生后,传输的第一个字节是“器件地址”(一个7位模式或两个10位模式),该地址始终由“I2C主机”发送给“I2C从机”。
当“8位字节”传输完成后,在第9个时钟脉冲期间,“接收机”必须向“发送机”发送“一个确认位”,这个确认位就是I2C应答;
“确认位”为低电平叫“ACK应答”;“确认位”为高电平叫“NACK应答”。
波形图如下:
SCL时钟拉伸:
6、软件复位I2C:
1)、配置I2C_CR1寄存器bit0(PE),令PE=0
2)、读I2C_CR1寄存器bit0(PE),查询PE是否为0
3)、配置I2C_CR1寄存器bit0(PE),令PE=1
7、I2C初始化流程:
1)、配置I2C_CR1寄存器bit0(PE),令PE=0,不使能I2C设备
2)、配置I2C_CR1寄存器bit12(ANFOFF),令ANFOFF=0使能“模拟滤波器”
3)、配置I2C_CR1寄存器bit11:8(DNF[3:0]),令DNF[3:0]=0000b,不使能“数字滤波器”
4)、配置“I2C_TIMINGR寄存器”
I2C_TIMINGR寄存器bit31:28(PRESC[3:0]),PRESC[3:0]表示I2C时钟分频器值,分频后的周期:
tPRESC = (PRESC[3:0]+1) x tI2CCLK = (0+1)/170 = T
I2C_TIMINGR寄存器bit23:20(SCLDEL[3:0]),SCLDEL[3:0]表示SDA边沿和SCL上升沿之间的时间,是指数据建立时间:
tSCLDEL = (SCLDEL[3:0]+1) x tPRESC = (3+1) * T = 4T
I2C_TIMINGR寄存器bit19:16(SDADEL[3:0]),SDADEL[3:0]表示SCL下降沿和SDA边沿之间的时间,是指数据保持时间:
tSDADEL= SDADEL[3:0] x tPRESC = 0 * T = 0
I2C_TIMINGR寄存器bit15:8(SCLH[7:0]),SCLH[7:0]表示SCL高电平的时间:
tSCLH = (SCLH[7:0]+1) x tPRESC = (0x3D+1) * T = 62T
I2C_TIMINGR寄存器bit7:0(SCLL[7:0]),SCLL[7:0]表示SCL低电平的时间:
tSCLL = (SCLL[7:0]+1) x tPRESC = (0x5B+1) * T = 92T
5)、配置I2C_CR1寄存器bit17(NOSTRETCH),令NOSTRETCH=0,使能“时钟拉伸”
6)、配置I2C_CR1寄存器bit0(PE),令PE=1,使能I2C设备
7)、I2C初始化完成
8、从机初始化流程:
1)、配置I2C_CR1寄存器bit0(PE),令PE=0,不使能I2C设备
2)、配置I2C_CR1寄存器bit12(ANFOFF),令ANFOFF=0使能“模拟滤波器”
3)、配置I2C_CR1寄存器bit11:8(DNF[3:0]),令DNF[3:0]=0000b,不使能“数字滤波器”
4)、配置“I2C_TIMINGR寄存器”
I2C_TIMINGR寄存器bit31:28(PRESC[3:0]),PRESC[3:0]表示I2C时钟分频器值,分频后的周期:
tPRESC = (PRESC[3:0]+1) x tI2CCLK = (0+1)/170 = T
I2C_TIMINGR寄存器bit23:20(SCLDEL[3:0]),SCLDEL[3:0]表示SDA边沿和SCL上升沿之间的时间,是指数据建立时间:
tSCLDEL = (SCLDEL[3:0]+1) x tPRESC = (3+1) * T = 4T
I2C_TIMINGR寄存器bit19:16(SDADEL[3:0]),SDADEL[3:0]表示SCL下降沿和SDA边沿之间的时间,是指数据保持时间:
tSDADEL= SDADEL[3:0] x tPRESC = 0 * T = 0
I2C_TIMINGR寄存器bit15:8(SCLH[7:0]),SCLH[7:0]表示SCL高电平的时间:
tSCLH = (SCLH[7:0]+1) x tPRESC = (0x3D+1) * T = 62T
I2C_TIMINGR寄存器bit7:0(SCLL[7:0]),SCLL[7:0]表示SCL低电平的时间:
tSCLL = (SCLL[7:0]+1) x tPRESC = (0x5B+1) * T = 92T
5)、配置I2C_CR1寄存器bit17(NOSTRETCH),令NOSTRETCH=0,使能“时钟拉伸”
6)、配置I2C_CR1寄存器bit0(PE),令PE=1,使能I2C设备
7)、配置I2C_OAR1寄存器bit15(OA1EN),令OA1EN=0,不使能“自身设备地址1”
8)、配置I2C_OAR2寄存器bit15(OA2EN),令OA2EN=0,不使能“自身设备地址2”
9)、配置I2C_OAR1寄存器bit9:0(OA1[9:0]),设置“自身设备地址1”,最多为10位
10)、配置I2C_OAR1寄存器bit10(OA1MODE),令OA1MODE=1表示OA1[9:0]是10位地址
11)、配置I2C_OAR1寄存器bit15(OA1EN),令OA1EN=1,使能“自身设备地址1”
12)、I2C_OAR2寄存器bit7:1(OA2[7:1]),设置“自身设备地址2”,最多为6位
13)、I2C_OAR2寄存器bit10:8(OA2MSK[2:0]),OA2MSK[2:0]=000b表示不屏蔽OA2[7:1]
14)、配置I2C_OAR2寄存器bit15(OA2EN),令OA2EN=1,使能“自身设备地址2
15)、配置I2C_CR1寄存器bit19(GCEN),令GCEN=1,General call enabled.
16)、配置I2C_CR1寄存器bit16(SBC),令SBC=1,Slave byte control enabled.
17)、使能中断
I2C_CR1寄存器bit1(TXIE),令TXIE=1表示使能“I2C发送中断”:当I2C_TXDR为空时,传输中断状态TXIS=1,写I2C_TXDR会使TXIS=0;
I2C_CR1寄存器bit4(NACKIE),令NACKIE=1表示使能“I2C接收到NACK中断”:当收到NACK应答时,NACKF=1,I2C从机自动释放SCL和SDA,让I2C主机执行停止条件和重启条件
I2C_CR1寄存器bit5(STOPIE),令STOPIE=1表示使能“I2C检测到STOP中断”:当收到“STOP条件”时,STOPF=1
I2C_CR1寄存器bit2(RXIE),令RXIE=1表示使能“I2C接收中断”
I2C_CR1寄存器bit3(ADDRIE),令ADDRIE=1表示使能“I2C地址匹配中断”
I2C_CR1寄存器bit6(TCIE),令TCIE=1表示使能“I2C传输完成中断”
I2C_CR1寄存器bit7(ERRIE),令ERRIE=1表示使能“I2C错误中断”
9、时间溢出中断的条件
I2C_TIMEOUTR寄存器bit15(TIMOUTEN),令TIMOUTEN=1使能SCL时间溢出检测。
I2C_TIMEOUTR寄存器bit10:0(TIMEOUTA[11:0])
I2C_TIMEOUTR寄存器bit12(TIDLE),配置TIDLE=0,就可以用来检测SCL低电平时间:
tTIMEOUT= (TIMEOUTA[11:0]+1) x 2048 x tI2CCLK
如果SCL被拉低时间超过tTIMEOUT,则I2C_ISR寄存器bit12(TIMEOUT)被置1,令TIMEOUTCF=1可以清除TIMEOUT=0
配置TIDLE=1用来检测SCL低电平时间和高电平时间之和:tIDLE= (TIMEOUTA[11:0]+1) x 4 x tI2CCLK
10、读写EEPROM的步骤
1)、从EEPROM读取1个字节的步骤:
发送“I2C启动条件”
发送“写器件地址”,告诉“这个地址的I2C从机”做好通讯准备,24LC256的“写器件地址”为0xA0
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
发送器件的子地址高8位值,告诉I2C从机读/写数据的位置;
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
发送器件的子地址低8位值,告诉I2C从机读/写数据的位置;
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
发送“I2C重启动条件”
发送“读器件地址”,告诉“这个地址的I2C从机”做好通讯准备
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
从EEPROM读取一个字节
CPU发送不应答信号
发送“I2C停止条件”
2)、从EEPROM读取2个字节的步骤:
发送“I2C启动条件”
发送“写器件地址”,告诉“这个地址的I2C从机”做好通讯准备,24LC256的“写器件地址”为0xA0
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
发送器件的子地址高8位值,告诉I2C从机读/写数据的位置;
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
发送器件的子地址低8位值,告诉I2C从机读/写数据的位置;
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
发送“I2C重启动条件”
发送“读器件地址”,告诉“这个地址的I2C从机”做好通讯准备
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
从EEPROM读取第1个字节
CPU发送应答信号
从EEPROM读取最后1个字节
CPU发送不应答信号
发送“I2C停止条件”
3)、向EEPROM写1个字节的步骤:
发送“I2C启动条件”
发送“写器件地址”,告诉“这个地址的I2C从机”做好通讯准备,24LC256的“写器件地址”为0xA0
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
发送器件的子地址高8位值,告诉I2C从机读/写数据的位置;
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
发送器件的子地址低8位值,告诉I2C从机读/写数据的位置;
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
无须发送“I2C重启动条件”,直接向EEPROM写入1个字节
等待从器件接收方的应答,即等待SDA脚被EEPROM拉低;
发送“I2C停止条件”
void I2C1_Init(void)
{
I2C_HandleTypeDef hi2c1;
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct;
RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2C1;//初始化I2C1时钟
RCC_PeriphCLKInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_SYSCLK;
//RCC_CCIPR寄存器bit13:12(I2C1SEL[1:0]),令I2C1SEL[1:0]=01b设置I2C1时钟源为SYSCLK系统时钟170MHz
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
//HAL_RCCEx_PeriphCLKConfig()初始化I2C1外设时钟
//Configure the I2C clock source. The clock is derived from the SYSCLK
__HAL_RCC_I2C1_CLK_ENABLE(); //使能I2C1外设时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA外设时钟
GPIO_InitStruct.Pin = GPIO_PIN_15;//选择引脚编号15
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; //复用功能开漏极模式
GPIO_InitStruct.Pull = GPIO_PULLUP; //引脚上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; //引脚速度为低速
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; //将引脚复用为I2C1_SCL
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
__HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB外设时钟
GPIO_InitStruct.Pin = GPIO_PIN_9;//选择引脚编号9
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; //复用功能开漏极模式
GPIO_InitStruct.Pull = GPIO_PULLUP; //引脚上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; //引脚速度为低速
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; //将引脚复用为I2C1_SDA
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
hi2c1.Instance = I2C1;
#if I2C_SPeed== I2C_100KHz
hi2c1.Init.Timing = 0x3030A8FD;
//I2C_TIMINGR寄存器bit31:28(PRESC[3:0]),PRESC[3:0]表示I2C时钟分频器值,分频后的周期:
//tPRESC = (PRESC[3:0]+1) x tI2CCLK = 3+1 = 4
//I2C_TIMINGR寄存器bit23:20(SCLDEL[3:0]),SCLDEL[3:0]表示SDA边沿和SCL上升沿之间的时间,是指数据建立时间:
//tSCLDEL = (SCLDEL[3:0]+1) x tPRESC = (3+1) / 170 = 0.0235294117647059us;
//I2C_TIMINGR寄存器bit19:16(SDADEL[3:0]),SDADEL[3:0]表示SCL下降沿和SDA边沿之间的时间,是指数据保持时间:
//tSDADEL= SDADEL[3:0] x tPRESC = 0 / 170 = 0us
//I2C_TIMINGR寄存器bit15:8(SCLH[7:0]),SCLH[7:0]表示SCL高电平的时间:
//tSCLH = (SCLH[7:0]+1) x tPRESC = 0xA8+1 = 169
//I2C_TIMINGR寄存器bit7:0(SCLL[7:0]),SCLL[7:0]表示SCL低电平的时间:
//tSCLL = (SCLL[7:0]+1) x tPRESC = 0xFD+1 =254
//1)、当tI2CCLK=170MHz时,计算“I2C标准模式”主时钟周期:
//tSYNC1 + tSYNC2 = 4 x tI2CCLK = (4 / 170) * 1000 =23.529ns
//PRESC[3:0]=3
//tSCLH = (SCLH[7:0]+1) x tPRESC = 0xA8+1 = 169
//tSCLL = (SCLL[7:0]+1) x tPRESC = 0xFD+1 = 254
//{ [ (SCLH[7:0]+1) + (SCLL[7:0]+1) ] * (PRESC[3:0]+1) * tI2CCLK } =[ (169+254) * (3+1) / 170 ] *1000 = 9952.941ns
//因此,tSCL = 23.529 + 9952.941 = 9976.47ns,fSCL = 100.23585KHz
#elif I2C_SPeed == I2C_400KHz
hi2c1.Init.Timing = 0x00FFA7FB;
//I2C_TIMINGR寄存器bit31:28(PRESC[3:0]),PRESC[3:0]表示I2C时钟分频器值,分频后的周期:
//tPRESC = (PRESC[3:0]+1) x tI2CCLK = 0+1 = 1
//I2C_TIMINGR寄存器bit23:20(SCLDEL[3:0]),SCLDEL[3:0]表示SDA边沿和SCL上升沿之间的时间,是指数据建立时间:
//tSCLDEL = (SCLDEL[3:0]+1) x tPRESC = (15+1) / 170
//I2C_TIMINGR寄存器bit19:16(SDADEL[3:0]),SDADEL[3:0]表示SCL下降沿和SDA边沿之间的时间,是指数据保持时间:
//tSDADEL= SDADEL[3:0] x tPRESC = 15 / 170
//I2C_TIMINGR寄存器bit15:8(SCLH[7:0]),SCLH[7:0]表示SCL高电平的时间:
//tSCLH = (SCLH[7:0]+1) x tPRESC = 0xA7+1 = 168
//I2C_TIMINGR寄存器bit7:0(SCLL[7:0]),SCLL[7:0]表示SCL低电平的时间:
//tSCLL = (SCLL[7:0]+1) x tPRESC = 0xFB+1 =252
//1)、当tI2CCLK=170MHz时,计算“I2C标准模式”主时钟周期:
//tSYNC1 + tSYNC2 = 4 x tI2CCLK = (4 / 170) * 1000 =23.529ns
//PRESC[3:0]=0
//tSCLH = (SCLH[7:0]+1) x tPRESC = 0xA7+1 = 168
//tSCLL = (SCLL[7:0]+1) x tPRESC = 0xFB+1 = 252
//{ [ (SCLH[7:0]+1) + (SCLL[7:0]+1) ] * (PRESC[3:0]+1) * tI2CCLK } =[ (168+252) * (0+1) / 170 ] *1000 = 2470.588ns
//因此,tSCL = 23.529 + 2470.588 = 2494.178ns,fSCL = 400.933KHz
#endif
#if EEPROM_Device_Address_Size == 0 //I2C从机“器件地址”为7位数据
hi2c1.Init.OwnAddress1 = I2C_Own_Address;
//I2C_OAR1寄存器bit9:0(OA1[9:0]),自身地址1:OA1[9:0]=hi2c->Init.OwnAddress1
hi2c1.Init.OwnAddress2 = 0;
//I2C_OAR2寄存器bit17:1(OA2[7:1]),自身地址2:OA2[7:1]=hi2c->Init.OwnAddress2
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
//I2C_OAR2寄存器bit110:8(OA2MSK[2:0]),自身地址2的掩码OA2MSK[2:0]=000b,自身地址2(OA2[7:1])无掩码
//OA2MSK[2:0]=000b,OA2[7:1]必须要全部匹配才能使I2C正常通讯
//OA2MSK[2:0]=001b,忽略OA2[1]匹配,但OA2[7:2]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=010b,忽略OA2[2:1]匹配,但OA2[7:3]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=011b,忽略OA2[3:1]匹配,但OA2[7:4]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=100b,忽略OA2[4:1]匹配,但OA2[7:5]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=101b,忽略OA2[5:1]匹配,但OA2[7:6]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=110b,忽略OA2[6:1]匹配,但OA2[7]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=111b,忽略OA2[7:1]匹配,OA2[7:1]不做比较就可以使I2C通讯了
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
//I2C_OAR2寄存器bit15(OA2EN),令OA2EN=0不使能“双地址模式”,即不使用自身地址2(OA2[7:1])
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
//I2C_CR2寄存器bit11(ADD10),令ADD10=0表示I2C主机工作在7位地址模式,则对应的I2C从机的器件地址必须是7位
//I2C主机和从机地址模式相同,因此从机地址占7位
#else
hi2c1.Init.OwnAddress1 = I2C_Own_Address;
//I2C_OAR1寄存器bit9:0(OA1[9:0]),自身地址1:OA1[9:0]=hi2c->Init.OwnAddress1
hi2c1.Init.OwnAddress2 = 0;
//I2C_OAR2寄存器bit17:1(OA2[7:1]),自身地址2:OA2[7:1]=hi2c->Init.OwnAddress2
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
//I2C_OAR2寄存器bit110:8(OA2MSK[2:0]),自身地址2的掩码OA2MSK[2:0]=000b,自身地址2(OA2[7:1])无掩码
//OA2MSK[2:0]=000b,OA2[7:1]必须要全部匹配才能使I2C正常通讯
//OA2MSK[2:0]=001b,忽略OA2[1]匹配,但OA2[7:2]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=010b,忽略OA2[2:1]匹配,但OA2[7:3]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=011b,忽略OA2[3:1]匹配,但OA2[7:4]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=100b,忽略OA2[4:1]匹配,但OA2[7:5]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=101b,忽略OA2[5:1]匹配,但OA2[7:6]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=110b,忽略OA2[6:1]匹配,但OA2[7]必须要匹配才能使I2C正常通讯
//OA2MSK[2:0]=111b,忽略OA2[7:1]匹配,OA2[7:1]不做比较就可以使I2C通讯了
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_ENABLE;
//I2C_OAR2寄存器bit15(OA2EN),令OA2EN=1使能“双地址模式”,即使用自身地址2(OA2[7:1])
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_10BIT;
//I2C_CR2寄存器bit11(ADD10),令ADD10=1表示I2C主机工作在10位地址模式,则对应的I2C从机的器件地址必须是10位
//I2C主机和从机地址模式相同,因此从机地址占10位
#endif
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
//I2C_CR1寄存器bit19(GCEN),GCEN=0不使能“普通地址访问”
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
//I2C_CR1寄存器bit17(NOSTRETCH),NOSTRETCH=0表示“SCL时钟不拉伸”不使能,意思是允许时钟拉伸;
//即在从机应答期间,SCL的低电平时间会被拉长了。
HAL_I2C_Init(&hi2c1);
HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE);
//使能“模拟滤波器”,并使能I2C外设,Analog filter delay is maximum 260 ns.
HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0);
//不使能“数字滤波器”,并使能I2C外设,Digital filter delay is DNF x tI2CCLK.
__HAL_SYSCFG_FASTMODEPLUS_ENABLE(I2C_FASTMODEPLUS_I2C1);
//使能“I2C1快速模式+”,I2C Fast mode Plus enable
//配置I2C1快速模式(1MHz)和驱动能力
}
//开始24LC256驱动程序
/**-------------------------------------------------------------------------------
函数说明:从EEPROM芯片24LC256的某个地址单元addr,读取到rerurn_value中.
并将读到的值返回;
---------------------------------------------------------------------------------*/
uint8_t EEPROM_U8_Data_Read1(uint16_t addr)
{
uint8_t rerurn_value;
uint8_t ret,cnt,i;
uint32_t Timeout;
delay_ms(1); //在连续进行"字节连续读"时,此处必须加delay_ms(5),否则程序可能读不到EEPROM中的数据;
Timeout=10;
ret=0;
cnt=Timeout;
while (_HAL_I2C_GET_FLAG(I2C1, I2C_FLAG_BUSY) == SET)
{//等待I2C总线空闲
delay_ms(1);
cnt--;
if (cnt== 0)
{ ret=I2C_Err_BUSY;//表示I2C忙超时
break;
}
}
if(ret==0)//发送“器件地址”
{
#if EEPROM_Device_SubAddress_Size == 0 //I2C从机“器件子地址”为1个字节
My_I2C_TransferConfig(I2C1,EEPROM_Device_Address, 1, I2C_SOFTEND_MODE, I2C_GENERATE_START_WRITE);
//发送“启动条件”和“写从机器件地址”,发送字节数为1
//Request=I2C_GENERATE_START_WRITE表示需要产生I2C启动条件,主机请求写
//I2C_CR2寄存器bit25(AUTOEND),Mode=I2C_SOFTEND_MODE表示“软件结束模式”,当NBYTES[7:0]个字节被发送完后,SCL被拉伸
//I2C_CR2寄存器bit24(RELOAD),Mode=I2C_SOFTEND_MODE表示当NBYTES[7:0]个字节被发送完后,传送器完成发送,要求后面紧接着是“停止条件或重启条件”
#else //I2C从机“器件子地址”为2个字节
My_I2C_TransferConfig(I2C1,EEPROM_Device_Address, 2, I2C_SOFTEND_MODE, I2C_GENERATE_START_WRITE);
//发送“启动条件”和“写从机器件地址”,发送字节数为2
//Request=I2C_GENERATE_START_WRITE表示需要产生I2C启动条件,主机请求写
//I2C_CR2寄存器bit25(AUTOEND),Mode=I2C_SOFTEND_MODE表示“软件结束模式”,当NBYTES[7:0]个字节被发送完后,SCL被拉伸
//I2C_CR2寄存器bit24(RELOAD),Mode=I2C_SOFTEND_MODE表示当NBYTES[7:0]个字节被发送完后,传送器完成发送,要求后面紧接着是“停止条件或重启条件”
#endif
/* Wait until TXIS flag is set */
if (My_I2C_WaitOnTXISFlagUntilTimeout(I2C1,Timeout))
{ //返回值为0,表示I2C操作写完成
//返回值为1,表示读到了NACK位
//返回值为2,表示没有接收到“I2C停止条件”
//返回值为3,表示TXIS=0,发送器件地址失败
ret=I2C_Err_TXIS_Zero;//表示写超时
}
}
if(ret==0)//发送“器件子地址”
{
#if EEPROM_Device_SubAddress_Size == 0 //I2C从机“器件子地址”为8位数据
I2C1->TXDR = I2C_MEM_ADD_LSB(addr);//发送“器件子地址”
#else //I2C从机“器件子地址”为16位数据
I2C1->TXDR = I2C_MEM_ADD_MSB(addr);//发送“器件子地址高8位”
/* Wait until TXIS flag is set */
if (My_I2C_WaitOnTXISFlagUntilTimeout(I2C1,Timeout))
{ //返回值为0,表示TXIS=1
//返回值为1,表示读到了NACK位
//返回值为2,表示没有接收到“I2C停止条件”
//返回值为3,表示TXIS=0,发送器件地址失败
ret=I2C_Err_TXIS_Zero;//表示TXIS=0,发送器件地址失败
}
if(ret==0) I2C1->TXDR = I2C_MEM_ADD_LSB(addr);//发送“器件子地址低8位”
#endif
if ( ret==0 && My_I2C_WaitOnFlagUntilTimeout(I2C1,I2C_FLAG_TC, RESET, Timeout) )
{ //读I2C_ISR寄存器bit6(TC),等待I2C主机传送完“I2C_CR2寄存器bit23:16(NBYTES[7:0])”个数据后TC=1,
//当“RELOAD=0, AUTOEND=0”时,NBYTES[7:0]个数据被发送完成后令TC=1,而当发送“启动条件或停止条件”时令TC=0
//这里是查询“器件子地址”是否发送完成
ret=I2C_Err_TC_Zero;//表示TC=0,发送器件子地址失败
}
}
if(ret==0)//主备读I2C从机的数据
{
My_I2C_TransferConfig(I2C1,EEPROM_Device_Address, 1, I2C_AUTOEND_MODE, I2C_GENERATE_START_READ);
//发送“重启条件”和“读从机器件地址EEPROM_Device_Address”,发送字节数为n
//I2C_CR2寄存器bit25(AUTOEND),I2C_AUTOEND_MODE表示“自动结束模式”,即一次可以读完
//I2C_GENERATE_START_READ表示需要产生I2C启动条件,主机请求读
if (My_I2C_WaitOnFlagUntilTimeout(I2C1,I2C_FLAG_RXNE, RESET, Timeout))
{//等待新数据
ret=I2C_Err_RXNE_Zero;//表示RXNE=0,读数据超时
}
if(ret==0)//读到新数据
{
rerurn_value = (uint8_t)I2C1->RXDR;//保存接收到新数据
}
/* No need to Check TC flag, with AUTOEND mode the stop is automatically generated */
/* Wait until STOPF flag is reset */
if ( ret==0 && My_I2C_WaitOnSTOPFlagUntilTimeout(I2C1,Timeout))
{//读I2C_ISR寄存器bit5(STOPF),等待“I2C停止条件”STOPF=1,并查询I2C主机发送一个字节后是否建立NACKF标志
ret=I2C_Err_STOP_Zero;//表示STOP=0,没有收到停止条件
}
if(ret==0)//发送停止条件正确
{
/* Clear STOP Flag */
_HAL_I2C_CLEAR_FLAG(I2C1,I2C_FLAG_STOPF);
//设置I2C_ICR寄存器bit5(STOPCF)为1,清除“I2C_ISR寄存器bit5(STOPF),“I2C停止条件”标志”
/* Clear Configuration Register 2 */
_I2C_RESET_CR2(I2C1);
//清除“I2C_CR2寄存器bit9:0(SADD[9:0]),bit10(RD_WRN),bit12(HEAD10R),bit23:16(NBYTES[7:0]),bit24(RELOAD)”
}
}
return rerurn_value;
}
上面的代码,是从HAL库中抠出来,读EEPROM是可以的。HAL库的代码确实太复杂,不适合实际使用。