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

STM32L073RZ I2C的问题!!!

[复制链接]
macompengluo 提问时间: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
某处缓存没有清理。
macompengluo 回答时间: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

查看全部评分

macompengluo 回答时间: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
            }
lrzxc111 回答时间:2019-10-29 08:51:04
macompengluo 发表于 2019-10-28 15:10
问题已经解决,在I2C地址匹配中断处理的读操作方向分支,要加上 LL_I2C_ClearFlag_TXE(I2C1); 这条语句!! ...

好滴,多谢分享,还是LL库的
macompengluo 回答时间: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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版