STM32F10x硬件I2c读写AT24c02,程序卡死
扫描二维码
随时随地手机看文章
stm32是自带硬件I2C,相比于软件模拟I2c,硬件I2c效率更高。但是据说不稳定,这个我倒暂时还没有体会到。
在最开始使用硬件I2c的时候,程序总是卡死,要不从一开始就卡死,要不从某一步开始卡死。我初学stm32,所以每学习一个模块都会把程序放进去,程序相互叠加。但是今天移植别人的硬件I2C却总是不能成功。一直是卡死在while等待;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
下面进行简单的分析:
1,很多时候,如果在线调试中发现从程序的第一个while就卡死,那很可能是配置有问题;比如SDA/SCL是否为开漏,I2C1时钟,GPIOB时钟是否开启,是否重复使用IO口导致异常,这里有个好方法就是把程序精简,就是把程序最简化,然后查错,纠正错误后再加;
2,可以用万用表看看你的IO口空闲时是不是上拉到高电平,我的程序就是用万用表测量SDA/SCL空闲不为高,这就有问题了,我查了很长时间没有引脚用重复,于是把多的程序全部删掉就好了。
3,这位楼主STM32 库I2c 调试成功!!!也是遇到功能用重复了,这里直接把帖子贴出来。
4,有位博主说他的硬件I2C 10K速率时稳定,但是我觉得不至于,我用200K读写都很正常,如果上诉不能解决,可以调整下速率看看效果。
之前写了一个写单字节的读写程序不能用,没办法我移植的是野火的《stm32库开发实战指南》的硬件I2C读写24C02的例程,下面贴上代码:(加上了少部分注释)
5, 经本人反复折腾发现,引脚在你认为的“空闲”时间表现为低电平的的另一种可能,就是程序写的有问题,AT24c0x把总线拉低表现为等待主机响应,要想知道是什么原因导致总线电平被拉低,可以重新插从器件供电,并使主器件复位(因为此时的主器件一般处于卡死循环状态),在不触发AT24c0x的情况下,测SDA,SCL的电平状态,查看是否为高电平,再触发读写从器件,结束后测SDA.SCL电平变化!
这个是头文件:
#ifndef __I2C_EE_H #define __I2C_EE_H #include/* EEPROM Addresses defines */ #define EEPROM_Block0_ADDRESS 0xA0 /* E2 = 0 *///AT24C0x的I2C硬件地址 //#define EEPROM_Block1_ADDRESS 0xA2 /* E2 = 0 */ //#define EEPROM_Block2_ADDRESS 0xA4 /* E2 = 0 */ //#define EEPROM_Block3_ADDRESS 0xA6 /* E2 = 0 */ void I2C_EE_Init(void); void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite); void I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr); void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite); void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead); void I2C_EE_WaitEepromStandbyState(void); #endif /* __I2C_EE_H */
下面是C文件:
/******************************************************************************** * 文件名 :i2c_ee.c * 描述 :i2c EEPROM(AT24C02)应用函数库 * 实验平台:野火STM32开发板 * 硬件连接:----------------- * | | * | PB6-I2C1_SCL | * | PB7-I2C1_SDA | * | | * ----------------- * 库版本 :ST3.5.0 * 作者 :保留 * 论坛 :http://www.amobbs.com/forum-1008-1.html * 淘宝 :http://firestm32.taobao.com **********************************************************************************/ #include "i2c_ee.h" #define I2C_Speed 200000 #define I2C1_OWN_ADDRESS7 0x0A //stm32z自身I2C地址,自己定义的 #define I2C_PageSize 8 /* AT24C02每页有8个字节 */ u16 EEPROM_ADDRESS; /* * 函数名:I2C_GPIO_Config * 描述 :I2C1 I/O配置 * 输入 :无 * 输出 :无 * 调用 :内部调用 */ static void I2C_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能与 I2C1 有关的时钟 / //RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); // PB6-I2C1_SCL、PB7-I2C1_SDA/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出 GPIO_Init(GPIOB, &GPIO_InitStructure); } /* * 函数名:I2C_Configuration * 描述 :I2C 工作模式配置 * 输入 :无 * 输出 :无 * 调用 :内部调用 */ static void I2C_Mode_Configu(void) { I2C_InitTypeDef I2C_InitStructure; /* I2C 配置 */ I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 =I2C1_OWN_ADDRESS7; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = I2C_Speed; /* 使能 I2C1 */ I2C_Cmd(I2C1, ENABLE); /* I2C1 初始化 */ I2C_Init(I2C1, &I2C_InitStructure); } /* * 函数名:I2C_EE_Init * 描述 :I2C 外设(EEPROM)初始化 * 输入 :无 * 输出 :无 * 调用 :外部调用 */ void I2C_EE_Init(void) { I2C_GPIO_Config(); I2C_Mode_Configu(); /* 根据头文件i2c_ee.h中的定义来选择EEPROM要写入的地址 */ #ifdef EEPROM_Block0_ADDRESS /* 选择 EEPROM Block0 来写入 */ EEPROM_ADDRESS = EEPROM_Block0_ADDRESS; #endif #ifdef EEPROM_Block1_ADDRESS /* 选择 EEPROM Block1 来写入 */ EEPROM_ADDRESS = EEPROM_Block1_ADDRESS; #endif #ifdef EEPROM_Block2_ADDRESS /* 选择 EEPROM Block2 来写入 */ EEPROM_ADDRESS = EEPROM_Block2_ADDRESS; #endif #ifdef EEPROM_Block3_ADDRESS /* 选择 EEPROM Block3 来写入 */ EEPROM_ADDRESS = EEPROM_Block3_ADDRESS; #endif } /* * 函数名:I2C_EE_BufferWrite * 描述 :将缓冲区中的数据写到I2C EEPROM中 * 输入 :-pBuffer 缓冲区指针 * -WriteAddr 接收数据的EEPROM的地址 * -NumByteToWrite 要写入EEPROM的字节数 * 输出 :无 * 返回 :无 * 调用 :外部调用 */ void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite) { u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0; Addr = WriteAddr % I2C_PageSize;//通过计算写入的初始地址是否从页的开始写 count = I2C_PageSize - Addr; //计算写的第一页需要写几个字节(写的首地址不在页的开始) NumOfPage = NumByteToWrite / I2C_PageSize;//计算需要写几个整页 NumOfSingle = NumByteToWrite % I2C_PageSize;//计算写完整页后还剩几个字节没有写 /* If WriteAddr is I2C_PageSize aligned */ if(Addr == 0) //通写入的初始地址是从页的开始写 { /* If NumByteToWrite < I2C_PageSize */ if(NumOfPage == 0) //如果写的小于一页,也就是只写NumOfSingle个数据 { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState(); } /* If NumByteToWrite > I2C_PageSize */ else //如果写的大于等于一页,也就是写NumOfPage个整页 { while(NumOfPage--) { I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); I2C_EE_WaitEepromStandbyState(); WriteAddr += I2C_PageSize; pBuffer += I2C_PageSize; } if(NumOfSingle!=0) //写完整页后剩下的不够一页的 { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState(); } } } /* If WriteAddr is not I2C_PageSize aligned */ else //通写入的初始地址不是从页的开始写,这个要比前面的要复杂很多 { /* If NumByteToWrite < I2C_PageSize */ if(NumOfPage== 0) { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState(); } /* If NumByteToWrite > I2C_PageSize */ else { NumByteToWrite -= count; NumOfPage = NumByteToWrite / I2C_PageSize; NumOfSingle = NumByteToWrite % I2C_PageSize; if(count != 0) //先把第一页的几个数据写进去例如我写的首地址为5,则count=3,则从5写到7 { I2C_EE_PageWrite(pBuffer, WriteAddr, count); I2C_EE_WaitEepromStandbyState(); WriteAddr += count; //写地址加上count,前面没有对齐的数据写完,该写中间对齐的部分 pBuffer += count; } while(NumOfPage--) { I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); I2C_EE_WaitEepromStandbyState(); WriteAddr += I2C_PageSize; pBuffer += I2C_PageSize; } if(NumOfSingle != 0)//最后写最后几个没有对齐的数据,不够一整页 { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState(); } } } } /* * 函数名:I2C_EE_ByteWrite * 描述 :写一个字节到I2C EEPROM中 * 输入 :-pBuffer 缓冲区指针 * -WriteAddr 接收数据的EEPROM的地址 * 输出 :无 * 返回 :无 * 调用 :外部调用 */ void I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr) { /* Send STRAT condition */ I2C_GenerateSTART(I2C1, ENABLE); /* Test on EV5 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); /* Send EEPROM address for write */ I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); /* Test on EV6 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); /* Send the EEPROM's internal address to write to */ I2C_SendData(I2C1, WriteAddr); /* Test on EV8 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); /* Send the byte to be written */ I2C_SendData(I2C1, *pBuffer); /* Test on EV8 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); /* Send STOP condition */ I2C_GenerateSTOP(I2C1, ENABLE); } /* * 函数名:I2C_EE_PageWrite * 描述 :在EEPROM的一个写循环中可以写多个字节,但一次写入的字节数 * 不能超过EEPROM页的大小。AT24C02每页有8个字节。 * 输入 :-pBuffer 缓冲区指针 * -WriteAddr 接收数据的EEPROM的地址 * -NumByteToWrite 要写入EEPROM的字节数 * 输出 :无 * 返回 :无 * 调用 :外部调用 */ void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite) { while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // Added by Najoua 27/08/2008 /* Send START condition */ I2C_GenerateSTART(I2C1, ENABLE); /* Test on EV5 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); /* Send EEPROM address for write */ I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); /* Test on EV6 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); /* Send the EEPROM's internal address to write to */ I2C_SendData(I2C1, WriteAddr); /* Test on EV8 and clear it */ while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); /* While there is data to be written */ while(NumByteToWrite--) { /* Send the current byte */ I2C_SendData(I2C1, *pBuffer); /* Point to the next byte to be written */ pBuffer++; /* Test on EV8 and clear it */ while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); } /* Send STOP condition */ I2C_GenerateSTOP(I2C1, ENABLE); } /* * 函数名:I2C_EE_BufferRead * 描述 :从EEPROM里面读取一块数据。 * 输入 :-pBuffer 存放从EEPROM读取的数据的缓冲区指针。 * -WriteAddr 接收数据的EEPROM的地址。 * -NumByteToWrite 要从EEPROM读取的字节数。 * 输出 :无 * 返回 :无 * 调用 :外部调用 */ void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead) { //*((u8 *)0x4001080c) |=0x80; while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // Added by Najoua 27/08/2008 /* Send START condition */ I2C_GenerateSTART(I2C1, ENABLE); //*((u8 *)0x4001080c) &=~0x80; /* Test on EV5 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); /* Send EEPROM address for write */ I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); /* Test on EV6 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); /* Clear EV6 by setting again the PE bit */ I2C_Cmd(I2C1, ENABLE); /* Send the EEPROM's internal address to write to */ I2C_SendData(I2C1, ReadAddr); /* Test on EV8 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); /* Send STRAT condition a second time */ I2C_GenerateSTART(I2C1, ENABLE); /* Test on EV5 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); /* Send EEPROM address for read */ I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver); /* Test on EV6 and clear it */ while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); /* While there is data to be read */ while(NumByteToRead) { if(NumByteToRead == 1) { /* Disable Acknowledgement */ I2C_AcknowledgeConfig(I2C1, DISABLE); /* Send STOP Condition */ I2C_GenerateSTOP(I2C1, ENABLE); } /* Test on EV7 and clear it */ if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) { /* Read a byte from the EEPROM */ *pBuffer = I2C_ReceiveData(I2C1); /* Point to the next location where the byte read will be saved */ pBuffer++; /* Decrement the read bytes counter */ NumByteToRead--; } } /* Enable Acknowledgement to be ready for another reception */ I2C_AcknowledgeConfig(I2C1, ENABLE); } /* * 函数名:I2C_EE_WaitEepromStandbyState * 描述 :Wait for EEPROM Standby state * 输入 :无 * 输出 :无 * 返回 :无 * 调用 : */ void I2C_EE_WaitEepromStandbyState(void) { vu16 SR1_Tmp = 0; do { /* Send START condition */ I2C_GenerateSTART(I2C1, ENABLE); /* Read I2C1 SR1 register */ SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1); /* Send EEPROM address for write */ I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); }while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002)); /* Clear AF flag */ I2C_ClearFlag(I2C1, I2C_FLAG_AF); /* STOP condition */ I2C_GenerateSTOP(I2C1, ENABLE); } /*************************END OF FILE*************************************/