你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32L073RZ I2C的问题!!!

[复制链接]
希望在转角 提问时间:2019-10-21 19:04 /
20ST金币
    使用“NUCLEO-L073RZ”开发板进行调试。在调试I2C时,遇到了一些问题。
    下面介绍一下我们的使用背景以及遇到的问题:
    - 使用背景
      对外通信是通过I2C接口,L073作为I2C从机,供主机读/写数据
         
    - 遇到的问题
        注:通过一个自制的USB转I2C通信板,与“NUCLEO-L073RZ”开发板进行I2C通信。
    1. 尝试模拟主机向从机的"BYTE ADDRESS"为0x08的地址写入一个字节的数据,假设为0x12。
        通过在线仿真,发现数据已经被正确的写入并保存到相关的数组中了。

        接下来,尝试模拟主机从从机的"BYTE ADDRESS"为0x08的地址读出一个字节的数据,出现的现象是:
        第一次读,可以正确的读出数据为0x12;
        第二次读,读出数据变成了0x00;
        第三次读,可以正确的读出数据为0x12;
        后面都能正确的读出数据为0x12;

   2. 尝试模拟主机向从机的"BYTE ADDRESS"为0x08和0x09的地址连续写入两个字节的数据,假设为0x12和0x34。
       通过在线仿真,发现数据已经被正确的写入并保存到相关的数组中了。

        接下来,尝试模拟主机从从机的"BYTE ADDRESS"为0x08的地址连续读出两个字节的数据,出现的现象是:
        第一次读,可以正确的读出数据为0x12和0x34;
        第二次读,读出数据变成了0x00和0x12;
        第三次读,读出数据变成了0x34和0x12;
        后面读出数据都为0x34和0x12;

   3. 尝试模拟主机向从机的"BYTE ADDRESS"为0x08、0x09和0x0A的地址连续写入三个字节的数据,假设为0x12、0x34和0x56。
       通过在线仿真,发现数据已经被正确的写入并保存到相关的数组中了。

        接下来,尝试模拟主机从从机的"BYTE ADDRESS"为0x08的地址连续读出三个字节的数据,出现的现象是:
        第一次读,可以正确的读出数据为0x12、0x34和0x56;
        第二次读,读出数据变成了0x00、0x12和0x34;
        第三次读,读出数据变成了0x56、0x12和0x34;
        后面读出数据都为0x56、0x12和0x34;
     ... ...
   
  另外,我在调试过程中发现一个现象:就是如果Firmware初次运行的时候,如果接收到I2C读一个字节操作的请求,程序会连续进入TXIS中断两次!
  但是紧接着再多次发送读一个字节操作的请求,程序就只会进入TXIS一次了!为什么第一次读操作的时候会进入TXIS中断两次?
附I2C初始化及中断部分代码:
/* I2C1 init function */
void MX_I2C1_Init(void)
{
    LL_I2C_InitTypeDef I2C_InitStruct = {0};

    LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

    LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
    /**I2C1 GPIO Configuration
    PB8   ------> I2C1_SCL
    PB9   ------> I2C1_SDA
    */
    GPIO_InitStruct.Pin = I2C1_SCL_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
    LL_GPIO_Init(I2C1_SCL_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = I2C1_SDA_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
    LL_GPIO_Init(I2C1_SDA_GPIO_Port, &GPIO_InitStruct);

    /* Peripheral clock enable */
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);                             // 使能I2C1的时钟
    RCC->CCIPR &= ~RCC_CCIPR_I2C1SEL;                                               // 选择APB时钟作为I2C1的时钟(Cube代码里没有这一行)  


    NVIC_SetPriority(I2C1_IRQn, 0);
    NVIC_EnableIRQ(I2C1_IRQn);


    LL_I2C_EnableAutoEndMode(I2C1);
    LL_I2C_DisableOwnAddress2(I2C1);
    LL_I2C_DisableGeneralCall(I2C1);
    LL_I2C_EnableClockStretching(I2C1);
    I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
    I2C_InitStruct.Timing = 0x20302E37;
    I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
    I2C_InitStruct.DigitalFilter = 0;
    I2C_InitStruct.OwnAddress1 = SLAVE_OWN_ADDRESS;
    I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
    I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
    LL_I2C_Init(I2C1, &I2C_InitStruct);
    LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);

    LL_I2C_EnableIT_ADDR(I2C1);
    //LL_I2C_EnableIT_NACK(I2C1);
    //LL_I2C_EnableIT_ERR(I2C1);
    LL_I2C_EnableIT_STOP(I2C1);
    //LL_I2C_EnableIT_TC(I2C1);
    //LL_I2C_EnableIT_RX(I2C1);
}


/* USER CODE BEGIN 1 */
void I2C_interrupt_op(void)
{
    uint8_t DoPrefetch = 0;                                                         // 为I2C读操作执行预取数操作的标志,如果不为0,则需要预先取数
    uint8_t Receiverdata;

    //---------------------------------------- Check ADDR flag value in ISR register
    //---------------------------------------- 检查是否为地址匹配中断
    if(LL_I2C_IsActiveFlag_ADDR(I2C1))
    {
        LL_I2C_ClearFlag_ADDR(I2C1);                                                // Clear ADDR flag value in ISR register
        //---------------------------------------- Verify the Address Match with the OWN Slave address
        //---------------------------------------- 检查地址是否为设置的从机地址
        if(LL_I2C_GetAddressMatchCode(I2C1) == SLAVE_OWN_ADDRESS)
        {
            //---------------------------------------- Verify the transfer direction, a read direction, Slave enters transmitter mode */
            //---------------------------------------- 如果传输方向是读操作
            if(LL_I2C_GetTransferDirection(I2C1) == LL_I2C_DIRECTION_READ)
            {
                I2CBusy = TRUE;
                LL_I2C_EnableIT_TX(I2C1);                                           // Enable Transmit Interrupt
            }
            //---------------------------------------- 如果传输方向是写操作
            else if (LL_I2C_GetTransferDirection(I2C1) == LL_I2C_DIRECTION_WRITE)
            {
                LL_I2C_EnableIT_RX(I2C1);                                           // Enable Receive Interrupt
                I2CRxCount = 0;
                I2CBusy = TRUE;
            }
            else
            {
                I2C_error_callback();                                               // Call Error function
            }
        }
        else
        {
            I2C_error_callback();                                                   // Call Error function
        }
    }

    //---------------------------------------- Check TXIS flag value in ISR register
    //---------------------------------------- 发送中断
    else if(LL_I2C_IsActiveFlag_TXIS(I2C1))
    {
        //LL_I2C_DisableIT_TX(I2C1);
        LL_I2C_TransmitData8(I2C1, I2CTxBufData);                                   // Send the Byte requested by the Master
        DoPrefetch = 1;

        //---------------------------------------- 低128字节地址寻址
        if(DataAddr == 127)
        {   
            DataAddr = 0;
        }
        //---------------------------------------- 高128字节地址寻址
                else if(DataAddr == 255)
        {
                        DataAddr = 128;
        }
        //---------------------------------------- 地址索引增1
        else
        {
            DataAddr++;
        }
    }
    //---------------------------------------- Check RXNE flag value in ISR register
    //---------------------------------------- 接收中断
    else if(LL_I2C_IsActiveFlag_RXNE(I2C1))
    {
        Receiverdata = LL_I2C_ReceiveData8(I2C1);                                   // 将RXDR中的数据保存到Receiverdata
        //---------------------------------------- 如果是接收到的Data的第一个字节
            if(I2CRxCount == 0)
        {
            I2CRxCount++;
                   DataAddr = Receiverdata;                                                                // 按照协议规定,Data的第一个字节为MEMORY ADDR
            DoPrefetch = 2;
        }        
        else
        {
            Page00H[DataAddr] = Receiverdata;
            //---------------------------------------- 低128字节地址寻址
            if(DataAddr == 127)
            {   
                DataAddr = 0;
            }
            //---------------------------------------- 高128字节地址寻址
            else if(DataAddr == 255)
            {
                DataAddr = 128;
            }
            //---------------------------------------- 地址索引增1
            else
            {
                DataAddr++;
            }  
        }        
    }
    //---------------------------------------- Check STOP flag value in ISR register
    //---------------------------------------- STOPF中断
    else if(LL_I2C_IsActiveFlag_STOP(I2C1))
    {
        LL_I2C_ClearFlag_STOP(I2C1);                                                // Clear STOP flag value in ISR register
        I2CBusy = FALSE;        
    }

    else
    {
        I2CBusy = FALSE;
        I2C_error_callback();                                                           // Call Error function
    }

    //---------------------------------------- 如果预取数标志大于0,则需要预先将数据取出并保存到变量I2CTxBufData中去,等待Host去读取
    if (DoPrefetch > 0)
    {
        I2CTxBufData = Page00H[DataAddr];  
    }
}

收藏 1 评论8 发布时间:2019-10-21 19:04

举报

8个回答
maxtch 回答时间:2019-10-22 10:43:36
某处缓存没有清理。
希望在转角 回答时间:2019-10-23 13:04:39
maxtch 发表于 2019-10-22 10:43
某处缓存没有清理。

你指的缓存是?能说具体点吗?
多谢了!
maxtch 回答时间:2019-10-23 18:33:31
macompengluo 发表于 2019-10-23 13:04
你指的缓存是?能说具体点吗?
多谢了!

FIFO 有残余数据

评分

参与人数 1蝴蝶豆 +3 收起 理由
STMCU + 3

查看全部评分

edmundlee 回答时间:2019-10-26 09:16:02
我也认为是FIFO 有残余数据

评分

参与人数 1蝴蝶豆 +2 收起 理由
STMCU + 2

查看全部评分

希望在转角 回答时间:2019-10-28 15:10:15
问题已经解决,在I2C地址匹配中断处理的读操作方向分支,要加上 LL_I2C_ClearFlag_TXE(I2C1); 这条语句!!
           //---------------------------------------- 如果传输方向是读操作
            if(LL_I2C_GetTransferDirection(I2C1) == LL_I2C_DIRECTION_READ)
            {
                LL_I2C_ClearFlag_TXE(I2C1);
                LL_I2C_EnableIT_TX(I2C1);                                           // Enable Transmit Interrupt
                I2CEvent = EVENT_OPCOD_SEND;                                        // Set I2C entor transmit mode
            }
lrzxc 回答时间:2019-10-29 08:51:04
macompengluo 发表于 2019-10-28 15:10
问题已经解决,在I2C地址匹配中断处理的读操作方向分支,要加上 LL_I2C_ClearFlag_TXE(I2C1); 这条语句!! ...

好滴,多谢分享,还是LL库的
希望在转角 回答时间:2019-10-30 10:21:16
lrzxc111 发表于 2019-10-29 08:51
好滴,多谢分享,还是LL库的

HAL库里面很多操作都限定死了,不是很方便。因为我需要用到很多自己定制化的功能,还是觉得LL更好用,甚至我都想用寄存器直接设置
butterflyspring 回答时间:2019-10-30 16:02:38
HAL库可以帮着把外设先调通,然后照着改LL库。过了河~~~~~
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版