×
单片机 > 单片机程序设计 > 详情

STM32F10x硬件I2c读写AT24c02,程序卡死

发布时间:2020-08-21 发布时间:
|

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电平变化!


这个是头文件:


  1. #ifndef __I2C_EE_H  

  2. #define __I2C_EE_H  

  3.   

  4. #include   

  5.   

  6. /* EEPROM Addresses defines */  

  7. #define EEPROM_Block0_ADDRESS 0xA0   /* E2 = 0 *///AT24C0x的I2C硬件地址  

  8. //#define EEPROM_Block1_ADDRESS 0xA2 /* E2 = 0 */  

  9. //#define EEPROM_Block2_ADDRESS 0xA4 /* E2 = 0 */  

  10. //#define EEPROM_Block3_ADDRESS 0xA6 /* E2 = 0 */  

  11.   

  12. void I2C_EE_Init(void);  

  13. void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite);  

  14. void I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr);  

  15. void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite);  

  16. void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead);  

  17. void I2C_EE_WaitEepromStandbyState(void);  

  18.   

  19. #endif /* __I2C_EE_H */  


下面是C文件:


  1. /******************************************************************************** 

  2.  * 文件名  :i2c_ee.c 

  3.  * 描述    :i2c EEPROM(AT24C02)应用函数库          

  4.  * 实验平台:野火STM32开发板 

  5.  * 硬件连接:----------------- 

  6.  *          |                 | 

  7.  *          |  PB6-I2C1_SCL   | 

  8.  *          |  PB7-I2C1_SDA   | 

  9.  *          |                 | 

  10.  *           ----------------- 

  11.  * 库版本  :ST3.5.0 

  12.  * 作者    :保留  

  13.  * 论坛    :http://www.amobbs.com/forum-1008-1.html 

  14.  * 淘宝    :http://firestm32.taobao.com 

  15. **********************************************************************************/  

  16. #include "i2c_ee.h"  

  17.   

  18. #define I2C_Speed              200000  

  19. #define I2C1_OWN_ADDRESS7    0x0A           //stm32z自身I2C地址,自己定义的  

  20. #define I2C_PageSize           8            /* AT24C02每页有8个字节 */  

  21.   

  22. u16 EEPROM_ADDRESS;  

  23.   

  24. /* 

  25.  * 函数名:I2C_GPIO_Config 

  26.  * 描述  :I2C1 I/O配置 

  27.  * 输入  :无 

  28.  * 输出  :无 

  29.  * 调用  :内部调用 

  30.  */  

  31. static void I2C_GPIO_Config(void)  

  32. {  

  33.   GPIO_InitTypeDef  GPIO_InitStructure;   

  34.   

  35.     // 使能与 I2C1 有关的时钟 /  

  36.   //RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  

  37.   //RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);    

  38.       

  39.   // PB6-I2C1_SCL、PB7-I2C1_SDA/  

  40.   GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;  

  41.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

  42.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;          // 开漏输出  

  43.   GPIO_Init(GPIOB, &GPIO_InitStructure);  

  44. }  

  45.   

  46. /* 

  47.  * 函数名:I2C_Configuration 

  48.  * 描述  :I2C 工作模式配置 

  49.  * 输入  :无 

  50.  * 输出  :无 

  51.  * 调用  :内部调用 

  52.  */  

  53. static void I2C_Mode_Configu(void)  

  54. {  

  55.   I2C_InitTypeDef  I2C_InitStructure;   

  56.   

  57.   /* I2C 配置 */  

  58.   I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;  

  59.   I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;  

  60.   I2C_InitStructure.I2C_OwnAddress1 =I2C1_OWN_ADDRESS7;   

  61.   I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;  

  62.   I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;  

  63.   I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;  

  64.     

  65.   /* 使能 I2C1 */  

  66.   I2C_Cmd(I2C1, ENABLE);  

  67.   

  68.   /* I2C1 初始化 */  

  69.   I2C_Init(I2C1, &I2C_InitStructure);  

  70.   

  71.      

  72. }  

  73.   

  74. /* 

  75.  * 函数名:I2C_EE_Init 

  76.  * 描述  :I2C 外设(EEPROM)初始化 

  77.  * 输入  :无 

  78.  * 输出  :无 

  79.  * 调用  :外部调用 

  80.  */  

  81. void I2C_EE_Init(void)  

  82. {  

  83.   

  84.   I2C_GPIO_Config();   

  85.    

  86.   I2C_Mode_Configu();  

  87.   

  88. /* 根据头文件i2c_ee.h中的定义来选择EEPROM要写入的地址 */  

  89. #ifdef EEPROM_Block0_ADDRESS  

  90.   /* 选择 EEPROM Block0 来写入 */  

  91.   EEPROM_ADDRESS = EEPROM_Block0_ADDRESS;  

  92. #endif  

  93.   

  94. #ifdef EEPROM_Block1_ADDRESS    

  95.     /* 选择 EEPROM Block1 来写入 */  

  96.   EEPROM_ADDRESS = EEPROM_Block1_ADDRESS;  

  97. #endif  

  98.   

  99. #ifdef EEPROM_Block2_ADDRESS    

  100.     /* 选择 EEPROM Block2 来写入 */  

  101.   EEPROM_ADDRESS = EEPROM_Block2_ADDRESS;  

  102. #endif  

  103.   

  104. #ifdef EEPROM_Block3_ADDRESS    

  105.     /* 选择 EEPROM Block3 来写入 */  

  106.   EEPROM_ADDRESS = EEPROM_Block3_ADDRESS;  

  107. #endif  

  108. }  

  109.   

  110. /* 

  111.  * 函数名:I2C_EE_BufferWrite 

  112.  * 描述  :将缓冲区中的数据写到I2C EEPROM中 

  113.  * 输入  :-pBuffer 缓冲区指针 

  114.  *         -WriteAddr 接收数据的EEPROM的地址 

  115.  *         -NumByteToWrite 要写入EEPROM的字节数 

  116.  * 输出  :无 

  117.  * 返回  :无 

  118.  * 调用  :外部调用 

  119.  */  

  120. void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)  

  121. {  

  122.   u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;  

  123.   

  124.   Addr = WriteAddr % I2C_PageSize;//通过计算写入的初始地址是否从页的开始写  

  125.   count = I2C_PageSize - Addr;     //计算写的第一页需要写几个字节(写的首地址不在页的开始)  

  126.   NumOfPage =  NumByteToWrite / I2C_PageSize;//计算需要写几个整页  

  127.   NumOfSingle = NumByteToWrite % I2C_PageSize;//计算写完整页后还剩几个字节没有写  

  128.    

  129.   /* If WriteAddr is I2C_PageSize aligned  */  

  130.   if(Addr == 0) //通写入的初始地址是从页的开始写  

  131.   {  

  132.     /* If NumByteToWrite 

  133.     if(NumOfPage == 0) //如果写的小于一页,也就是只写NumOfSingle个数据  

  134.     {  

  135.       I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);  

  136.       I2C_EE_WaitEepromStandbyState();  

  137.     }  

  138.     /* If NumByteToWrite > I2C_PageSize */  

  139.     else  //如果写的大于等于一页,也就是写NumOfPage个整页       

  140.     {  

  141.       while(NumOfPage--)  

  142.       {  

  143.         I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);   

  144.         I2C_EE_WaitEepromStandbyState();  

  145.         WriteAddr +=  I2C_PageSize;  

  146.         pBuffer += I2C_PageSize;  

  147.       }  

  148.   

  149.       if(NumOfSingle!=0) //写完整页后剩下的不够一页的  

  150.       {  

  151.         I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);  

  152.         I2C_EE_WaitEepromStandbyState();  

  153.       }  

  154.     }  

  155.   }  

  156.   /* If WriteAddr is not I2C_PageSize aligned  */  

  157.   else //通写入的初始地址不是从页的开始写,这个要比前面的要复杂很多  

  158.   {  

  159.     /* If NumByteToWrite 

  160.     if(NumOfPage== 0)   

  161.     {  

  162.       I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);  

  163.       I2C_EE_WaitEepromStandbyState();  

  164.     }  

  165.     /* If NumByteToWrite > I2C_PageSize */  

  166.     else  

  167.     {  

  168.       NumByteToWrite -= count;  

  169.       NumOfPage =  NumByteToWrite / I2C_PageSize;  

  170.       NumOfSingle = NumByteToWrite % I2C_PageSize;    

  171.         

  172.       if(count != 0) //先把第一页的几个数据写进去例如我写的首地址为5,则count=3,则从5写到7  

  173.       {    

  174.         I2C_EE_PageWrite(pBuffer, WriteAddr, count);  

  175.         I2C_EE_WaitEepromStandbyState();  

  176.         WriteAddr += count;   //写地址加上count,前面没有对齐的数据写完,该写中间对齐的部分  

  177.         pBuffer += count;  

  178.       }   

  179.         

  180.       while(NumOfPage--)  

  181.       {  

  182.         I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);  

  183.         I2C_EE_WaitEepromStandbyState();  

  184.         WriteAddr +=  I2C_PageSize;  

  185.         pBuffer += I2C_PageSize;    

  186.       }  

  187.       if(NumOfSingle != 0)//最后写最后几个没有对齐的数据,不够一整页  

  188.       {  

  189.         I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);   

  190.         I2C_EE_WaitEepromStandbyState();  

  191.       }  

  192.     }  

  193.   }    

  194. }  

  195.   

  196. /* 

  197.  * 函数名:I2C_EE_ByteWrite 

  198.  * 描述  :写一个字节到I2C EEPROM中 

  199.  * 输入  :-pBuffer 缓冲区指针 

  200.  *         -WriteAddr 接收数据的EEPROM的地址  

  201.  * 输出  :无 

  202.  * 返回  :无 

  203.  * 调用  :外部调用 

  204.  */  

  205. void I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)  

  206. {  

  207.   /* Send STRAT condition */  

  208.   I2C_GenerateSTART(I2C1, ENABLE);  

  209.   

  210.   /* Test on EV5 and clear it */  

  211.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));    

  212.   

  213.   /* Send EEPROM address for write */  

  214.   I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);  

  215.     

  216.   /* Test on EV6 and clear it */  

  217.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));  

  218.         

  219.   /* Send the EEPROM's internal address to write to */  

  220.   I2C_SendData(I2C1, WriteAddr);  

  221.     

  222.   /* Test on EV8 and clear it */  

  223.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));  

  224.   

  225.   /* Send the byte to be written */  

  226.   I2C_SendData(I2C1, *pBuffer);   

  227.      

  228.   /* Test on EV8 and clear it */  

  229.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));  

  230.     

  231.   /* Send STOP condition */  

  232.   I2C_GenerateSTOP(I2C1, ENABLE);  

  233. }  

  234.   

  235. /* 

  236.  * 函数名:I2C_EE_PageWrite 

  237.  * 描述  :在EEPROM的一个写循环中可以写多个字节,但一次写入的字节数 

  238.  *         不能超过EEPROM页的大小。AT24C02每页有8个字节。 

  239.  * 输入  :-pBuffer 缓冲区指针 

  240.  *         -WriteAddr 接收数据的EEPROM的地址  

  241.  *         -NumByteToWrite 要写入EEPROM的字节数 

  242.  * 输出  :无 

  243.  * 返回  :无 

  244.  * 调用  :外部调用 

  245.  */  

  246. void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)  

  247. {  

  248.     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // Added by Najoua 27/08/2008  

  249.       

  250.   /* Send START condition */  

  251.   I2C_GenerateSTART(I2C1, ENABLE);  

  252.     

  253.   /* Test on EV5 and clear it */  

  254.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));   

  255.     

  256.   /* Send EEPROM address for write */  

  257.   I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);  

  258.     

  259.   /* Test on EV6 and clear it */  

  260.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));    

  261.   

  262.   /* Send the EEPROM's internal address to write to */      

  263.   I2C_SendData(I2C1, WriteAddr);    

  264.   

  265.   /* Test on EV8 and clear it */  

  266.   while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));  

  267.   

  268.   /* While there is data to be written */  

  269.   while(NumByteToWrite--)    

  270.   {  

  271.     /* Send the current byte */  

  272.     I2C_SendData(I2C1, *pBuffer);   

  273.   

  274.     /* Point to the next byte to be written */  

  275.     pBuffer++;   

  276.     

  277.     /* Test on EV8 and clear it */  

  278.     while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));  

  279.   }  

  280.   

  281.   /* Send STOP condition */  

  282.   I2C_GenerateSTOP(I2C1, ENABLE);  

  283. }  

  284.   

  285.   

  286. /* 

  287.  * 函数名:I2C_EE_BufferRead 

  288.  * 描述  :从EEPROM里面读取一块数据。  

  289.  * 输入  :-pBuffer 存放从EEPROM读取的数据的缓冲区指针。 

  290.  *         -WriteAddr 接收数据的EEPROM的地址。  

  291.  *         -NumByteToWrite 要从EEPROM读取的字节数。 

  292.  * 输出  :无 

  293.  * 返回  :无 

  294.  * 调用  :外部调用 

  295.  */  

  296. void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)  

  297. {    

  298.   //*((u8 *)0x4001080c) |=0x80;   

  299.     while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // Added by Najoua 27/08/2008  

  300.       

  301.       

  302.   /* Send START condition */  

  303.   I2C_GenerateSTART(I2C1, ENABLE);  

  304.   //*((u8 *)0x4001080c) &=~0x80;  

  305.     

  306.   /* Test on EV5 and clear it */  

  307.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));  

  308.   

  309.   /* Send EEPROM address for write */  

  310.   I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);  

  311.   

  312.   /* Test on EV6 and clear it */  

  313.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));  

  314.     

  315.   /* Clear EV6 by setting again the PE bit */  

  316.   I2C_Cmd(I2C1, ENABLE);  

  317.   

  318.   /* Send the EEPROM's internal address to write to */  

  319.   I2C_SendData(I2C1, ReadAddr);    

  320.   

  321.   /* Test on EV8 and clear it */  

  322.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));  

  323.     

  324.   /* Send STRAT condition a second time */    

  325.   I2C_GenerateSTART(I2C1, ENABLE);  

  326.     

  327.   /* Test on EV5 and clear it */  

  328.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));  

  329.     

  330.   /* Send EEPROM address for read */  

  331.   I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);  

  332.     

  333.   /* Test on EV6 and clear it */  

  334.   while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));  

  335.     

  336.   /* While there is data to be read */  

  337.   while(NumByteToRead)    

  338.   {  

  339.     if(NumByteToRead == 1)  

  340.     {  

  341.       /* Disable Acknowledgement */  

  342.       I2C_AcknowledgeConfig(I2C1, DISABLE);  

  343.         

  344.       /* Send STOP Condition */  

  345.       I2C_GenerateSTOP(I2C1, ENABLE);  

  346.     }  

  347.   

  348.     /* Test on EV7 and clear it */  

  349.     if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))    

  350.     {        

  351.       /* Read a byte from the EEPROM */  

  352.       *pBuffer = I2C_ReceiveData(I2C1);  

  353.   

  354.       /* Point to the next location where the byte read will be saved */  

  355.       pBuffer++;   

  356.         

  357.       /* Decrement the read bytes counter */  

  358.       NumByteToRead--;          

  359.     }     

  360.   }  

  361.   

  362.   /* Enable Acknowledgement to be ready for another reception */  

  363.   I2C_AcknowledgeConfig(I2C1, ENABLE);  

  364. }  

  365.   

  366.   

  367. /* 

  368.  * 函数名:I2C_EE_WaitEepromStandbyState 

  369.  * 描述  :Wait for EEPROM Standby state  

  370.  * 输入  :无 

  371.  * 输出  :无 

  372.  * 返回  :无 

  373.  * 调用  :  

  374.  */  

  375. void I2C_EE_WaitEepromStandbyState(void)        

  376. {  

  377.   vu16 SR1_Tmp = 0;  

  378.   

  379.   do  

  380.   {  

  381.     /* Send START condition */  

  382.     I2C_GenerateSTART(I2C1, ENABLE);  

  383.     /* Read I2C1 SR1 register */  

  384.     SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);  

  385.     /* Send EEPROM address for write */  

  386.     I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);  

  387.   }while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));  

  388.     

  389.   /* Clear AF flag */  

  390.   I2C_ClearFlag(I2C1, I2C_FLAG_AF);  

  391.     /* STOP condition */      

  392.     I2C_GenerateSTOP(I2C1, ENABLE);   

  393. }  

  394.   

  395. /*************************END OF FILE*************************************/ 





『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
Keil(MDK-ARM)系列教程(七)_菜单