OpenNuvoton / M051BSP

BSP for M051 Series MCU
8 stars 3 forks source link

I2C transmission issues #2

Open prometheans152 opened 2 days ago

prometheans152 commented 2 days ago

I referred to the I2C-related example in your sample code and wrote an I2C API. However, when I tested sending commands continuously, it got stuck after the first command (after setting the STA for the next command, no interrupt was received). I added an initialization for the I2C at the beginning of my API and a shutdown of the I2C at the end, and it started working properly.

I would like to ask if there is a way to perform normal transmission without repeatedly turning the I2C on and off. Doing it this way doesn’t make much sense. Here is my API:

#include <stdio.h>
#include "M051Series.h"
#include "MID_I2C.h"
typedef struct __I2C_HandleTypeDef
{
    uint16_t Addr;
    uint8_t mode;
    uint8_t *pTxBuff;
    uint16_t TxBuffSize;
    uint8_t TxLenFlag;
    uint8_t *pRxBuff;
    uint16_t RxBuffSize;
    uint8_t RxLenFlag;
    uint8_t EndFlag;
    uint32_t Timeout;
    uint32_t Timeoutcount;
    uint32_t Error;
}I2C_HandleTypeDef;
typedef struct __I2C_PortHandleTypeDef
{
    I2C_HandleTypeDef I2C0Handle;
    I2C_HandleTypeDef I2C1Handle;
}I2C_PortHandleTypeDef;

I2C_PortHandleTypeDef I2CPortHandle;

uint16_t BytesToWord(uint8_t *buff)
{
    uint16_t temp = (buff[1]<<8)+buff[0];
    return temp;
}
uint8_t MID_IIC_Master_RX(I2C_T *i2c, uint16_t DevAddress, uint8_t *pData, uint16_t datasize,uint32_t timeout)
{
    I2C_HandleTypeDef* I2CHandle;
    I2C_Open(i2c, 100000); 
    I2C_EnableInt(i2c);
    switch ((uint32_t)i2c)
    {
        case I2C0_BASE:
            I2CHandle = &I2CPortHandle.I2C0Handle;
        break;
        case I2C1_BASE:
            I2CHandle = &I2CPortHandle.I2C1Handle;
        break;
        default:
        break;
    }
    I2CHandle->Addr = DevAddress;
    I2CHandle->pRxBuff = pData;
    I2CHandle->RxBuffSize = datasize;
    I2CHandle->mode = 0;
    I2CHandle->RxLenFlag = 0;
    I2CHandle->Timeout = timeout;
    I2CHandle->EndFlag = 0;
    I2C_SET_CONTROL_REG(i2c, I2C_I2CON_STA);
    while(I2CHandle->EndFlag == 0);
    I2C_DisableInt(i2c);
    I2C_Close(i2c);
    return I2CHandle->Error;
}
uint8_t MID_IIC_Master_TX(I2C_T *i2c, uint16_t DevAddress, uint8_t *pData, uint16_t datasize,uint32_t timeout)
{
    I2C_HandleTypeDef* I2CHandle;
    I2C_Open(i2c, 100000); 
    I2C_EnableInt(i2c);
    switch ((uint32_t)i2c)
    {
        case I2C0_BASE:
            I2CHandle = &I2CPortHandle.I2C0Handle;
        break;
        case I2C1_BASE:
            I2CHandle = &I2CPortHandle.I2C1Handle;
        break;
        default:
        break;
    }
    I2CHandle->Addr = DevAddress;
    I2CHandle->pTxBuff = pData;
    I2CHandle->TxBuffSize = datasize;
    I2CHandle->mode = 1;
    I2CHandle->TxLenFlag = 0;
    I2CHandle->Timeout = timeout;
    I2C_SET_CONTROL_REG(i2c, I2C_I2CON_STA);
    I2CHandle->EndFlag = 0;
    while(I2CHandle->EndFlag == 0);
    I2C_DisableInt(i2c);
    I2C_Close(i2c);
    return I2CHandle->Error;
}

void I2C0_IRQHandler(void)
{
    uint32_t u32Status;
    u32Status = I2C_GET_STATUS(I2C0);
    if(I2C_GET_TIMEOUT_FLAG(I2C0))
    {
        /* Clear I2C0 Timeout Flag */
        I2C_ClearTimeoutFlag(I2C0);
    }
    else
    {
        //printf("code = %x\r\n",u32Status);
        switch (u32Status)
        {
            case 0x08://start ack//done
                if(I2CPortHandle.I2C0Handle.mode  == 1)
                    I2C_SET_DATA(I2C0, (I2CPortHandle.I2C0Handle.Addr & 0xFE));//設定dev w addr
                else
                    I2C_SET_DATA(I2C0, (I2CPortHandle.I2C0Handle.Addr | 0x01));//設定設定dev r addr
                I2C_SET_CONTROL_REG(I2C0, I2C_I2CON_SI);//清除中斷
            break;
            case 0x18://done
                I2C_SET_DATA(I2C0, I2CPortHandle.I2C0Handle.pTxBuff[I2CPortHandle.I2C0Handle.TxLenFlag++]);//設定Tx資料
                I2C_SET_CONTROL_REG(I2C0, I2C_I2CON_SI);//清除中斷
            break;
            case 0x20://nack done
                I2C_STOP(I2C0);//收到nack 停止I2C
                I2C_START(I2C0);//重啟I2C
            break;
            case 0x28://TX done
                if(I2CPortHandle.I2C0Handle.TxLenFlag == I2CPortHandle.I2C0Handle.TxBuffSize)//TX finish
                    I2CPortHandle.I2C0Handle.EndFlag = 1;
                else
                    I2C_SET_DATA(I2C0, I2CPortHandle.I2C0Handle.pTxBuff[I2CPortHandle.I2C0Handle.TxLenFlag++]);
                I2C_SET_CONTROL_REG(I2C0, I2C_I2CON_SI);
            break;
            case 0x10://repeat start done
                I2C_SET_DATA(I2C0, (I2CPortHandle.I2C0Handle.Addr | 0x01));//設定設定dev r addr
                I2C_SET_CONTROL_REG(I2C0, I2C_I2CON_SI);//清除中斷
            break;
            case 0x40://done
                if(I2CPortHandle.I2C0Handle.RxBuffSize == 1)//長度為1則不設AA
                    I2C_SET_CONTROL_REG(I2C0, I2C_I2CON_SI);
                else
                    I2C_SET_CONTROL_REG(I2C0, I2C_I2CON_SI_AA);
            break;
            case 0x50://RX
                I2CPortHandle.I2C0Handle.pRxBuff[I2CPortHandle.I2C0Handle.RxLenFlag++] = (unsigned char) I2C_GET_DATA(I2C0);
                if(I2CPortHandle.I2C0Handle.RxBuffSize -1 == I2CPortHandle.I2C0Handle.RxLenFlag)
                    I2C_SET_CONTROL_REG(I2C0, I2C_I2CON_SI);
                else
                    I2C_SET_CONTROL_REG(I2C0, I2C_I2CON_SI_AA);
            break;
            case 0x58://RX End
                I2CPortHandle.I2C0Handle.EndFlag = 1;
                I2CPortHandle.I2C0Handle.pRxBuff[I2CPortHandle.I2C0Handle.RxLenFlag] = (unsigned char) I2C_GET_DATA(I2C0);
                I2C_SET_CONTROL_REG(I2C0, I2C_I2CON_STO_SI);
            break;
            default:
                I2CPortHandle.I2C0Handle.Error = u32Status;
                printf("Status 0x%x is NOT processed\n", u32Status);    
            break;
        }
    }
}
void I2C1_IRQHandler(void)
{
    uint32_t u32Status;
    u32Status = I2C_GET_STATUS(I2C1);
    if(I2C_GET_TIMEOUT_FLAG(I2C1))
    {
        /* Clear I2C1 Timeout Flag */
        I2C_ClearTimeoutFlag(I2C1);
    }
    else
    {
        switch (u32Status)
        {
            case 0x08://start ack//done
                if(I2CPortHandle.I2C1Handle.mode  == 1)
                    I2C_SET_DATA(I2C1, (I2CPortHandle.I2C1Handle.Addr & 0xFE));//設定dev w addr
                else
                    I2C_SET_DATA(I2C1, (I2CPortHandle.I2C1Handle.Addr | 0x01));//設定設定dev r addr
                I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI);//清除中斷
            break;
            case 0x18://done
                I2C_SET_DATA(I2C1, I2CPortHandle.I2C1Handle.pTxBuff[I2CPortHandle.I2C1Handle.TxLenFlag++]);//設定Tx資料
                I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI);//清除中斷
            break;
            case 0x20://nack done
                I2C_STOP(I2C1);//收到nack 停止I2C
                I2C_START(I2C1);//重啟I2C
            break;
            case 0x28://TX done
                if(I2CPortHandle.I2C1Handle.TxLenFlag == I2CPortHandle.I2C1Handle.TxBuffSize)//TX finish
                    I2CPortHandle.I2C1Handle.EndFlag = 1;
                else
                    I2C_SET_DATA(I2C1, I2CPortHandle.I2C1Handle.pTxBuff[I2CPortHandle.I2C1Handle.TxLenFlag++]);
                I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI);
            break;
            case 0x10://repeat start done
                I2C_SET_DATA(I2C1, (I2CPortHandle.I2C1Handle.Addr | 0x01));//設定設定dev r addr
                I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI);//清除中斷
            break;
            case 0x40://done
                if(I2CPortHandle.I2C1Handle.RxBuffSize == 1)//長度為1則不設AA
                    I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI);
                else
                    I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
            break;
            case 0x50://RX
                I2CPortHandle.I2C1Handle.pRxBuff[I2CPortHandle.I2C1Handle.RxLenFlag++] = (unsigned char) I2C_GET_DATA(I2C1);
                if(I2CPortHandle.I2C1Handle.RxBuffSize -1 == I2CPortHandle.I2C1Handle.RxLenFlag)
                    I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI);
                else
                    I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
            break;
            case 0x58://RX End
                I2CPortHandle.I2C1Handle.EndFlag = 1;
                I2CPortHandle.I2C1Handle.pRxBuff[I2CPortHandle.I2C1Handle.RxLenFlag] = (unsigned char) I2C_GET_DATA(I2C1);
                I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_STO_SI_AA);
            break;
            default:
                I2CPortHandle.I2C1Handle.Error = u32Status;
                printf("Status 0x%x is NOT processed\n", u32Status);    
            break;
        }
    }
}

Here is my code for continuous transmission:

uint8_t reg_cmd_array[BQ40Z50_COMMAND_LEN][3]={
    {BQ40Z50_TEMPERATURE_COMMAND,1,2},
    {BQ40Z50_VOLTAGE_COMMAND,1,2},
    {BQ40Z50_CURRNT_COMMAND,1,2},
    {BQ40Z50_AVERAGECURRENT_COMMAND,1,2},
    {BQ40Z50_RSOC_COMMAND,1,1},
    {BQ40Z50_ASOC_COMMAND,1,1},
    {BQ40Z50_REMAININGCAPACITY_COMMAND,1,2},
    {BQ40Z50_FULLCHARGECAPACITY_COMMAND,1,2},
    {BQ40Z50_CHRAGINGCURRNT_COMMAND,1,2},
    {BQ40Z50_CHRAGINGVOLTAGE_COMMAND,1,2},
    {BQ40Z50_BATTEARYSTATUS_COMMAND,1,2},
    {BQ40Z50_DESIGNCAPACITY_COMMAND,1,2},
    {BQ40Z50_DESIGNVOLATGE_COMMAND,1,2},
    {BQ40Z50_CELLVOLTAGE4_COMMAND,1,2},
    {BQ40Z50_CELLVOLTAGE3_COMMAND,1,2},
    {BQ40Z50_CELLVOLTAGE2_COMMAND,1,2},
    {BQ40Z50_CELLVOLTAGE1_COMMAND,1,2},
    {BQ40Z50_SOH_COMMAND,1,1},
};
uint8_t  SendSBSCommand(uint8_t* cmd,uint8_t cmdlen,uint8_t* rxbuff,uint8_t len)
{
    uint8_t status;
    MID_IIC_Master_TX(BQ40Z50_IIC_PORT,BQ40Z50_DEV_ADDR,cmd,cmdlen,50000);
    status =  MID_IIC_Master_RX(BQ40Z50_IIC_PORT,BQ40Z50_DEV_ADDR,rxbuff,len,50000);
    return status;
}
void BQ40Z50Dataprocessor(void)
{   
    uint8_t rxtemp[4];
    for (uint8_t i = 0; i < BQ40Z50_COMMAND_LEN; i++)
    {
        SendSBSCommand(&reg_cmd_array[i][0],reg_cmd_array[i][1],rxtemp,reg_cmd_array[i][2]);
        switch (reg_cmd_array[i][0])
        {
            case BQ40Z50_TEMPERATURE_COMMAND:
                DataPool.BQ40Z50.Temperaure = BytesToWord(rxtemp);
                printf("temp is %x\r\n",DataPool.BQ40Z50.Temperaure);
            break;
            case BQ40Z50_VOLTAGE_COMMAND:
                DataPool.BQ40Z50.Voltage = BytesToWord(rxtemp);
                printf("vol is %x\r\n",DataPool.BQ40Z50.Voltage);
            break;
            /*case BQ40Z50_CURRNT_COMMAND:
            break;
            case BQ40Z50_AVERAGECURRENT_COMMAND:
            break;
            case BQ40Z50_RSOC_COMMAND:
            break;
            case BQ40Z50_ASOC_COMMAND:
            break;
            case BQ40Z50_REMAININGCAPACITY_COMMAND:
            break;
            case BQ40Z50_FULLCHARGECAPACITY_COMMAND:
            break;
            case BQ40Z50_CHRAGINGCURRNT_COMMAND:
            break;
            case BQ40Z50_CHRAGINGCURRNT_COMMAND:
            break;
            case BQ40Z50_CHRAGINGCURRNT_COMMAND:
            break;
            case BQ40Z50_CHRAGINGCURRNT_COMMAND:
            break;
            case BQ40Z50_CHRAGINGCURRNT_COMMAND:
            break;
            case BQ40Z50_CHRAGINGCURRNT_COMMAND:
            break;
            default:
            break;*/
        }
        CLK_SysTickDelay(1000);
    }

}
wschang0 commented 1 day ago

I suggest you don't close I2C in Tx/Rx API. Don't call I2C_DisableInt(i2c); I2C_Close(i2c); to close I2C, if you still want to use them next time. By the way, you also don't need to re-open it. You may refer I2C protocol in TRM and use Scop to monitor the transer on line to check what's going on. M051 TRM: [https://www.[nuvoton.com/resource-files/TRM_M051(BN_DN_DE)_Series_EN_Rev1.03.pdf](https://www.nuvoton.com/resource-files/TRM_M051(BN_DN_DE)_Series_EN_Rev1.03.pdf)](M051 TRM)

in section 6.12.7.1 and 6.12.7.2, you could find the protocol flow of I2C.

prometheans152 commented 23 hours ago

I have already read it, and I designed my API based on Figure 6-82 and Figure 6-23.

I am currently stuck after a complete transmission (one TX and one RX). I am unable to execute the next transmission. I printed the I2C status after the transmission ended, and the status shows 0xF8.

Next, I modified the code to execute only one transmission. I connected an oscilloscope to observe the transmission process. I noticed that at the end of the transmission (P), the SCL keeps sending clock signals and doesn't stop.

The waveform is as shown in the photo. 314009

The changes in the status code during the transmission process are shown in the picture. status From my perspective, the status code changes match my expectations.

prometheans152 commented 21 hours ago

I modified my code to try sending only one TX, and after the TX is completed, I release the I2C. image I also modified the interrupt code to send I2C_I2CON_STO_SI when the status code is 0x28, ending the transmission. (After ending, the status code becomes 0xF8, indicating that the bus is released.) image image Upon execution, I observed the status code changes as expected. image After confirming that the status is 0xF8, I used an oscilloscope to measure and found that the SCL is still continuously sending clock signals. image In my understanding, the Master has absolute control in the I2C communication. If the Master wants to end the transmission, the Slave cannot prevent it. However, in this issue, I actively instructed the communication to stop, but in reality, it didn’t stop (SCL keeps sending clock signals). I believe this is a problem. Or is there another standard method to stop the communication?

wschang0 commented 4 hours ago

Hi,

I sugguest you to remove "disable interrupt" and close I2C from your code.

It is still not work, you may use our sample code I2C_Master and I2C_Slave to check your hardware environment. If the sample code running well, it means the hardware environment is ok. Next, you could modify the sample code for what you want step by step for easy to figure out what's going on.

prometheans152 commented 3 hours ago

I have already remove the code that disables interrupts and closes I2C. I tested under the condition of disabling interrupts and closing I2C for the previous two comments as well. The current issue is that after completing a transmission (issuing a P), the SCL continues to send clocks. However, at this moment, the status register indicates that the bus is idle (0xF8).

prometheans152 commented 3 hours ago

By the way, My program works fine when 'interrupts are disabled and I2C is closed,' but I feel it doesn't make sense. That's why I'm raising this issue.

wschang0 commented 3 hours ago

Hi,

For continuse SCL, it may be casued by timing issue, that's why I sugguest you to verify the hardware environment by m051 sample code.

You may slow down the I2C clock, modify the pull up resistor value and try again. By the way, it is recommanded to enable Schmitt for I/O of I2C for better reliability. You may refer to TRM and search Px_MFP register to enable it.

prometheans152 commented 21 minutes ago

Hi,

I have used the method of disabling interrupts and closing I2C to verify the hardware. In this state, I was able to successfully retrieve temperature and voltage data from the slave, and the data was correct. Do you think this method is not feasible?

My clock settings are shown in the diagram below. image This was modified based on your example. I didn’t use XTL12M, so I switched to HIRC.

I modified the example (EEPROM) for testing, and the same issue occurred.