打印
[STM32L0]

STM32L073RZ I2C的问题

[复制链接]
1253|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Lp19800618|  楼主 | 2019-10-21 19:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Lp19800618 于 2019-10-21 19:06 编辑

    使用“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];  
    }
}


使用特权

评论回复
沙发
heimaojingzhang| | 2019-11-18 11:22 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
板凳
jay8830095| | 2019-12-17 15:03 | 只看该作者
模拟IIC不好用吗?一般不用ST的自带IIC

使用特权

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

本版积分规则

3

主题

6

帖子

0

粉丝