打印
[STM32F0]

网上PMBus资料太少,大师是否可以帮忙指点迷津?

[复制链接]
1980|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
hzocce|  楼主 | 2020-4-3 20:54 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 hzocce 于 2020-4-4 19:42 编辑

看了老半天,没有看懂PMBus,MCU需要怎么去设置呢?

有配置好的范例参考么?
初始化,收发那些。

使用特权

评论回复
沙发
hzocce|  楼主 | 2020-4-4 11:31 | 只看该作者
有做过的朋友没?

使用特权

评论回复
板凳
hzocce|  楼主 | 2020-4-4 16:03 | 只看该作者
本帖最后由 hzocce 于 2020-4-4 16:04 编辑

按理应该是:

PMBus_Init();
PMBus_SendByte();
PMbus_RecByte();

可是为什么找不到呢?

----------------------------------------------------------------------------------------------------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
----------------------------------------------------------------------------------------------------------------------------------------------------

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


#include "main.h"
#include "stm32_PMBUS_stack.h"
/** @addtogroup STM32F3xx_HAL_Examples
  * @{
  */
/** @addtogroup SMBUS
  * @{
  */
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#ifdef ARP
#ifdef HOST1
static uint32_t        ARP_process = 0U;
#else /* HOST1 */
static uint8_t         UDID[16] =
{
  0x81, 0x23, 0x45, 0x67, 0x78, 0x91, 0xBC, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif /* HOST1 */
#ifdef SMB2
static uint8_t         UDID2[16] =
{
  0x81, 0x23, 0x45, 0x67, 0x78, 0x97, 0xBC, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif /* SMB2 */
#endif /* ARP */
#ifdef TEST4
#ifdef HOST1
static uint8_t         TEST_PLAN[8] =
{
  0x25, 0x1A, 0x10, 0x78, 0x90, 0x14, 0x3D, 0x65
};
#endif /* HOST1 */
#endif /* TEST4 */
uint32_t               buttonpress = 0U;
SMBUS_HandleTypeDef    handle1, handle2;
uint8_t                deviceList[MAX_DEVICE_NUM];
/* Private function prototypes -----------------------------------------------*/
static void SystemClock_Config(void);
static void Error_Handler(void);
static void Error_Check( SMBUS_StackHandleTypeDef *pStackContext);
/* Private functions ---------------------------------------------------------*/
/**
  * @brief  Callback function notifying about extended command incoming, implementation
    is supporting extended command defined by PMBus
  * @param  pStackContext : Pointer to a SMBUS_StackHandleTypeDef structure that contains
  *                the configuration information for the specified SMBUS.
  * @retval HAL_StatusTypeDef response code. Equal STACK_OK if success, any other value means problem
  */
HAL_StatusTypeDef STACK_SMBUS_ExtendCommand( SMBUS_StackHandleTypeDef *pStackContext )
{
  uint16_t       size = 0U;
  uint8_t       *piobuf = NULL;
  /* accessing the IO buffer */
  piobuf = STACK_SMBUS_GetBuffer( pStackContext );
  /*
    Extended command must be identified by the value of the actual command code
   */
  switch ( piobuf[0] )
  {
    case 0:
      pStackContext->CurrentCommand = (st_command_t *) & EXTENDED_READ_BYTE;
      break;
    case 1:
      pStackContext->CurrentCommand = (st_command_t *) & EXTENDED_READ_WORD;
      break;
    case 2:
      pStackContext->CurrentCommand = (st_command_t *) & EXTENDED_WRITE_BYTE;
      /*
        size of the bytes yet to be received
       */
      size = 1U;
      break;
    case 3:
      pStackContext->CurrentCommand = (st_command_t *) & EXTENDED_WRITE_WORD;
      size = 2U;
      break;
    default:
      pStackContext->StateMachine |= SMBUS_SMS_ERROR;
      return STACK_ERROR;
  }
  /*
    reading the remaining data (stack won't do that for us this time
  */
  if ((pStackContext->CurrentCommand->cmnd_query & WRITE) == WRITE )
  {
    /*
      To make sure the executecommand is called again once the remaining data
      is in the buffer
    */
    STACK_PMBUS_ExtendExecution(pStackContext);
    /*
    PEC byte y/n?
    */
    if ((pStackContext->StateMachine & SMBUS_SMS_PEC_ACTIVE ) == SMBUS_SMS_PEC_ACTIVE )
    {
      size += PEC_SIZE;
    }
    /*
      asking the HAL for more bytes to receive
    */
    pStackContext->Byte_count += size;
    HAL_SMBUS_Slave_Receive_IT( pStackContext->Device, &(pStackContext->Buffer[2]), size, SMBUS_NEXT_FRAME );
  }
  return STACK_OK;
}
/**
  * @brief  Callback function notifying slave about command incoming, implementation
    is supporting extended command defined by PMBus
  * @param  pStackContext : Pointer to a SMBUS_StackHandleTypeDef structure that contains
  *                the configuration information for the specified SMBUS.
  * @retval HAL_StatusTypeDef response code. Equal STACK_OK if success, any other value means problem
  */
HAL_StatusTypeDef STACK_SMBUS_ExecuteCommand( SMBUS_StackHandleTypeDef *pStackContext )
{
  uint8_t       *piobuf = NULL;
  /* accessing the IO buffer */
  piobuf = STACK_SMBUS_GetBuffer( pStackContext );
  if ( piobuf == NULL )
  {
    pStackContext->StateMachine |= SMBUS_SMS_ERROR;
  }
  else if ( pStackContext->CurrentCommand == (st_command_t *)&HOST_NOTIFY_PROTOCOL )
  {
    /* host notify command */
    if ( pStackContext->Buffer[0] == SMBUS_ADDR_DEFAULT )
    {
      /* Usually the Host notify is used for ARP, but may not be limited to it */
#ifdef ARP
#ifdef HOST1
      /* case of ARP notify */
      ARP_process = 1U;
#endif /* HOST1 */
#endif /* ARP */
    }
  }
  else    /* Normal device command execution */
  {
    /* Zone config command example implementation */
#ifdef PMBUS13
    if ( pStackContext->CurrentCommand->cmnd_code == PMBC_ZONE_CONFIG )
    {
      pStackContext->TheZone.writeZone = pStackContext->Buffer[1];
      pStackContext->TheZone.readZone = pStackContext->Buffer[2];
    }
    else if ( pStackContext->CurrentCommand->cmnd_code ==  PMBC_ZONE_ACTIVE )
    {
      pStackContext->TheZone.activeWriteZone = pStackContext->Buffer[1];
      pStackContext->TheZone.activeReadZone = pStackContext->Buffer[2];
    }
#endif /* PMBUS13 */
    /*
      first step is to see if we have a case of extended command
    */
    if ( pStackContext->CurrentCommand->cmnd_code == PMBC_PMBUS_COMMAND_EXT )
    {
      BSP_LED_Toggle((Led_TypeDef)((piobuf[0] & 0x07U)));
    }
    else /* regular command case */
    {
      BSP_LED_Toggle((Led_TypeDef)(pStackContext->CurrentCommand->cmnd_code & 0x07U));
      if ((pStackContext->CurrentCommand->cmnd_query & BLOCK ) == BLOCK )
      {
        *piobuf = (pStackContext->CurrentCommand->cmnd_master_Rx_size) - 1U;
        /* byte size of reply for block read command */
        /* One byte for size, rest are [size] of data */
      }
    }
  }
  return STACK_OK;
}
/**
  * @brief  Stub of an error treatment function - set to ignore most errors
  * @param  pStackContext : Pointer to a SMBUS_StackHandleTypeDef structure that contains
  *                the configuration information for the specified SMBUS.
  * @retval None
  */
static void Error_Check( SMBUS_StackHandleTypeDef *pStackContext)
{
  if ( ( STACK_SMBUS_IsBlockingError(pStackContext) ) || ( STACK_SMBUS_IsCmdError( pStackContext ) ) )
  {
    /* No action, error symptoms are ignored */
    pStackContext->StateMachine &= ~(SMBUS_ERROR_CRITICAL | SMBUS_COM_ERROR);
  }
  else if ((pStackContext->StateMachine & SMBUS_SMS_ERR_PECERR ) ==
           SMBUS_SMS_ERR_PECERR ) /* PEC error, we won't wait for any more action */
  {
    pStackContext->StateMachine |= SMBUS_SMS_READY;
    pStackContext->CurrentCommand = NULL;
    pStackContext->StateMachine &= ~(SMBUS_SMS_ACTIVE_MASK | SMBUS_SMS_ERR_PECERR);
  }
}
/**
  * @brief  Main program - executes various test sequences
  * @param  None
  * @retval None
  */
int main(void)
{
#ifdef HOST1
  uint32_t      commandi = 0U;
#ifdef TEST5
  SMBUS_ZoneStateTypeDef TheZone;
#endif /* TEST5 */
#endif /* HOST1 */
  uint8_t       *piobuf;
  SMBUS_HandleTypeDef *phandle1;
  SMBUS_StackHandleTypeDef *pcontext1;
  SMBUS_StackHandleTypeDef context1;
#ifdef SMB2
  SMBUS_HandleTypeDef *phandle2;
  SMBUS_StackHandleTypeDef *pcontext2;
  SMBUS_StackHandleTypeDef context2;
#endif /* SMB2 */
  uint32_t      index;
  /* Reset of all peripherals, Initializes the Flash interface and the systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Configure LEDs */
  BSP_LED_Init(LED3);
  BSP_LED_Init(LED4);
  BSP_LED_Init(LED5);
  BSP_LED_Init(LED6);
#ifdef STM32F303xC
  BSP_LED_Init(LED7);
  BSP_LED_Init(LED8);
  BSP_LED_Init(LED9);
  BSP_LED_Init(LED10);
#endif /*STM32F303xC*/
  /* SMBUS instance initialization - smbus 1. */
  handle1.Instance = SMBUS1;
  handle1.Init.Timing = SMBUS_TIMING_100K;
  handle1.Init.AnalogFilter = SMBUS_ANALOGFILTER_ENABLE;
  handle1.Init.AddressingMode = SMBUS_ADDRESSINGMODE_7BIT;
#ifdef PMBUS13
  handle1.Init.DualAddressMode = SMBUS_DUALADDRESS_ENABLE;
  handle1.Init.OwnAddress2 = 0x40U;
  handle1.Init.OwnAddress2Masks = SMBUS_OA2_MASK05;
#else /* PMBUS13 */
  handle1.Init.DualAddressMode = SMBUS_DUALADDRESS_DISABLE;
  handle1.Init.OwnAddress2 = 0U;
  handle1.Init.OwnAddress2Masks = SMBUS_OA2_NOMASK;
#endif /* PMBUS13 */
  handle1.Init.GeneralCallMode = SMBUS_GENERALCALL_DISABLE;
  handle1.Init.NoStretchMode = SMBUS_NOSTRETCH_DISABLE;
#ifdef ARP
#ifdef DEV_PSA
  handle1.Init.OwnAddress1 = SMBUS_ADDR_DEVICE;
#else /* DEV_PSA */
  handle1.Init.OwnAddress1 = 0U;
#endif /* DEV_PSA */
  handle1.Init.PeripheralMode = SMBUS_PERIPHERAL_MODE_SMBUS_SLAVE_ARP;
#else      /* ARP */
  handle1.Init.OwnAddress1 = SMBUS_ADDR_DEVICE;
  handle1.Init.PeripheralMode = SMBUS_PERIPHERAL_MODE_SMBUS_SLAVE;
#endif /* ARP */
#ifdef USE_PEC
  handle1.Init.PacketErrorCheckMode = SMBUS_PEC_ENABLE;
#else
  handle1.Init.PacketErrorCheckMode = SMBUS_PEC_DISABLE;
#endif /* USE_PEC */
#ifdef HOST1
  handle1.Init.PeripheralMode = SMBUS_PERIPHERAL_MODE_SMBUS_HOST;
  handle1.Init.OwnAddress1 = 0U;
#endif /* HOST1 */
  handle1.Init.SMBusTimeout = SMBUS_TIMEOUT_DEFAULT;
  handle1.pBuffPtr = context1.Buffer;
  phandle1 = &handle1;
  HAL_SMBUS_Init( phandle1 );
  context1.CMD_table = (st_command_t *) & PMBUS_COMMANDS_TAB[0];
  context1.CMD_tableSize = PMBUS_COMMANDS_TAB_SIZE;
#ifndef TEST4
#ifndef TEST5
  /* Most tests do not use actual PMBUS commands */
  context1.CMD_table = (st_command_t *) & PMBUS_COMMANDS_TEST[0];
  context1.CMD_tableSize = PMBUS_CMD_TBL_SIZE;
#endif /* TEST4 */
#endif /* TEST5 */
  context1.Device = phandle1;
  context1.SRByte = 0x55U;
  context1.CurrentCommand = NULL;
#ifdef ARP
  context1.StateMachine = SMBUS_SMS_NONE;
#ifdef DEV_PSA
  context1.OwnAddress = SMBUS_ADDR_DEVICE;
#else /* DEV_PSA */
  context1.OwnAddress = 0U;
#endif /* DEV_PSA */
#ifndef  HOST1
  context1.ARP_UDID = (uint8_t *) &UDID;
#endif /* HOST1 */
#else /* ARP */
  context1.StateMachine = SMBUS_SMS_ARP_AR;
  context1.OwnAddress = SMBUS_ADDR_DEVICE;
#endif /* ARP */
#ifdef USE_PEC
  context1.StateMachine |= SMBUS_SMS_PEC_ACTIVE;
#endif
  pcontext1 = &context1;
  STACK_SMBUS_Init( pcontext1 );
  /* SMBUS instance initialization - smbus 2. */
#ifdef SMB2
  handle2.Instance = SMBUS2;
  handle2.Init.Timing = SMBUS_TIMING_100K;
  handle2.Init.AnalogFilter = SMBUS_ANALOGFILTER_ENABLE;
  handle2.Init.AddressingMode = SMBUS_ADDRESSINGMODE_7BIT ;
#ifdef PMBUS13
  handle2.Init.DualAddressMode = SMBUS_DUALADDRESS_ENABLE;
  handle2.Init.OwnAddress2 = 0x40U;
  handle2.Init.OwnAddress2Masks = SMBUS_OA2_MASK05;
#else /* PMBUS13 */
  handle2.Init.DualAddressMode = SMBUS_DUALADDRESS_DISABLE;
  handle2.Init.OwnAddress2 = 0U;
  handle2.Init.OwnAddress2Masks = SMBUS_OA2_NOMASK;
#endif /* PMBUS13 */
  handle2.Init.GeneralCallMode = SMBUS_GENERALCALL_DISABLE;
  handle2.Init.NoStretchMode = SMBUS_NOSTRETCH_DISABLE;
#ifdef ARP
#ifdef DEV_PSA
  handle2.Init.OwnAddress1 = SMBUS_ADDR_DEVICE + 2U;
#else /* DEV_PSA */
  handle2.Init.OwnAddress1 = 0U;
#endif /* DEV_PSA */
  handle2.Init.PeripheralMode = SMBUS_PERIPHERAL_MODE_SMBUS_SLAVE_ARP ;
#else /* ARP */
  handle2.Init.OwnAddress1 = SMBUS_ADDR_DEVICE + 2U;
  handle2.Init.PeripheralMode = SMBUS_PERIPHERAL_MODE_SMBUS_SLAVE ;
#endif /* ARP */
#ifdef USE_PEC
  handle2.Init.PacketErrorCheckMode = SMBUS_PEC_ENABLE;
#else
  handle2.Init.PacketErrorCheckMode = SMBUS_PEC_DISABLE;
#endif /* USE_PEC */
  handle2.Init.SMBusTimeout = SMBUS_TIMEOUT_DEFAULT;
  handle2.pBuffPtr = context2.Buffer;
  phandle2 = &handle2;
  HAL_SMBUS_Init( phandle2 );
#ifdef TEST4
  context2.CMD_table = (st_command_t *) & PMBUS_COMMANDS_TAB[0];
  context2.CMD_tableSize = PMBUS_COMMANDS_TAB_SIZE;
#else /* TEST4 */
  context2.CMD_table = (st_command_t *) & PMBUS_COMMANDS_TEST[0];
  context2.CMD_tableSize = PMBUS_CMD_TBL_SIZE;
#endif /* TEST4 */
  context2.Device = phandle2;
  context2.SRByte = 0x55U;
  context2.CurrentCommand = NULL;
#ifdef ARP  
  context2.StateMachine = SMBUS_SMS_NONE;
  context2.ARP_UDID = (uint8_t *) & UDID2;
#ifdef DEV_PSA
  context2.OwnAddress = SMBUS_ADDR_DEVICE + 2U;
#else /* DEV_PSA */
  context2.OwnAddress = 0U;
#endif /* DEV_PSA */
#else /* ARP */
  context2.StateMachine = SMBUS_SMS_ARP_AR;
  context2.OwnAddress = SMBUS_ADDR_DEVICE + 2U;
#endif /* ARP */
#ifdef USE_PEC
  context2.StateMachine |= SMBUS_SMS_PEC_ACTIVE;
#endif
  pcontext2 = &context2;
  STACK_SMBUS_Init( pcontext2 );
#endif /*SMB2*/
  /* Configure the control */
  BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
  piobuf = STACK_SMBUS_GetBuffer( pcontext1 );
  if (piobuf != NULL )
  {
    for (index = 0U; index < STACK_NBYTE_SIZE; index++)
    {
      piobuf[index] = 0U;
    }
  }
#ifdef HOST1
  while (1)
  {
#ifdef ARP
    if ( ARP_process == 1U)
    {
      for (index = 0U; index < 8U; index++)
      {
        deviceList[index] = 0U;
      }
      index = 0U;
      /* sending ARP prepare */
      STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&PMBUS_COMMANDS_ARP[0], SMBUS_ADDR_DEFAULT, WRITE);
      while ( !STACK_SMBUS_IsReady(pcontext1))
      {
        Error_Check( pcontext1 );
      }
      ARP_process ++;
      pcontext1->StateMachine &= ~SMBUS_SMS_ERR_ACKF ;
    }
    while (ARP_process == 2U)
    {
      /* send ARP getID */
      STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&PMBUS_COMMANDS_ARP[2], SMBUS_ADDR_DEFAULT, READ);
      while ( !STACK_SMBUS_IsReady(pcontext1))
      {
        Error_Check( pcontext1 );
      }
      if ((pcontext1->Buffer[2] != 0xFFU)
          && (( pcontext1->StateMachine & (SMBUS_SMS_ERR_ACKF | SMBUS_SMS_ERR_PECERR) ) == 0U)) /* not empty answer */
      {
        /* select a free address */
        deviceList[index] = (uint8_t)(2U * index + 0x16U);
        piobuf = STACK_SMBUS_GetBuffer( pcontext1 );
        if (piobuf == NULL )
        {
          break;
        }
        piobuf[0] = 17U;
        piobuf[17] = deviceList[index];
        STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&PMBUS_COMMANDS_ARP[3], SMBUS_ADDR_DEFAULT, WRITE);
        while ( !STACK_SMBUS_IsReady(pcontext1))
        {
          Error_Check( pcontext1 );
        }
        if (STACK_SMBUS_IsCmdError( pcontext1 ))
        {
          deviceList[index] = 0U;
        }
        else
        {
          index++;
          index &= 7U;
        }
        /* Wait until Tamper push-button is pressed ########################*/
        while (buttonpress == 0U)
        {}
        buttonpress = 0U;
      }
      else
      {
        pcontext1->StateMachine &= ~(SMBUS_SMS_ERR_PECERR | SMBUS_SMS_ERR_BTO);
        ARP_process++;
      }
    }
#else /* ARP */
    deviceList[0] = SMBUS_ADDR_DEVICE;
#endif  /* ARP */
    BSP_LED_On((Led_TypeDef)(commandi & 0x07U));
    HAL_Delay( 250U );
   
#ifdef TEST5
    if (deviceList[0] != 0U)
    {   
    /* TEST5 - sending different Zone commands */
    switch (commandi)
    {
      case 0:
        TheZone.writeZone = 0x01U;
        TheZone.readZone = 0x05U;
        STACK_PMBUS_MasterZoneConfig(pcontext1, SMBUS_ADDR_DEVICE, &TheZone);
        break;
      case 1:
        TheZone.activeWriteZone = 0x01U;
        TheZone.activeReadZone = 0x05U;
        STACK_PMBUS_MasterZoneActive(pcontext1, &TheZone);
        break;
      case 2:
        pcontext1->Buffer[1] = 0x5aU;
        STACK_PMBUS_MasterZoneWrite(pcontext1, (st_command_t *)&PMBUS_COMMANDS_TAB[13] );
        break;
      case 3:
        STACK_PMBUS_MasterReadZoneStatus( pcontext1, 0x00U, 0xFFU);
        break;
      default:
        commandi &= 3U;
        break;
    }
    }
#else
#ifdef TEST4
    if (deviceList[0] != 0U)
    {
      pcontext1->CMD_table = (st_command_t *) & PMBUS_COMMANDS_TAB[0];
      /* Test plan is 8 records */
      commandi &= 7U;
      
      /* Testing selected PMBUS command codes */
      index = TEST_PLAN[commandi];
      /* buffer preparation */
      if (PMBUS_COMMANDS_TAB[commandi].cmnd_query & BLOCK )
      {
        piobuf = STACK_SMBUS_GetBuffer( pcontext1 );
        piobuf[0] = 5U;
      }
      /* Command issued */
      if ( PMBUS_COMMANDS_TAB[index].cmnd_query & WRITE )
      {
        STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&PMBUS_COMMANDS_TAB[index], (uint16_t)deviceList[0], WRITE);
      }
      else if ( PMBUS_COMMANDS_TAB[index].cmnd_query & READ )
      {
        STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&PMBUS_COMMANDS_TAB[index], (uint16_t)deviceList[0], READ);
      }
      else
      {
        STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&PMBUS_COMMANDS_TAB[index], (uint16_t)deviceList[0], 0U);
      }
    }
#else /* TEST4 */
    /* buffer preparation */
    if ((PMBUS_COMMANDS_TEST[commandi].cmnd_query & BLOCK) == BLOCK )
    {
      piobuf = STACK_SMBUS_GetBuffer( pcontext1 );
      if ( piobuf == NULL )
      {
        Error_Check( pcontext1 );
      }
      else
      {
        piobuf[0] = PMBUS_COMMANDS_TEST[commandi].cmnd_master_Tx_size - 2U;
      }
    }
    if (deviceList[0] != 0U)
    {
#ifdef TEST3
      /* writing extended command code */
      piobuf = STACK_SMBUS_GetBuffer( pcontext1 );
      if ( piobuf == NULL )
      {
        Error_Check( pcontext1 );
      }
      else
      {
        piobuf[0] = (uint8_t)commandi;
      }
      /* issuing extended commands */
      switch (commandi)
      {
        case 0:
          STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&EXTENDED_READ_BYTE, (uint16_t)deviceList[0], 0U);
          break;
        case 1:
          STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&EXTENDED_READ_WORD, (uint16_t)deviceList[0], 0U);
          break;
        case 2:
          STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&EXTENDED_WRITE_BYTE, (uint16_t)deviceList[0], WRITE);
          break;
        case 3:
          STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&EXTENDED_WRITE_WORD, (uint16_t)deviceList[0], WRITE);
          break;
        default:
          commandi = 0U;
          break;
      }
#else   /* TEST3 */
      if (commandi == 0U)
      {
#ifdef TEST2
        /* command group test case */
        for ( index = 0U; index < 4U; index++)
        {
          STACK_PMBUS_HostCommandGroup( pcontext1, (st_command_t *)&PMBUS_COMMANDS_TEST[commandi],
                                        SMBUS_ADDR_DEFAULT + (uint16_t)index - 3U,
                                        (index == 3U) ? 1U : 0U );
          while
          (
            ( pcontext1->Device->State != HAL_SMBUS_STATE_READY ) &&
            ( pcontext1->Device->State != HAL_SMBUS_STATE_LISTEN )
          )
          {}
        }
        /*
        Note: the group command assumes presence of several devices. In their absence it fails to transmit successfully.
        We then reset the stack error.
        */
        pcontext1->StateMachine |= SMBUS_SMS_READY;
        pcontext1->CurrentCommand = NULL;
        pcontext1->StateMachine &= ~(SMBUS_SMS_ACTIVE_MASK | SMBUS_ERROR_CRITICAL);
#else /* TEST2 */
        /* testing receive byte */
        STACK_SMBUS_HostRead( pcontext1, &(context1.SRByte), (uint16_t)deviceList[0]);
#endif /* TEST2 */
      }
      /* issuing command from test table */
      else if (( PMBUS_COMMANDS_TEST[commandi].cmnd_query & READ ) == READ )
      {
        STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&PMBUS_COMMANDS_TEST[commandi], (uint16_t)deviceList[0], READ);
      }
      else if (( PMBUS_COMMANDS_TEST[commandi].cmnd_query & PROCESS_CALL ) == PROCESS_CALL )
      {
        STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&PMBUS_COMMANDS_TEST[commandi], (uint16_t)deviceList[0], 0U);
      }
      else
      {
        STACK_SMBUS_HostCommand( pcontext1, (st_command_t *)&PMBUS_COMMANDS_TEST[commandi], (uint16_t)deviceList[0], WRITE);
      }
#endif /* TEST3 */
    }
#endif /* TEST4 */
#endif /* TEST5 */
    while ( STACK_SMBUS_IsReady(pcontext1) != SMBUS_SMS_READY)
    {
      Error_Check( pcontext1 );
    }
    for (index = 0; index < 8; index++)
    {
      BSP_LED_Off((Led_TypeDef)index);
    }
    HAL_Delay( 750U );
    if (buttonpress == 1U)
    {
      /* next test case */
      commandi++;
      if (commandi >= PMBUS_CMD_TBL_SIZE)
      {
        commandi = 0U;
      }
      buttonpress = 0U;
    }
  }
#else   /* HOST1 */
#ifdef ARP
  if ( ( context1.OwnAddress == 0U ) || ((context1.StateMachine & SMBUS_SMS_ARP_AR) == 0U ) )
  {
    STACK_SMBUS_NotifyHost( pcontext1 );
  }
#ifdef SMB2
  else if ( ( context2.OwnAddress == 0U ) || ((context2.StateMachine & SMBUS_SMS_ARP_AR) == 0U ) )
  {
    STACK_SMBUS_NotifyHost( pcontext2 );
  }
#endif /* SMB2 */
#endif /* ARP */
  while (1)
  {
    HAL_Delay( 200U );
    /* Periodically checking error state of the stack */
    Error_Check( &context1 );
    /* Periodically checking for Quick command */
    if ( context1.StateMachine & ( SMBUS_SMS_QUICK_CMD_W | SMBUS_SMS_QUICK_CMD_R ) )
    {
      BSP_LED_On((Led_TypeDef)0U);
      context1.StateMachine &= ~( SMBUS_SMS_QUICK_CMD_W | SMBUS_SMS_QUICK_CMD_R );
    }
#ifdef SMB2
    Error_Check( &context2 );
    if ( context2.StateMachine & ( SMBUS_SMS_QUICK_CMD_W | SMBUS_SMS_QUICK_CMD_R ) )
    {
      BSP_LED_On((Led_TypeDef)0U);
      context2.StateMachine &= ~( SMBUS_SMS_QUICK_CMD_W | SMBUS_SMS_QUICK_CMD_R );
    }
    context2.StateMachine &= ~SMBUS_SMS_ERR_ARLO;
#endif /* SMB2 */
    /* Dim the LED */
    for (index = 0; index < 8; index++)
    {
      BSP_LED_Off((Led_TypeDef)index);
    }
    /* Cleaning arbitration loss flag */
    context1.StateMachine &= ~SMBUS_SMS_ERR_ARLO;
    if (buttonpress == 1U)
    {
#ifdef ALERT
      STACK_SMBUS_SendAlert( pcontext1 );
#else /* ALERT */
      STACK_SMBUS_NotifyHost( pcontext1 );
#endif /* ALERT */
      buttonpress = 0U;
    }
  }
#endif /* HOST1 */
}
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
static void Error_Handler(void)
{
  /* User may add here some code to deal with this error */
  while (1)
  {}
}
/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow :
  *        System Clock source                    | PLL (HSI)
  *        SYSCLK(Hz)                             | 48000000
  *        HCLK(Hz)                               | 48000000
  *        AHB Prescaler                          | 1
  *        APB2 Prescaler                         | 2
  *        APB1 Prescaler                         | 2
  *        HSI Frequency(Hz)                      | 8000000
  *        PLLMUL                                 | 12
  *        PREDIV                                 | 2
  *        USB Clock                              | DISABLE
  *        Flash Latency(WS)                      | 2
  *        Prefetch Buffer                        | OFF
  * @param  None
  * @retval None
  */
static void SystemClock_Config(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_PeriphCLKInitTypeDef RCC_Peri = {0};
  /* Enable HSI Oscillator and activate PLL with HSI as source */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /* Set the I2C clock */
  RCC_Peri.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
  RCC_Peri.I2c1ClockSelection = RCC_I2C1CLKSOURCE_SYSCLK;
  HAL_RCCEx_PeriphCLKConfig(&RCC_Peri);
  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
     clocks dividers */
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}
/**
  * @brief EXTI line detection callbacks
  * @param GPIO_Pin: Specifies the pins connected EXTI line
  * @retval None
  */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  buttonpress = 1U;
}
#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* Infinite loop */
  Error_Handler();
}
#endif

  

main.rar

6.64 KB

使用特权

评论回复
地板
hzocce|  楼主 | 2020-4-4 19:40 | 只看该作者
本帖最后由 hzocce 于 2020-4-4 19:41 编辑

还是说,只有有普通的I2C初始化就好了?



I2C_Init();
PMBus_process();

PMBus就是I2C,然后只是数据命令而已???

如果是普通的I2C,那为什么规格书上面说支持SMBus/PMBus呢?
而又的规格书上面又未提及支持PMBus

使用特权

评论回复
5
hzocce|  楼主 | 2020-4-5 14:44 | 只看该作者
??

使用特权

评论回复
6
hzocce|  楼主 | 2020-4-6 14:52 | 只看该作者
求关注~~

使用特权

评论回复
7
hzocce|  楼主 | 2020-4-7 20:50 | 只看该作者
浮起来~~

使用特权

评论回复
8
hzocce|  楼主 | 2020-4-9 10:25 | 只看该作者
深度求关注~~

使用特权

评论回复
9
hzocce|  楼主 | 2020-4-10 15:13 | 只看该作者
CubeMX中的I2C 3中工作方式指示的是什么?

STM32_I2C工作方式.PNG (439.99 KB )

STM32_I2C工作方式.PNG

使用特权

评论回复
10
hzocce|  楼主 | 2020-4-22 17:10 | 只看该作者
别沉了

使用特权

评论回复
11
g753388438| | 2020-4-22 21:33 | 只看该作者
我之前用过smbus,只用了几条指令。iic,smbus,pmbus其实都是一样的,只是smbus和pmbus的时序更加严格一些。smbus和pmbus大多都是用在电源上的。
我用的CPU是stm32f429IG和stm32f103c8(调试使用),控制数字电源模块。当时选定了3个厂家的电源模块,其中两个厂家模块我是用IO口模拟的SMBUS,可以正常使用,有一个厂家无法正常使用,想了很多的办法,调整上拉电阻,修改延时等等,最后还是不行,试着用了一下stm32的硬件IIC,可以正常控制。
另外就是stm32有部分的单片机是由smbus模式(就是IIC的一种形式),大部分是不支持的。
之前调试的时候,看过stm32的smbus库,感觉就是根据行业标准,直接把寄存器都定义好,在IIC的基础上加了一个封装,方便直接调用。(类似于stm32的寄存器版和库函数版)

使用特权

评论回复
12
hzocce|  楼主 | 2020-4-23 21:09 | 只看该作者
g753388438 发表于 2020-4-22 21:33
我之前用过smbus,只用了几条指令。iic,smbus,pmbus其实都是一样的,只是smbus和pmbus的时序更加严格一些 ...

I2C没有校验部分。

I2C 就是:发器件地址>>发内部寄存器地址>>发数据>>发数据>>发数据>>发数据>>发数据。。。

可是SMBUS,PMBUS 多了校验字节。

如果用于SMBus,PMBus 在CubeMX是不是需要配置成SMBUS工作方式? 那cube界面上面有3个方式可选。
你是用那个工作方式实现的SMbus控制?

STM32作为从机??

能给些文件研究学习些么?

使用特权

评论回复
13
g753388438| | 2020-4-24 08:48 | 只看该作者
hzocce 发表于 2020-4-23 21:09
I2C没有校验部分。

I2C 就是:发器件地址>>发内部寄存器地址>>发数据>>发数据>>发数据>>发数据>>发数据 ...

我这边用的STM32是作为主机的。
cubemx配置的是IIC,没有选择SMBUS.使用的时候就是把库函数简单的封装了一下。
我看的资料主要有两个,一个是smbus的标准手册SMBus_3_0_20141220,另外的就是模块的使用手册


static uint8_t SM_Read(uint8_t dev,uint8_t addr)
{
        uint8_t dat;
        vTaskDelay(2);
        if(dev<2)
        {
                MX_I2C2_Init();
                HAL_I2C_Mem_Read(&hi2c2,DEVICE_ADD[dev],addr,I2C_MEMADD_SIZE_8BIT,(uint8_t *)&dat,1,1000);       
        }                       
       
       
        return dat;
}

static uint16_t SM_ReadN(uint8_t dev,uint8_t addr)
{
        uint16_t dat;
        vTaskDelay(2);
        if(dev<2)
        {
                MX_I2C2_Init();
                HAL_I2C_Mem_Read(&hi2c2,DEVICE_ADD[dev],addr,I2C_MEMADD_SIZE_8BIT,(uint8_t *)&dat,2,1000);       
        }
       
        return dat;
}
       
static uint32_t SM_ReadN2(uint8_t dev,uint8_t addr)
{
        uint64_t da2t;
                vTaskDelay(2);
        if(dev<2)
        {
                MX_I2C2_Init();
                HAL_I2C_Mem_Read(&hi2c2,DEVICE_ADD[dev],addr,I2C_MEMADD_SIZE_8BIT,(uint8_t *)&da2t,5,1000);       
        }
       
        return ((da2t>>8)&0xFFFFFFFF);
}

static void SM_Write(uint8_t dev,uint8_t addr,uint8_t dat)
{
                vTaskDelay(10);
        if(dev<2)
        {
                MX_I2C2_Init();
                HAL_I2C_Mem_Write(&hi2c2,DEVICE_ADD[dev],addr,I2C_MEMADD_SIZE_8BIT,(uint8_t *)&dat,1,1000);       
        }
       
}

static void SM_WriteN(uint8_t dev,uint8_t addr,uint16_t buf)
{
                vTaskDelay(10);
        if(dev<2)
        {
                MX_I2C2_Init();
                HAL_I2C_Mem_Write(&hi2c2,DEVICE_ADD[dev],addr,I2C_MEMADD_SIZE_8BIT,(uint8_t *)&buf,2,1000);       
        }
       
       
}
static void SM_WriteN2(uint8_t dev,uint8_t addr,uint32_t buf)
{
        uint64_t dat;
        dat=buf;
        dat<<=8;
        dat|=0x04;
        vTaskDelay(10);
        if(dev<2)
                {
                MX_I2C2_Init();
        HAL_I2C_Mem_Write(&hi2c2,DEVICE_ADD[dev],addr,I2C_MEMADD_SIZE_8BIT,(uint8_t *)&dat,5,1000);       
        }
       
}

使用特权

评论回复
14
g753388438| | 2020-4-24 08:51 | 只看该作者
这个是我用IO口模拟smbus。
部分厂家的模块是可以正常用的,部分厂家的不能用。你自己参考一下

#define DL_L         500         // 长延时
#define DL_S         20         // 短延时
#define ADDDD 82 //41//0X52
#define WRITE 0x00         // SMBus 写命令
#define READ         0x01         // SMBus 读命令


static void BRD_IO_IN(void)
{
        GPIO_InitTypeDef GPIO_InitStruct = {0};

        GPIO_InitStruct.Pin = G_BRD_DATA_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(G_BRD_DATA_GPIO_Port, &GPIO_InitStruct);
}
static void BRD_IO_OUT(void)
{
        GPIO_InitTypeDef GPIO_InitStruct = {0};
       
        GPIO_InitStruct.Pin = G_BRD_DATA_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(G_BRD_DATA_GPIO_Port, &GPIO_InitStruct);
}

void Delay_us(uint16_t time)
{   
  uint16_t i=0;  
        while(time--)
        {
//                i=5;
                i=41;
                while(i--);
        }
}



/*********************************************************************************************************
** 函数名称: I2C_Start
** 功能描述: 启动
** 输   入: 无
** 输          出: 无
********************************************************************************************************/
void I2C_Start(void)
{
        BRD_IO_OUT();// 把 SDA SCL 引脚配置成输出模式
        Delay_us(DL_L);
        I2C_SDA_H;
        Delay_us(DL_S);
        I2C_SCL_H;
        Delay_us(DL_L);
        I2C_SDA_L;
        Delay_us(DL_L);
//        I2C_SCL_L;
//        Delay_us(DL_L);
}

/*********************************************************************************************************
** 函数名称: I2C_Stop
** 功能描述: 停止
** 输   入: 无
** 输          出: 无
********************************************************************************************************/
void  I2C_Stop(void)
{
        BRD_IO_OUT();// 把 SDA SCL 引脚配置成输出模式
        Delay_us(DL_L);
        I2C_SDA_L;
        Delay_us(DL_S);
        I2C_SCL_H;
        Delay_us(DL_L);
        I2C_SDA_H;
        Delay_us(DL_L);
}

/*********************************************************************************************************
** 函数名称: I2C_Ack
** 功能描述: 需应答
** 输   入: 无
** 输          出: 0-正常;1-故障
********************************************************************************************************/
uint8_t I2C_Ack(void)
{
        uint8_t i;
        uint8_t bit_ack;
        BRD_IO_IN();        // 把 SDA 引脚配置成输入模式
        I2C_SCL_H;
        Delay_us(DL_L);
        for(i=0;i<200;i++)
        {
                bit_ack = READ_SDA;
//                Delay_us(DL_S);
                if(bit_ack == 0)
                        break;
        }
        I2C_SCL_L;
        Delay_us(DL_L);
        return bit_ack;
}

/*********************************************************************************************************
** 函数名称: I2C_Nack
** 功能描述: 无需应答
** 输   入: 无
** 输          出: 无
********************************************************************************************************/
void I2C_Nack(void)
{
        BRD_IO_OUT();// 把 SDA SCL 引脚配置成输出模式
        Delay_us(DL_L);
        I2C_SDA_H;
        Delay_us(DL_L);
        I2C_SCL_H;
        Delay_us(DL_L);
        I2C_SCL_L;
        Delay_us(DL_L);

}
/*********************************************************************************************************
** 函数名称: I2C_Cack
** 功能描述: 产生应答
** 输   入: 无
** 输          出: 无
********************************************************************************************************/

void I2C_Cack(void)
{
        BRD_IO_OUT();// 把 SDA SCL 引脚配置成输出模式
        Delay_us(DL_L);
        I2C_SDA_L;
        Delay_us(DL_L);
        I2C_SCL_H;
        Delay_us(DL_L);
        I2C_SCL_L;
        Delay_us(DL_L);
}
/*********************************************************************************************************
** 函数名称: I2C_write_data
** 功能描述: 单字节写函数
** 输   入: 待写数据 dat
** 输          出: 无
********************************************************************************************************/
void I2C_write_data( uint8_t dat)
{
        uint8_t i = 8;
        BRD_IO_OUT();// 把 SDA SCL 引脚配置成输出模式
        while( i-- )
        {
                Delay_us(DL_L);
                I2C_SCL_L;
                Delay_us(DL_L);
                if ( dat &0x80 )
                        I2C_SDA_H;
                else
                        I2C_SDA_L;
                Delay_us(DL_L);
                I2C_SCL_H;
                dat = dat << 1;
        }
        Delay_us(DL_L);
        I2C_SCL_L;
        Delay_us(DL_S);
        I2C_SDA_H;
        Delay_us(DL_L);
}

void I2C_write_data1( uint8_t dat)
{
        uint8_t i = 8;
        BRD_IO_OUT();// 把 SDA SCL 引脚配置成输出模式
        while( i-- )
        {
                Delay_us(DL_L);
                I2C_SCL_L;
                Delay_us(DL_L);
                if ( dat &0x80 )
                        I2C_SDA_H;
                else
                        I2C_SDA_L;
                Delay_us(DL_L);
                I2C_SCL_H;
                dat = dat << 1;
        }
        Delay_us(DL_L);
        I2C_SCL_L;
//        Delay_us(DL_S);
//        I2C_SDA_H;
//        Delay_us(DL_L);
}

/*********************************************************************************************************
** 函数名称: I2C_read_data
** 功能描述: 单字节读函数
** 输   入: 无
** 输          出: 数据
********************************************************************************************************/
uint8_t I2C_read_data(void)
{
        uint8_t i = 8;
        uint8_t dat = 0;
        BRD_IO_OUT();// 把 SDA SCL 引脚配置成输出模式
        Delay_us(DL_L);
        I2C_SCL_L;
        Delay_us(DL_S);
        I2C_SDA_H;
        Delay_us(DL_L);
        BRD_IO_IN();        // 把 SDA 引脚配置成输入模式
        Delay_us(DL_L);
        while(i--)
        {
                dat = dat << 1;
                I2C_SCL_H;
                Delay_us(DL_L);
                if(READ_SDA)
                        dat++;
                I2C_SCL_L;
                Delay_us(DL_L);
        }
        return dat;
}

/*********************************************************************************************************
** 函数名称: SM_Write
** 功能描述: SMBus 单字节写函数,向给定存储器地址写一个字节
** 输   入: 待写数据 dat
**                         待写存储器地址(2字节)addr
**                         待写EEPROM芯片的器件地址0xA0
** 输          出: 无
********************************************************************************************************/
void SM_Write(uint8_t addr,uint8_t dat)
{
        I2C_Start();
        I2C_write_data(ADDDD | WRITE);        // 片选 + WRITE
        I2C_Ack();
        I2C_write_data(addr);                         // 地址
        I2C_Ack();
        I2C_write_data(dat);
        I2C_Nack();
        I2C_Stop();
}

/*********************************************************************************************************
** 函数名称: SM_Write
** 功能描述: SMBus 多字节写函数,从向给定存储器地址开始写多个字节
** 输   入: 待写存储器起始地址(1字节)addr
**                         待写数据 I2C_Buf
**                         待写数据个数 count
** 输          出: 无
********************************************************************************************************/
void SM_WriteN1(uint8_t addr,uint16_t buf)
{
        I2C_Start();
        I2C_write_data(ADDDD | WRITE);        // 片选 + WRITE
        I2C_Ack();
        I2C_write_data(addr);                         // 地址
        I2C_Ack();
        I2C_write_data(buf&0XFF);
        I2C_Ack();
        //I2C_Nack();
        I2C_write_data((buf>>8)&0XFF);
        I2C_Ack();
        I2C_Stop();
       

}

/*********************************************************************************************************
** 函数名称: SM_Read
** 功能描述: SMBus 字节读函数,从给定存储器地址读一个字节
** 输   入: 待读存储器地址(1字节)addr
**                         待读EEPROM芯片的器件地址 0xA0
** 输          出: 数据
********************************************************************************************************/
uint8_t SM_Read(uint8_t addr)
{
        uint8_t dat;
        I2C_Start();
        I2C_write_data(ADDDD | WRITE);        // 片选 + WRITE
        I2C_Ack();
        I2C_write_data(addr);                                         // 地址
        I2C_Ack();
        I2C_Start();
        I2C_write_data(ADDDD | READ);                // 片选 + READ
        I2C_Ack();
        dat = I2C_read_data();
        I2C_Ack();
        I2C_Stop();

        return dat;
}


/*********************************************************************************************************
** 函数名称: SM_ReadN
** 功能描述: SMBus 多字节读函数,从给定存储器地址起读多个字节
** 输   入: 待读存储器起始地址(2字节)addr
**                         待读数据个数 count
** 输          出: 数据
********************************************************************************************************/
uint16_t SM_ReadN(uint8_t addr)
{
        uint16_t dat;
        uint8_t ddda;
        I2C_Start();
        I2C_write_data(ADDDD | WRITE);        // 片选 + WRITE
        I2C_Ack();
        I2C_write_data(addr);                                         // 地址
        I2C_Ack();       
        I2C_Start();
        I2C_write_data(ADDDD | READ);                // 片选 + READ
        I2C_Ack();
        ddda = I2C_read_data();
        I2C_Cack();
        dat=ddda;
        ddda = I2C_read_data();
        I2C_Cack();
        dat+=(ddda<<8);
        I2C_Stop();
        return dat;
}


使用特权

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

本版积分规则

127

主题

561

帖子

4

粉丝