打印
[产品应用]

【已解决】cw32l010 I2C的状态切换要清除状态吗?

[复制链接]
645|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
fox1|  楼主 | 2024-12-2 17:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 fox1 于 2024-12-4 21:28 编辑

大佬们,在做一个MEMS芯片的驱动移植,他的i2c read时序比较奇怪,是先1 START -> 2 SLA+W  【收ACK】->3 发读取的寄存器地址【收ACK】->4 START -> 5 SLA+R 【收ACK】->接收数据 -> END 最后发STOP

我看cw32l010的用户手册
状态间没有切换的,如果我第3步发完寄存器收完ACK后,直接重新发送起始信号,会报错吗?

根据时序图写的代码这样子:
----------------已经解决了,正确代码-------------------


I2C_ErrorDef I2C_QMA6100P_SEQ_READ(I2C_TypeDef *I2Cx, uint8_t regAddr, uint8_t *pu8Data, uint32_t u32Len)
{

    uint8_t u8i = 0, u8State;
    // 是否已发送 寄存器地址
    uint8_t flag = 0;
    uint8_t Qma6100PAddr = 0x24;
    uint32_t timeout = I2C_TIMEOUT;
    I2C_ClearIrq(I2Cx);
    I2C_GenerateSTART(I2Cx, ENABLE);
    while (timeout--)
    {
        while (timeout)
        {
            timeout--;
            if (I2C_GetIrq(I2Cx))
            {
                break;
            }
        }
        u8State = I2C_GetState(I2Cx);
        switch (u8State)
        {
        case 0x08: // 1 发送完START信号 发送SLA+W
            I2Cx->DR_f.DR = Qma6100PAddr & 0xfe;
            I2Cx->CR_f.STA = 0;
            I2Cx->CR_f.SI = 0;
            timeout = I2C_TIMEOUT;
            break;
        case 0x10: // 已发送重复起始信号
            // 发送完restart信号,发送SLA+R
            if (flag == 1)
            {
                I2Cx->DR = Qma6100PAddr | 0x01;
                I2Cx->CR_f.STA = 0;
                I2Cx->CR_f.SI = 0;
            }
            timeout = I2C_TIMEOUT;
            flag = 0;
            break;
        case 0x18: // 2.SLA+W 后收到 从机ACK,发第一个寄存器地址
            // printf("I2C SLA+W ACK \r\n");
            I2Cx->DR = regAddr;
            I2Cx->CR_f.SI = 0;
            flag = 1;
            timeout = I2C_TIMEOUT;
            break;
        case 0x28:
            // 3  发送寄存器地址,并收到ACK 发送Restart
            if (flag == 1)
            {
                I2Cx->CR_f.STA = 0;
                I2Cx->CR_f.STA = 1;
                timeout = I2C_TIMEOUT;
                break;
            }
            break;
        case 0x40: // 发送完SLA+R信号,开始接收数据

            if (u32Len == 1)
            {
                I2Cx->CR_f.AA = 0;
            }
            else
            {
                I2Cx->CR_f.AA = 1;
            }
            I2Cx->CR_f.SI = 0;
            timeout = I2C_TIMEOUT;
            break;
        case 0x50: // 接收完一字节数据,在接收最后1字节数据之前设置AA=0;
            pu8Data[u8i = (uint8_t)I2Cx->DR;
            // pass 0x40已经过滤了U32LEN = 1 的情况
            if (u8i == u32Len - 1)
            {
                I2Cx->CR_f.AA = 0;
            }
            printf("I2C 0x50 \r\n");
            printf("pu8Data[%d] = %x \r\n", u8i, pu8Data[u8i - 1]);
            timeout = I2C_TIMEOUT;
            break;
        case 0x58: // 接收到一个数据字节,且NACK已回应
            pu8Data[u8i = (uint8_t)I2Cx->DR;
            printf("I2C 0x58 \r\n");
            printf("pu8Data[%d] = %x \r\n", u8i, pu8Data[u8i - 1]);
            if (u32Len == 1 || u8i == u32Len)
            {
                I2C_GenerateSTOP(I2Cx);
                I2C_ClearIrq(I2Cx);
                return I2C_NO_ERROR;
            }
            I2C_GenerateSTOP(I2Cx);
            I2C_ClearIrq(I2Cx);
            return I2C_MR_DATA_NACK;
        case 0x38: // 主机在发送 SLA+W 阶段或者发送数据阶段丢失仲载  或者  主机在发送 SLA+R 阶段或者回应 NACK 阶段丢失仲裁
            I2C_GenerateSTOP(I2Cx);
            I2C_ClearIrq(I2Cx);
            return I2C_MR_ARBITRATION_LOST;
        case 0x48: // 发送完SLA+R后从机返回NACK
            I2C_GenerateSTOP(I2Cx);
            I2C_ClearIrq(I2Cx);
            return I2C_MR_ADDR_NACK;
        default:
            break;
        }
        I2C_ClearIrq(I2Cx);
    }
}







使用特权

评论回复
沙发
tpgf| | 2024-12-3 14:49 | 只看该作者
在I2C通信过程中,当从一种状态切换到另一种状态时,可能需要清除一些特定的状态或标志位,以确保通信的正确进行

使用特权

评论回复
板凳
xch| | 2024-12-3 16:53 | 只看该作者
本帖最后由 xch 于 2024-12-3 16:55 编辑

第三步后发 start 时序在I2C 规范之中称作 re-start . 符合规范。 我不这么写,直接发stop ,然后再start 。这样好处有1节约代码空间(仅需要I2C读写函数各一个),仅费一点stop 时间。2 I2C总线安全,stop 时序可以修复I2C总线逻辑故障。

使用特权

评论回复
地板
jobszheng| | 2024-12-4 10:28 | 只看该作者
xch 发表于 2024-12-3 16:53
第三步后发 start 时序在I2C 规范之中称作 re-start . 符合规范。 我不这么写,直接发stop ,然后再start  ...

我也非常赞成这样的处理方式

使用特权

评论回复
5
jobszheng| | 2024-12-4 10:28 | 只看该作者
楼主,您这代码贴出来看着真难受

使用特权

评论回复
6
xionghaoyun| | 2024-12-4 11:17 | 只看该作者
jobszheng 发表于 2024-12-4 10:28
楼主,您这代码贴出来看着真难受

+1 看得费眼

使用特权

评论回复
7
fox1|  楼主 | 2024-12-4 21:25 | 只看该作者
已经解决了,谢谢大佬们

使用特权

评论回复
8
suncat0504| | 2024-12-5 22:19 | 只看该作者
确实,楼主的代码看起来有点不习惯。

使用特权

评论回复
9
renzheshengui| | 2024-12-6 11:46 | 只看该作者
在发送完寄存器地址并收到ACK后,如果直接重新发送起始信号而不清除相关状态,可能会导致I2C通信出错

使用特权

评论回复
10
wowu| | 2024-12-6 15:12 | 只看该作者
在进行状态切换时,通常需要调用相应的函数来清除状态,如[size=0.875]I2C_ClearIrq()函数,该函数用于清除I2C中断标志位

使用特权

评论回复
11
xiaoqizi| | 2024-12-6 18:32 | 只看该作者
大概有多少个状态需要清除呢? 是手动清除还是自动清除呢

使用特权

评论回复
12
木木guainv| | 2024-12-6 20:22 | 只看该作者
如果需要调用函数的话  程序会自动进行调用吗

使用特权

评论回复
13
磨砂| | 2024-12-6 22:08 | 只看该作者
是否可以考虑自己操作寄存器来实现清除状态的目的呢

使用特权

评论回复
14
LOVEEVER| | 2024-12-12 09:19 | 只看该作者
应该是要进行清楚状态位的

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

32

帖子

0

粉丝