OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4995|回复: 13

STM32的硬件I2C1读取数据问题

[复制链接]

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
发表于 2021-11-14 21:58:54 | 显示全部楼层 |阅读模式
1金钱
STM32的I2C硬件的确难玩,I2C2没反应,I2C1发送读取命令,居然会自动读取第一个字节内容,也就是第一个内容在发送读取命令时就莫名其妙的出来了,全程没有半个I2C_ReceiveData函数调用,实际永远无法读取!!,因为发送读命令时就飞掉了!!但是类似代码,发送写命令完全正常,如下图:
A.png
代码如下,哪位高手看下哪里除了问题?
====================================
//测试板为迷你开发板
#include "stm32f10x_i2c.h"

int main(void)
{
        RCC->APB2ENR |=1<<2;
        RCC->APB2ENR |=1<<4;
        GPIOA->CRH &=0xFFFFFFF0;
        GPIOA->CRH |=3;
        GPIOA->BRR=1<<8;//点亮LED0
        GPIOC->CRL &=0xFF0FFFFF;
        GPIOC->CRL |=8<<20;
        GPIOC->BSRR=1<<5;
        RCC->APB2ENR |=1 | (1<<3);//开启GPIOB,AFIO时钟
        RCC->APB1ENR |=1<<21;//打开I2C1时钟
        //设置PB6,PB7为复用开漏输出
        GPIOB->CRL &=0x00FFFFFF;
        GPIOB->CRL |=0xFF000000;//复用开漏输出
        I2C_InitTypeDef stru;
        stru.I2C_Ack=I2C_Ack_Enable;
        stru.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
        stru.I2C_ClockSpeed=200000;
        stru.I2C_DutyCycle=I2C_DutyCycle_2;
        stru.I2C_Mode=I2C_Mode_I2C;
        stru.I2C_OwnAddress1=0x00;
        I2C_Init(I2C1,&stru);//初始化I2C硬件
        I2C_Cmd(I2C1,ENABLE);//启动I2C硬件
        u8 key=1;
        while(1)
        {
                if ((key==1) && (!(GPIOC->IDR & (1<<5))))
                {
                        key=0;
                        I2C_GenerateSTART(I2C1,ENABLE);//发送开始信号
                        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
                        //发送从设备物理地址(读取)
                        I2C_Send7bitAddress(I2C1, 0x51<<1,
                                I2C_Direction_Receiver);
                        while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//<-----------------------这里似乎就开始读数据了,莫名其妙!!
                        I2C_GenerateSTOP(I2C1,ENABLE);//发送停止
                }
        }
}

另外,模拟I2C发送读命令是正常的
B.png

最佳答案

查看完整内容[请看2#楼]

出错后重新配置I2C寄存器就可以重新通讯了,给你贴个读取的吧 //Function: AT24CXX_ReadBytes(driver) //Description: use I2C to read bytes from AT24CXX //Input: uint16_t addr ---- register address in AT24CXX // uint16_t count ---- data length //Output: uin ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

11

主题

2153

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4938
金钱
4938
注册时间
2015-1-10
在线时间
619 小时
发表于 2021-11-14 21:58:55 | 显示全部楼层
cyradg 发表于 2021-11-15 16:39
I2C时序很重要,一旦乱了,单片机按RESET都没用,一定要断点,有人说是I2C硬件问题,其实不一定,比如24C02 ...

出错后重新配置I2C寄存器就可以重新通讯了,给你贴个读取的吧
//Function:                 AT24CXX_ReadBytes(driver)
//Description:        use I2C to read bytes from AT24CXX
//Input:                                uint16_t addr   ---- register address in AT24CXX
//                                                        uint16_t count  ---- data length
//Output:                                uint8_t *pdata
//Return:                                SUCCESS ---- 0
//                                                        FAIL                ---- 1
uint8_t AT24CXX_ReadBytes(uint16_t addr, uint8_t *pdata, uint16_t count)
{  
  uint32_t errcnt = 0;
        
        do {
                errcnt++;
                if (errcnt > SystemCoreClock/1000)
                        goto error;
        } while (I2C_GetFlagStatus(AT24CXX_I2C, I2C_FLAG_BUSY));
  
  /*!< Send START condition */
  I2C_GenerateSTART(AT24CXX_I2C, ENABLE);
  
  /*!< Test on EV5 and clear it (cleared by reading SR1 then writing to DR) */
/* Test on EV5 and clear it */
        errcnt = 0;
        do {
                errcnt++;
                if (errcnt > SystemCoreClock/1000)
                        goto error;
        } while (!I2C_CheckEvent(AT24CXX_I2C, I2C_EVENT_MASTER_MODE_SELECT));
  
  /*!< Send EEPROM address for write */
  I2C_Send7bitAddress(AT24CXX_I2C, AT24CXX_SLAVEADDR, I2C_Direction_Transmitter);

        if (EE_TYPE <= AT24C16) {
                /*!< Test on EV6 and clear it */
                errcnt = 0;
                do {
                        errcnt++;
                        if (errcnt > SystemCoreClock/1000)
                                goto error;
                } while (!I2C_CheckEvent(AT24CXX_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); /* EV7 */
               
                I2C_SendData(AT24CXX_I2C, addr);
        } else {
                /*!< Test on EV6 and clear it */
                errcnt = 0;
                do {
                        errcnt++;
                        if (errcnt > SystemCoreClock/1000)
                                goto error;
                } while (!I2C_CheckEvent(AT24CXX_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); /* EV7 */
               
                I2C_SendData(AT24CXX_I2C, addr>>8);
               
                /*!< Test on EV6 and clear it */
                errcnt = 0;
                do {
                        errcnt++;
                        if (errcnt > SystemCoreClock/1000)
                                goto error;
                } while (!I2C_CheckEvent(AT24CXX_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); /* EV7 */
               
                I2C_SendData(AT24CXX_I2C, addr);
        }

  errcnt = 0;
        do {
                errcnt++;
                if (errcnt > SystemCoreClock/1000)
                        goto error;
        } while (!I2C_CheckEvent(AT24CXX_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
        
        I2C_GenerateSTART(AT24CXX_I2C, ENABLE);
        
        errcnt = 0;
        do {
                errcnt++;
                if (errcnt > SystemCoreClock/1000)
                        goto error;
        } while (!I2C_CheckEvent(AT24CXX_I2C, I2C_EVENT_MASTER_MODE_SELECT));
        
        /* Send address for read */
        I2C_Send7bitAddress(AT24CXX_I2C, AT24CXX_SLAVEADDR, I2C_Direction_Receiver);
        
        errcnt = 0;
        do {
                errcnt++;
                if (errcnt > SystemCoreClock/1000)
                        goto error;
        } while (!I2C_CheckEvent(AT24CXX_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); /* EV7 */
        
        for (int i = 0; i < count - 1; i++) {
                I2C_AcknowledgeConfig(AT24CXX_I2C, ENABLE);   
               
                errcnt = 0;
                do {
                        errcnt++;
                        if (errcnt > SystemCoreClock/1000)
                                goto error;
                } while (I2C_GetFlagStatus(AT24CXX_I2C, I2C_FLAG_RXNE) == RESET);
               
                *pdata++ = I2C_ReceiveData(AT24CXX_I2C);
        }
        
        I2C_AcknowledgeConfig(AT24CXX_I2C, DISABLE);   
        
        errcnt = 0;
        do {
                errcnt++;
                if (errcnt > SystemCoreClock/1000)
                        goto error;
        } while (I2C_GetFlagStatus(AT24CXX_I2C, I2C_FLAG_RXNE) == RESET);        
        
        *pdata = I2C_ReceiveData(AT24CXX_I2C);
        
        /* Send STOP condition */
        I2C_GenerateSTOP(AT24CXX_I2C, ENABLE);           

        return STATUS_OK;
        
        error:
        return STATUS_FAIL;
}
回复

使用道具 举报

11

主题

2153

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4938
金钱
4938
注册时间
2015-1-10
在线时间
619 小时
发表于 2021-11-15 16:10:12 | 显示全部楼层
硬件能用,但是不稳定,出错率我测下来是0.5%左右
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-15 16:20:58 | 显示全部楼层
阿侑kevin 发表于 2021-11-15 16:10
硬件能用,但是不稳定,出错率我测下来是0.5%左右

我是100%出错,这是我单独提取出来的一段代码,发现发送读命令后,还没开始读取数据就给我扔出了一个数据。我也是偶然发现的,开始我以为调试I2C1成功了,后来看代码,然后对应逻辑分析仪,才发现多读了一个字节,居然发现是发送读命令时就飞掉了第一个字节,这个字节我无法读取,也是抓狂了。。。。。
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-15 16:22:36 | 显示全部楼层
阿侑kevin 发表于 2021-11-15 16:10
硬件能用,但是不稳定,出错率我测下来是0.5%左右

能提供下你的代码吗,读取24C02一个字节的代码就行,我上一下逻辑分析仪就知道是不是多读了一个,也就是说,代码读的一个字节,实际是第二个字节的内容,第一个字节的内容在发送读命令时就飞掉了。
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-15 16:39:24 | 显示全部楼层
I2C时序很重要,一旦乱了,单片机按RESET都没用,一定要断点,有人说是I2C硬件问题,其实不一定,比如24C02,你发送写命令,写入操作内存地址,然后发送读命令,假设这时操作乱了,你发送停止命令是没用的,好像是24C02锁死了SCL还是SDA我不记得了,用逻辑分析仪看下就知道发停止命令根本没响应,你按1万遍RESET都没用,24C02死了,除非你把24C02断电,也就是拔电。所以我个人认为I2C除了问题也不要老是怪硬件问题,软件I2C也一样的。
所以我写I2C代码,必上逻辑分析仪,否则不知道问题出在哪。但是STM32的这种情况,我还真不知道问题出在哪?24C02收到了一个读取命令就会发一个字节的内容出来,I2C硬件为什么还有时钟,干了些什么?
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2021-11-16 02:41:13 | 显示全部楼层
放弃吧。。。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

4

主题

163

帖子

0

精华

高级会员

Rank: 4

积分
955
金钱
955
注册时间
2018-9-7
在线时间
115 小时
发表于 2021-11-16 10:11:56 | 显示全部楼层
原子哥叫你放弃
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-16 19:28:16 | 显示全部楼层
阿侑kevin 发表于 2021-11-16 15:30
出错后重新配置I2C寄存器就可以重新通讯了,给你贴个读取的吧
//Function:                 AT24CXX_Rea ...

非常感谢!看了下野火的硬件IC代码,是这样写的:
while(NumByteToRead)
{
        if(NumByteToRead == 1)
        {
                I2C_AcknowledgeConfig(I2C1, DISABLE);
                I2C_GenerateSTOP(I2C1, ENABLE);
        }
        if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        {
                *pBuffer = I2C_ReceiveData(I2C1);
                pBuffer++;
                NumByteToRead--;
        }
}
I2C_AcknowledgeConfig(I2C1, ENABLE);

这个写法有点不按常理,测试了下,居然是正确的。其实有一点我搞错了,那就是在开始读时,24C02的确是扔出了一个数据,用I2C_ReceiveData函数后,是接收到刚才那个扔出来的数据,然后24C02又会扔出一个数据,所以逻辑分析仪上是24C02发出了两个数据,但是I2C_ReceiveData收到的是第一个数据,我一直以为是第二个,后来验证是第一个。如果一直使用I2C_ReceiveData,就一直会多出一个数据,如何不多一个,就是在最后读取一个数据时,禁止回应,然后停止,然后用I2C_ReceiveData就会收最后一个数据,因为这时停止了,不会触发24C02继续发数据,也就是最后一个数据读取时,I2C_GenerateSTOP写在I2C_ReceiveData前面去了,有些不按常理,不过,I2C_GenerateSTOP写在I2C_ReceiveData后面也应该无妨,也就是最后一个数据没读取而已,实际上也是不读的。
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-16 19:39:52 | 显示全部楼层
A.png
我这个是24C512的测试,把读取到的两位数据作为地址再写回去,内容是正确的,首先发送读命令时,单片机就让24C512发送了一个字节,这是我没想到的,而且,STM32是等待接收完成事件在前,接收数据在后,这个也和Atmel不一样,Atmel是接收数据在前,等待接收完成在后,STM32最后读一个数据是STOP在前,接收数据在后,因为上一个接收数据命令之后就让24C512发送了最后一个数据,这也是我没想到的。
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-16 20:30:46 | 显示全部楼层
I2C2不玩了,一点反应也没有,玩的挺累的,有个I2C1顶着,再不济还有模拟I2C。上个开发板的图,右边的小板为嘉立创做的板子,日常想到个什么就做个玩玩。
I2C调试失败时,很多时候,重新改代码写进去按N次RESET键也没用,但是把右边的小板子电源拔了,再插回去,立马恢复了。。。。。
A.jpg
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2021-11-17 01:51:46 | 显示全部楼层
模拟大法好
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-17 08:32:30 | 显示全部楼层

模拟I2C稳定应该是肯定的,否则没这么多人用,其实野火关于I2C操作最近也改模拟了,硬件I2C操作也是早先的方式。但是有个缺陷好像频率低了点,左改改右改改撑死100K多些,也可能我的代码效率不高吧,硬件I2C可以跑到400K,测试读写24C512还算稳定,简单应用可能还行吧,加入其它中断可能又出什么问题就不知道了,好在出了问题不用改硬件电路,改模拟I2C方式就行,所以模拟I2C是终极大法。
回复

使用道具 举报

18

主题

190

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1068
金钱
1068
注册时间
2012-6-18
在线时间
158 小时
发表于 2021-11-19 23:13:15 | 显示全部楼层
用hal 库吧。寄存器 或者标准库,真心的不好用。
  1. /* I2C2 init function */
  2. void MX_I2C2_Init(void)
  3. {

  4.   /* USER CODE BEGIN I2C2_Init 0 */

  5.   /* USER CODE END I2C2_Init 0 */

  6.   /* USER CODE BEGIN I2C2_Init 1 */

  7.   /* USER CODE END I2C2_Init 1 */
  8.   hi2c2.Instance = I2C2;
  9.   hi2c2.Init.ClockSpeed = 400000;
  10.   hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
  11.   hi2c2.Init.OwnAddress1 = 0;
  12.   hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  13.   hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  14.   hi2c2.Init.OwnAddress2 = 0;
  15.   hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  16.   hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  17.   if (HAL_I2C_Init(&hi2c2) != HAL_OK)
  18.   {
  19.     Error_Handler();
  20.   }
  21.   /* USER CODE BEGIN I2C2_Init 2 */

  22.   /* USER CODE END I2C2_Init 2 */

  23. }
复制代码
  1. void Write_IIC_Data(uint8_t dat)
  2. {
  3.   HAL_I2C_Mem_Write(&hi2c2,IIC_SLAVE_ADDR,0x40,1,&dat,1,100);
  4.   
  5. }

  6. void Write_IIC_Command(uint8_t dat)
  7. {
  8.   HAL_I2C_Mem_Write(&hi2c2,IIC_SLAVE_ADDR,0x00,1,&dat,1,100);
  9. }

  10. void OLED_WR_Byte(uint8_t dat, uint8_t cmd)
  11. {
  12.         if(cmd)
  13.         {
  14.                 Write_IIC_Data(dat);
  15.   }
  16.         else
  17.         {
  18.                 Write_IIC_Command(dat);               
  19.         }
  20. }
复制代码
103c8t6 硬件IIC高速总线模式。驱动SSD1309 OLE刷屏钢钢滴块。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-19 04:58

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表