Haizangwang 发表于 2025-8-11 14:03

国民技术N32G003实现PMBus从机及使用STM32F103模拟I2C主机访问从机

最近有项目用到PMBus通信,经过一段时间的调试已满足客户的要求,先将代码贴上,再解释下基本的思路。

从机程序:

PMBusSlave.c

/*******************************************************************************
*
* PMBusSlave.c -   This program is a software implementation of PMBus over I2C,
*                  with the N32G031C8 device acting as the PMBus slave.
*
* Copyright (c) 2025 ASTO Incorporated.All rights reserved.
* Software License Agreement
*
* ASTO is supplying this software for use solely and
* exclusively on ASTO's microcontroller products. The software is owned by
* ASTO and/or its suppliers, and is protected under applicable copyright
* laws. You may not combine this software with "viral" open-source
* software in order to form a larger program.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
* NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
* NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
* CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
* DAMAGES, FOR ANY REASON WHATSOEVER.
*
******************************************************************************/
#include "PMBusSlave.h"
#include "PMBus.h"
#include "n32g003_i2c.h"
#include "n32g003.h"
#include <string.h>

volatile struct STATUS_REGS StatusRegs;

//This array contains all of the PMBus command bytes (according to the PMBus spec)
//indexed by the command indeces defined in PMBus.h
/////////////// The last three commmands are the new added ones/////////////////
/*
const unsigned char PMBus_Commands = {
    0x00, // dummy byte
    0x19, 0x78, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x98,
    0x79, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92,
    0x93, 0x94, 0x95, 0x96, 0x97, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
    0xA7, 0xA8, 0xA9, 0x13, 0x14, 0x17, 0x18, 0x3, 0x11, 0x12, 0x15, 0x16,
    0x0, 0x1, 0x2, 0x4, 0x10, 0x20, 0x3A, 0x3D, 0x41, 0x45, 0x47, 0x49,
    0x4C, 0x50, 0x54, 0x56, 0x5A, 0x5C, 0x63, 0x69, 0x21, 0x22, 0x23, 0x24,
    0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x31, 0x32, 0x33, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3B, 0x3C, 0x3E, 0x3F, 0x40, 0x42, 0x43, 0x44, 0x46, 0x48,
    0x4A, 0x4B, 0x4F, 0x51, 0x52, 0x53, 0x55, 0x57, 0x58, 0x59, 0x5B, 0x5D,
    0x5E, 0x5F, 0x60, 0x61, 0x62, 0x64, 0x65, 0x66, 0x68, 0x6A, 0x6B,
    0x99, 0x9A, 0x9B,
};
*/

//The CRC8 table needed to accelarate the calculation of CRC8
/*
static const uint8_t crc8_table = {
    0x00,0x07,0x0E,0x09,0x1C,0x1B,0x12,0x15,0x38,0x3F,0x36,0x31,0x24,0x23,0x2A,0x2D,
    0x70,0x77,0x7E,0x79,0x6C,0x6B,0x62,0x65,0x48,0x4F,0x46,0x41,0x54,0x53,0x5A,0x5D,
    0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD,
    0x90,0x97,0x9E,0x99,0x8C,0x8B,0x82,0x85,0xA8,0xAF,0xA6,0xA1,0xB4,0xB3,0xBA,0xBD,
    0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,0xD5,0xD2,0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA,
    0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,0x8F,0x88,0x81,0x86,0x93,0x94,0x9D,0x9A,
    0x27,0x20,0x29,0x2E,0x3B,0x3C,0x35,0x32,0x1F,0x18,0x11,0x16,0x03,0x04,0x0D,0x0A,
    0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A,
    0x89,0x8E,0x87,0x80,0x95,0x92,0x9B,0x9C,0xB1,0xB6,0xBF,0xB8,0xAD,0xAA,0xA3,0xA4,
    0xD9,0xDE,0xD7,0xD0,0xC5,0xC2,0xCB,0xCC,0xE1,0xE6,0xEF,0xE8,0xFD,0xFA,0xF3,0xF4,
    0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,0x51,0x56,0x5F,0x58,0x4D,0x4A,0x43,0x44,
    0x19,0x1E,0x17,0x10,0x05,0x02,0x0B,0x0C,0x21,0x26,0x2F,0x28,0x3D,0x3A,0x33,0x34,
    0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63,
    0x3E,0x39,0x30,0x37,0x22,0x25,0x2C,0x2B,0x06,0x01,0x08,0x0F,0x1A,0x1D,0x14,0x13,
    0xAE,0xA9,0xA0,0xA7,0xB2,0xB5,0xBC,0xBB,0x96,0x91,0x98,0x9F,0x8A,0x8D,0x84,0x83,
    0xDE,0xD9,0xD0,0xD7,0xC2,0xC5,0xCC,0xCB,0xE6,0xE1,0xE8,0xEF,0xFA,0xFD,0xF4,0xF3
};*/

//The CRC8 table needed to accelerate the calculation of CRC8
static const uint8_t crc8_table = {
        0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
        0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
        0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
        0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
        0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
        0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
        0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
        0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
        0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
        0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
        0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
        0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
        0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
        0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
        0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
        0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};

uint8_t CRC8_Calculate(const uint8_t *data, uint16_t length) {
    uint8_t crc = 0x00; //CRC8_INIT;
    while (length--) {
      crc = crc8_table;
    }
    return crc;
}

const uint8_t _MFR_ID = {0x44, 0x53, 0x50, 0x4F, 0x57, 0x45, 0x52, 0xF7};// "DSPOWER";
const uint8_t _MFR_MODEL = {0x4E, 0x50, 0x36, 0x30, 0x2D, 0x32, 0x32, 0x30, 0x57, 0x31, 0x31, 0x4D, 0x2D, 0x31, 0x43, 0xDD};//"NP60-220W11M-1C";
const uint8_t _MFR_REVISION = {0x31, 0x2E, 0x30, 0xFD};//"1.0";

static unsigned char slave_address;

uint8_t PMBusSlave_ReceiveBuffer = { 0, 0, 0, 0 };
uint8_t PMBusSlave_TransmitBuffer = { 0, 0, 0, 0 };
uint16_t PMBusSlave_Index = 0;
uint16_t PMBusSlave_DummyCommand = 0;
volatile _iicSlave iicSlave;   // useIT for I2C
uint8_t PMBus_timeout = 0;
unsigned char block_len = 0;//used to save the length of the block data
unsigned char block_loop = 0;
static __IO uint32_t I2CTimeout;
uint16_t uI2C_error_cnt = 0;

///////////external variables///////////
extern float pin;//defined in main.c
extern float p12_out;
extern float vin;
extern float iin;
//extern float v54;
//extern float i54;
extern float v12_out;
extern float i12_out;
extern float i12_warn_out;

extern signed char TEMP_OUT;//defined in adc.c
//extern uint16_t IOUT;
//extern uint16_t VOUT;

////////////USER CODE////////////
//Example variables, should be changed by user to be application specific
#warning    "User should declare application specific PMBus registers or variables."
//initial values
unsigned char Temperature = 0x12;   //STATUS_TEMPERATURE command (R byte)
unsigned char Default_Code = 0;   //STORE_DEFAULT_CODE command (W byte)
unsigned char Operation = 0x34;   //OPERATION command (R/W byte)
unsigned int Status_Word = 0x5678;//STATUS_WORD command (R word)
unsigned char Status_Byte = 0x00;   //STATUS_BYTE command (R byte)
unsigned char Status_Cml = 0x00;    //STATUS_CML command (R byte)
unsigned int Vout_Command = 0x90AB; //VOUT_COMMAND command (R/W word)
unsigned int ot_warn_limit = 0xEA80;//80C, linear11 format

////////////END USER CODE////////

//
// Defines
//
#define MAX_BUFFER_SIZE   0x10
#define I2C_NUMBYTES      0x10

void I2C_ResetBusy(void);
void CommTimeOut_CallBack(ErrCode_t errcode);

//
// Globals
//
unsigned char PMBusSlave_Command = 0, PMBusSlave_CommandType = 0;

/**
*\*\name    TIM6_IRQHandler.
*\*\fun   This function handles TIM6 global interrupt request.
*\*\param   none
*\*\returnnone
**/
void TIM6_IRQHandler(void)
{
    if (TIM_Interrupt_Status_Get(TIM6, TIM_INT_UPDATE) != RESET)
    {
      TIM_Interrupt_Status_Clear(TIM6, TIM_INT_UPDATE);
      if (++PMBus_timeout >= PMBUS_TIME_OUT)
      {
            PMBus_timeout = PMBUS_TIME_OUT;
      }
    }
}

//Start TIM6
void StartCpuTimer0(void)
{
    TIM6->CNT = 0;
    TIM6->CTRL1 |= TIM_CTRL1_CNTEN;
}

//Disable TIM6 and clear the counter
void StopCpuTimer0(void)
{
    TIM6->CNT = 0;
    TIM6->CTRL1 &= (uint32_t)(~((uint32_t)TIM_CTRL1_CNTEN));//disable timer
    PMBus_timeout = 0;
}

//Enable the TIM6 auto reload period, that is, the shadow register
void ReloadCpuTimer0(void)
{
    TIM6->CTRL1 |= TIM_CTRL1_ARPEN;
}

/*
0x20, use the default format
mode: 000
parameter: -9, 10111
*/
void PMBus_Cmd_Vout_Mode(void)
{
    /*
    uint8_t temp, u8VoutMode;
    //temp = pmb->rxBuffer;
    VoutMode.bit.Parameter = -9;
    VoutMode.bit.Mode = 0;
    */

    //pmb->txBuffer = VoutMode.all;      // Sends low byte first
    //pmb->txLength = 1;
    // PEC calculated
    //pmb->state = PMB_STATE_WRITE_READ_REQUESTED;
}

/**
@name: PMBus_Read_Vin
@description: 0x88: read the input voltage, convert the real voltage to Linear11 format
@input: vin, the actual input voltage(V)
@output: none
**/
void PMBus_Read_Vin(float vin)
{
    // LINEAR11 is used, for example as the following:
    // X = mantissa * 2^N

    int16_t exponent, mantissa, i16var;

    exponent = -1;//EXPI to make sure the accuracy

    //mantissa = (int16_t)(iout & 0x7FF);   // Get the mantissa in the LINEAR11
    mantissa = (int16_t)(((int16_t)(vin * 2)) & 0x7FF);// Get the mantissa in the LINEAR11

    i16var = (exponent << 11) + mantissa;       // Test OK

    //PMBusSlave_TransmitBuffer = i16var % 256;          // Sends upper byte first
    PMBusSlave_TransmitBuffer = i16var & 0x00FF;
    PMBusSlave_TransmitBuffer = i16var >> 8;         // And then sends lower byte

    //////////////////////////Linear16 is NOT used//////////////////////////////////////////////////////////
    //    //int16_t exponent;
    //    //int16_t i16var;
    //    int16_t mantissa;

    //    //exponent = -6;//0x1A, mode = 000, linear format, N = -6

    //    mantissa = (int16_t)(((int16_t)(vin * 64)) & 0xFFFF);// Get the mantissa in the LINEAR16, 0xFFFF

    //    PMBusSlave_TransmitBuffer = mantissa % 256;      // Sends low byte first
    //    PMBusSlave_TransmitBuffer = mantissa >> 8;       // And then sends high byte
    ////////////////////////////////////////////////////////////////////////////////////////////////////////
}

/**
@name: PMBus_Read_Iin
@description: 0x89, read the input current, convert the real voltage to Linear11 format
@input: iin, the actual input current(A)
@output: none
**/
void PMBus_Read_Iin(float iin)
{
    // LINEAR11 is used, for example as the following:
    // X = mantissa * 2^N

    int16_t exponent, mantissa, i16var;

    exponent = -7;//EXPI to make sure the accuracy // 0.0078125 A

    //mantissa = (int16_t)(iout & 0x7FF);   // Get the mantissa in the LINEAR11
    mantissa = (int16_t)(((int16_t)(iin * 128)) & 0x7FF);// Get the mantissa in the LINEAR11

    i16var = (exponent << 11) + mantissa;       // Test OK

    //PMBusSlave_TransmitBuffer = i16var % 256;          // Sends low byte first
    PMBusSlave_TransmitBuffer = i16var & 0x00FF;
    PMBusSlave_TransmitBuffer = i16var >> 8;         // And then sends high byte
}

/**
@name: PMBus_Read_Pin
@description: 0x97, read the input power, convert the real voltage to Linear11 format
@input: pin, the actual input power(W)
@output: none
**/
void PMBus_Read_Pin(float pin)
{
    // LINEAR11 is used, for example as the following:
    // X = mantissa * 2^N

    int16_t exponent, mantissa, i16var;

    exponent = -2;//EXPI to make sure the accuracy // 0.25 W

    //mantissa = (int16_t)(iout & 0x7FF);   // Get the mantissa in the LINEAR11
    mantissa = (int16_t)(((int16_t)(pin * 4)) & 0x7FF);// Get the mantissa in the LINEAR11

    i16var = (exponent << 11) + mantissa;       // Test OK

    //PMBusSlave_TransmitBuffer = i16var % 256;      // Sends low byte first
    PMBusSlave_TransmitBuffer = i16var & 0x00FF;
    PMBusSlave_TransmitBuffer = i16var >> 8;       // And then sends high byte
}

/**
@name: PMBus_Read_Pout
@description: 0x96, read the output power, convert the real voltage to Linear11 format
@input: pin, the actual output power(W)
@output: none
**/
void PMBus_Read_Pout(float pout)
{
    // LINEAR11 is used, for example as the following:
    // X = mantissa * 2^N

    int16_t exponent, mantissa, i16var;

    exponent = -2;//EXPI to make sure the accuracy // 0.25 W

    //mantissa = (int16_t)(iout & 0x7FF);   // Get the mantissa in the LINEAR11
    mantissa = (int16_t)(((int16_t)(pout * 4)) & 0x7FF);// Get the mantissa in the LINEAR11

    i16var = (exponent << 11) + mantissa;       // Test OK

    //PMBusSlave_TransmitBuffer = i16var % 256;      // Sends low byte first
    PMBusSlave_TransmitBuffer = i16var & 0x00FF;
    PMBusSlave_TransmitBuffer = i16var >> 8;       // And then sends high byte
}

/**
@name: PMBus_Read_V54_Vout
@description: 0x8B: read the output voltage of 12V, convert the real voltage to Linear16 format
@input: vout, the actual output voltage(V)
@output: none
**/
void PMBus_Read_V12_Vout(float vout)
{
    //int16_t exponent;
    //int16_t i16var;
    int16_t mantissa;

    //exponent = -9;//0x17, mode = 000, linear format, N = -9
    //exponent = -11;//0x15, mode = 000, linear format, N = -11
    mantissa = (int16_t)(((int16_t)(vout * 512)) & 0xFFFF);// Get the mantissa in the LINEAR16, 0xFFFF

    //PMBusSlave_TransmitBuffer = mantissa % 256;      // Sends low byte first
    PMBusSlave_TransmitBuffer = mantissa & 0x00FF;
    PMBusSlave_TransmitBuffer = mantissa >> 8;   // And then sends high byte
}

/**
@name: PMBus_Read_V54_Vout
@description: 0x8B: read the output voltage of 54V, convert the real voltage to Linear16 format
@input: vout, the actual output voltage(V)
@output: none
**/
void PMBus_Read_V54_Vout(float vout)
{
    // LINEAR16 is used, for example as the following:
    // X = mantissa * 2^N

    //int16_t exponent;
    //int16_t i16var;
    int16_t mantissa;

    //exponent = -9;//0x17, mode = 000, linear format, N = -9

    mantissa = (int16_t)(((int16_t)(vout * 512)) & 0xFFFF);// Get the mantissa in the LINEAR16, 0xFFFF

    //i16var = (exponent << 16) + mantissa; // Test OK
    //i16var = mantissa; // Test OK

    //the result
    //PMBusSlave_TransmitBuffer = mantissa % 256;      // Sends low byte first
    PMBusSlave_TransmitBuffer = mantissa & 0x00FF;
    PMBusSlave_TransmitBuffer = mantissa >> 8;       // And then sends high byte
}

/**
@name: PMBus_Read_V54_Iout
@description: 0x8C, read the output current of 12V, 54V: 9A, 12V: 16.8A
@input: iout, the actual output current(A)
@output: none
**/
void PMBus_Read_V54_Iout(float iout)
{
    // LINEAR11 is used, for example as the following:
    // X = mantissa * 2^N

    int16_t exponent, mantissa, i16var;

    exponent = -7;//EXPI to make sure the accuracy // 0.0078125 A

    //mantissa = (int16_t)(iout & 0x7FF);   // Get the mantissa in the LINEAR11
    mantissa = (int16_t)(((int16_t)(iout * 128)) & 0x7FF);// Get the mantissa in the LINEAR11

    i16var = (exponent << 11) + mantissa;       // Test OK

    //PMBusSlave_TransmitBuffer = i16var % 256;      // Sends low byte first
    PMBusSlave_TransmitBuffer = i16var & 0x00FF;
    PMBusSlave_TransmitBuffer = i16var >> 8;         // And then sends high byte
}

/**
@name: PMBus_Read_V12_Iout
@description: 0x8C, read the output current of 12V, 54V: 9A, 12V: 16.8A
@input: iout, the actual output current(A)
@output: none
**/
void PMBus_Read_V12_Iout(float iout)
{
    // LINEAR11 is used, for example as the following:
    // X = mantissa * 2^N

    int16_t exponent, mantissa, i16var;

    exponent = -7;//EXPI to make sure the accuracy // 0.0078125 A

    //mantissa = (int16_t)(iout & 0x7FF);   // Get the mantissa in the LINEAR11
    mantissa = (int16_t)(((int16_t)(iout * 128)) & 0x7FF);// Get the mantissa in the LINEAR11

    i16var = (exponent << 11) + mantissa;       // Test OK

    //PMBusSlave_TransmitBuffer = i16var % 256;      // Sends low byte first
    PMBusSlave_TransmitBuffer = i16var & 0x00FF;
    PMBusSlave_TransmitBuffer = i16var >> 8;       // And then sends high byte
}

/**
@name: PMBus_Read_Sec_Temp
@description: 0x8F, read the secondary side temperature
@input: tmp, the actual temperature(C)
@output: none
**/
void PMBus_Read_Sec_Temp(signed char tmp)
{
    // LINEAR11 is used, for example as the following:
    // X = mantissa * 2^N

    int16_t exponent, mantissa, i16var;

    if (tmp >= 0)
    {
      exponent = -3;//11100,EXPI to make sure the accuracy
    }
    else
    {
      exponent = -4;//11010
    }

    //mantissa = (int16_t)(iout & 0x7FF);   // Get the mantissa in the LINEAR11
    if (tmp >= 0)
    {
      mantissa = (int16_t)(((int16_t)(tmp * 8)) & 0x7FF);// Get the mantissa in the LINEAR11
    }
    else
    {
      //for example, -35C, mantissa = -35*16 = -560 & 0x7FF = 101 1101 0000
      //since the exponent = -4, that is, 11100, so the final result is below:
      //11100 101 1101 0000, hex format: 0xE5D0
      mantissa = (int16_t)(((int16_t)(tmp * 16)) & 0x7FF);
    }

    i16var = (exponent << 11) + mantissa;       // Test OK

    //PMBusSlave_TransmitBuffer = i16var % 256;      // Sends low byte first
    PMBusSlave_TransmitBuffer = i16var & 0x00FF;
    PMBusSlave_TransmitBuffer = i16var >> 8;       // And then sends high byte
}

/**
@name: PMBus_Read_V12_Iout_OC_Warn
@description: 0x4A, read the 12V output over current warning threshold value
@input: iout, the actual output current(A)
@output: none
**/
void PMBus_Read_V12_Iout_OC_Warn(float iout)
{
    // LINEAR11 is used, for example as the following:
    // X = mantissa * 2^N

    int16_t exponent, mantissa, i16var;

    exponent = -7;//EXPI to make sure the accuracy // 0.0078125 A

    //mantissa = (int16_t)(iout & 0x7FF);   // Get the mantissa in the LINEAR11
    mantissa = (int16_t)(((int16_t)(iout * 128)) & 0x7FF);// Get the mantissa in the LINEAR11

    i16var = (exponent << 11) + mantissa;       // Test OK

    //PMBusSlave_TransmitBuffer = i16var % 256;      // Sends low byte first
    PMBusSlave_TransmitBuffer = i16var & 0x00FF;
    PMBusSlave_TransmitBuffer = i16var >> 8;       // And then sends high byte
}

#if PEC
/*
// The slave reponds to the master(read) using PEC
tmp = (slave_addr << 1);   // slave address(write)
tmp = reg;                   // register
tmp = (slave_addr << 1) | 1; // slave address(read)
crc = _psu_crc8(0, tmp, 3);   // data
crc = _psu_crc8(crc, data, 2);// data
*/
/***************************************************************************//**
* @brief   Calculate the Packet Error Checking byte.
* @param   PMBusSlave_CRC Initial value.
* @param   PMBusSlave_Poly The polynomial to use for the calculation.
* @param   *PMBusSlave_PMsg Pointer to the bytes from the PMBus transaction.
* @param   PMBusSlave_MsgSize Number of bytes in the last transaction.
* @returnThe PEC byte.
******************************************************************************/
static unsigned short PMBusSlave_Crc8MakeBitwise(unsigned char PMBusSlave_CRC, unsigned char PMBusSlave_Poly, unsigned char* PMBusSlave_Pmsg, unsigned int PMBusSlave_MsgSize)
{
    unsigned int i, j, carry;
    unsigned char msg;

    PMBusSlave_CRC = *PMBusSlave_Pmsg++;            // first byte loaded in "crc"
    for (i = 0 ; i < PMBusSlave_MsgSize - 1 ; i ++)
    {
      msg = *PMBusSlave_Pmsg++;                   // next byte loaded in "msg"

      for (j = 0 ; j < 8 ; j++)
      {
            carry = PMBusSlave_CRC & 0x80;                        // check if MSB=1
            PMBusSlave_CRC = (PMBusSlave_CRC << 1) | (msg >> 7);    // Shift 1 bit of next byte into crc
            if (carry) PMBusSlave_CRC ^= PMBusSlave_Poly;         // If MSB = 1, perform XOR
            msg <<= 1;                                              // Shift left msg byte by 1
            msg &= 0x00FF;
      }
    }
    // The previous loop computes the CRC of the input bit stream. To this,
    // 8 trailing zeros are padded and the CRC of the resultant value is
    // computed. This gives the final CRC of the input bit stream.
    for (j = 0 ; j < 8 ; j++)
    {
      carry = PMBusSlave_CRC & 0x80;
      PMBusSlave_CRC <<= 1;
      if (carry) PMBusSlave_CRC ^= PMBusSlave_Poly;
    }

    PMBusSlave_CRC &= 0x00FF;   //We only want one byte (lower)

    return (PMBusSlave_CRC);
}

/***************************************************************************//**
* @brief   Calculate the Packet Error Checking byte.
* @param   data: the data buffer to be calculated.
* @param   data_len: data length of the buffer.
* @returnThe PEC byte.
******************************************************************************/
static unsigned char CRC8(unsigned char* data, int data_len) {
    unsigned char data_in;
    unsigned char i = 0;
    unsigned char crc = 0x00; //initial value
    unsigned char crc_poly = 0x07; //the polynomial
    while (data_len--) {
      data_in = *data++;
      crc = crc ^ data_in;
      for (i = 0; i < 8; i++) {
            if (crc & 0x80)//the bit 7 is 1 then we XOR the polynomial and then left shift one bit
            {
                crc = (crc << 1) ^ crc_poly;
            } else//directly left shift one bit
            {
                crc = crc << 1;
            }
      }
    }

    return (crc ^ 0x00);
}
#endif

/***************************************************************************//**
* @brief   Determine what type of PMBus command was received from the master.
*
*          The function also prepares data in the transmit buffer to send to
*          the master for supported READ and READ/WRITE commands. Users should modify
*          the code to implement their application's supported PMBus commands.
* @param   PMBusSlave_RxCommand The command byte received from the master.
* @returnCommand group of the received command.
******************************************************************************/
unsigned char PMBusSlave_DecodeCommand(unsigned char PMBusSlave_RxCommand)
{
    //unsigned char i = 0;
        unsigned char result = 0;

    ////////////USER CODE////////////
#warning "User should change code to implement their application's supported PMBus commands."
    switch (PMBusSlave_RxCommand)//should include all user supported commands
    {
      case CAPABILITY:
            PMBusSlave_TransmitBuffer = 0x80;//PEC check, 100KHz, no SMBAlert
                        result = 1;
            break;

      case STATUS_TEMPERATURE://0x7D
            PMBusSlave_TransmitBuffer = StatusRegs.StatusTemperature.all;
                        result = 1;
            break;

      case STORE_DEFAULT_CODE:
                        result = 0;
            break;

      case OPERATION:
            //PMBusSlave_TransmitBuffer = Operation;
                        result = 0;
            break;

      case OT_WARN_LIMIT://0x51
            PMBusSlave_TransmitBuffer = ot_warn_limit & 0x00FF;//upper byte first
            PMBusSlave_TransmitBuffer = ot_warn_limit >> 8;
                        result = 2;
            break;

      case STATUS_VOUT://0x7A
            PMBusSlave_TransmitBuffer = StatusRegs.StatusVout.all;
                        result = 1;
            break;

      case STATUS_IOUT://0x7B
            PMBusSlave_TransmitBuffer = StatusRegs.StatusIout.all;
                        result = 1;
            break;

      case STATUS_INPUT://0x7C
            PMBusSlave_TransmitBuffer = StatusRegs.StatusInput.all;
                        result = 1;
            break;

      case STATUS_BYTE://0x78
            PMBusSlave_TransmitBuffer = StatusRegs.StatusWord.all;
                        result = 1;
            break;

      case STATUS_CML://0x7E
            PMBusSlave_TransmitBuffer = StatusRegs.StatusCml.all;
                        result = 1;
            break;

      case STATUS_WORD://0x79
            PMBusSlave_TransmitBuffer = StatusRegs.StatusWord.all & 0x00FF; //upper byte first
            PMBusSlave_TransmitBuffer = StatusRegs.StatusWord.all >> 8;//lower byte
                        result = 2;
            break;

      case STATUS_MFR_SPECIFIC://0x80, input type
            PMBusSlave_TransmitBuffer = 0x01; //0x00: no input, 0x01: AC input
                        result = 1;
            break;

      case IOUT_OC_WARN_LIMIT://0x4A
            PMBus_Read_V12_Iout_OC_Warn(i12_warn_out);
                        result = 2;
            break;

      case VOUT_COMMAND://NOT supported yet
            PMBusSlave_TransmitBuffer = Vout_Command & 0x00FF;    //upper byte
            PMBusSlave_TransmitBuffer = Vout_Command >> 8;//lower byte
                        result = 2;
            break;

      case PAGE:
                        result = 0;
            break;

      //the implemented commands
      case READ_VIN://0x88
            PMBus_Read_Vin(vin);
                        result = 2;
            break;

      case READ_IIN://0x89
            PMBus_Read_Iin(iin);
                        result = 2;
            break;

      case READ_PIN://0x97
            PMBus_Read_Pin(pin);
                        result = 2;
            break;

      case READ_VOUT://0x8B
            PMBus_Read_V12_Vout(v12_out);
                        result = 2;
            break;

      case READ_IOUT://0x8C
            PMBus_Read_V12_Iout(i12_out);
                        result = 2;
            break;

      case READ_TEMPERATURE_3://secondary side temperature
            PMBus_Read_Sec_Temp(TEMP_OUT);
                        result = 2;
            break;

      case READ_POUT://0x96
            PMBus_Read_Pout(p12_out);
                        result = 2;
            break;

      case VOUT_MODE://read only here supported
            PMBusSlave_TransmitBuffer = 0x17;//0 00 10111, absolute value, Linear16 mode, step value: 2^-9
                        result = 1;
            break;

      ///////////////////Manufacture related commands//////////////////
      case MFR_ID:
                        /*
            block_len = strlen(_MFR_ID);
            for (block_loop = 0; block_loop < block_len; block_loop++)
            {
                PMBusSlave_TransmitBuffer = _MFR_ID;
            }*/
                        result = 7;
            break;

      case MFR_MODEL:
                        /*
            block_len = strlen(_MFR_MODEL);
            for (block_loop = 0; block_loop < block_len; block_loop++)
            {
                PMBusSlave_TransmitBuffer = _MFR_MODEL;
            }
                        */
                        result = 15;
            break;

      case MFR_REVISION:
                        /*
            block_len = strlen(_MFR_REVISION);
            for (block_loop = 0; block_loop < block_len; block_loop++)
            {
                PMBusSlave_TransmitBuffer = _MFR_REVISION;
            }*/
                        result = 3;
            break;

      case MFR_VIN_MIN://maximum input voltage
            PMBusSlave_TransmitBuffer = 0xD0;//lower byte, 90V, Linear11 format: 0xEAD0
            PMBusSlave_TransmitBuffer = 0xEA;
                        result = 2;
            break;

      case MFR_VIN_MAX://minimum input voltage
            PMBusSlave_TransmitBuffer = 0x10;//lower byte, 264V, Linear11 format: 0xFA10
            PMBusSlave_TransmitBuffer = 0xFA;
                        result = 2;
            break;

      case MFR_IIN_MAX://maximum input current
            PMBusSlave_TransmitBuffer = 0xC0;//lower byte, 1.5A, Linear11 format: 0xC8C0
            PMBusSlave_TransmitBuffer = 0xC8;
                        result = 2;
            break;

      case MFR_PIN_MAX://maximum input power
            PMBusSlave_TransmitBuffer = 0x30;//lower byte, 70W, Linear11 format: 0xEA30
            PMBusSlave_TransmitBuffer = 0xEA;
                        result = 2;
            break;

      case MFR_VOUT_MAX://maximum output voltage
            PMBusSlave_TransmitBuffer = 0xCC;//lower byte, 11.2V, Linear11 format: 0xD2CC
            PMBusSlave_TransmitBuffer = 0xD2;
                        result = 2;
            break;

      case MFR_VOUT_MIN://minimum output voltage
            PMBusSlave_TransmitBuffer = 0xB3;//lower byte, 10.8V, Linear11 format: 0xD2B3
            PMBusSlave_TransmitBuffer = 0xD2;
                        result = 2;
            break;

      case MFR_IOUT_MAX://maximum output current
            PMBusSlave_TransmitBuffer = 0xC0;//lower byte, 5.5A, Linear11 format: 0xCAC0
            PMBusSlave_TransmitBuffer = 0xCA;
                        result = 2;
            break;

      case MFR_POUT_MAX://maximum output power
            PMBusSlave_TransmitBuffer = 0xF0;//lower byte, 62W, Linear11 format: 0xE9F0
            PMBusSlave_TransmitBuffer = 0xE9;
                        result = 2;
            break;

      case MFR_TAMBIENT_MAX://maximum work temperature
            PMBusSlave_TransmitBuffer = 0x90;//lower byte, 50C, Linear11 format: 0xE990
            PMBusSlave_TransmitBuffer = 0xE9;
                        result = 2;
            break;

      case MFR_TAMBIENT_MIN://minimum work temperature
            PMBusSlave_TransmitBuffer = 0xC0;//lower byte, -20C, Linear11 format: 0xE6C0
            PMBusSlave_TransmitBuffer = 0xE6;
                        result = 2;
            break;

      case PMBUS_REVISION://PMBus version
            PMBusSlave_TransmitBuffer = 0x11;//V1.1
                        result = 1;
            break;

      case 0xAA://for test purpose
            result = 2;
            PMBusSlave_TransmitBuffer = uI2C_error_cnt & 0x00FF;
            PMBusSlave_TransmitBuffer = uI2C_error_cnt >> 8;
            break;

      default:
            //PMBusSlave_DummyCommand = 1;    //command not supported by this slave
                        result = 0;
            break;
    }
       
        return result;
}

/**
* =======================================================================================
* =======================================================================================
*/

void I2C_ResetBusy(void)
{
    I2C->CTRL1 |= 0x8000;// Reset Busy
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    I2C->CTRL1 &= ~0x8000;
}

/**
*\*\name    Delay.
*\*\fun   system ms delay function.
*\*\param   nCount
*\*\returnnone
**/
void Delay(uint32_t nCount)
{
    uint32_t tcnt;
    while (nCount--)
    {
      tcnt = 48000 / 5;//48M/48000 = 1000Hz, 1ms, one instruction needs about 5 system clocks
      while (tcnt--) {;} //instruction count per milli-second
    }
}

/**
*\*\name    I2C_NVIC_Config.
*\*\fun   NVIC Configuration.
*\*\param   none
*\*\returnresult
**/
void I2C_NVIC_Config(void)
{
    NVIC_InitType NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel         = I2C_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority   = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    NVIC_Initializes(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel         = I2C_ER_IRQn; /* test err */
    NVIC_InitStructure.NVIC_IRQChannelPriority   = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd      = DISABLE;
    NVIC_Initializes(&NVIC_InitStructure);

    /*
    NVIC_InitType NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel    = I2C2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 0x01;
    NVIC_Init(&NVIC_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel++;// I2Cx_ER_IRQn
    NVIC_Init(&NVIC_InitStructure);
    I2C_ConfigInt(I2C2, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, NVIC_InitStructure.NVIC_IRQChannelCmd);
    */
}

/**
*@name: I2C_Config
*@description: for the main communication with the upper machine
*@input: I2CSlave_OwnAddress: the slave device address
*@output: none
**/
void I2C_Config(uint16_t I2CSlave_OwnAddress)
{
    I2C_InitType I2C_InitStructure;
    GPIO_InitType i2c2_gpio;
    RCC_APB_Peripheral_Clock_Enable(RCC_APB_PERIPH_I2C);
    RCC_APB_Peripheral_Clock_Enable(RCC_APB_PERIPH_IOPA | RCC_APB_PERIPH_AFIO);

    GPIO_Structure_Initialize(&i2c2_gpio);
    //PA4 -- SCL; PA5 -- SDA
    i2c2_gpio.Pin      = I2C_SCL_PIN | I2C_SDA_PIN;
    i2c2_gpio.GPIO_Slew_Rate = GPIO_SLEW_RATE_FAST;
    i2c2_gpio.GPIO_Mode= GPIO_MODE_AF_OD;//alternate open-drain
    i2c2_gpio.GPIO_Alternate = GPIO_AF_I2C;
    GPIO_Peripheral_Initialize(GPIOy, &i2c2_gpio);

    I2C_Reset(I2C);
    I2C_Initializes_Structure(&I2C_InitStructure);
    I2C_ResetBusy();
    I2C_InitStructure.OwnAddr1 = I2CSlave_OwnAddress;
    I2C_InitStructure.DutyCycle   = I2C_SMDUTYCYCLE_1;
    I2C_InitStructure.AckEnable   = I2C_ACKEN;
    I2C_InitStructure.AddrMode    = I2C_ADDR_MODE_7BIT;
    I2C_InitStructure.ClkSpeed    = 100000;// 100Kbits/s
    I2C_Initializes(I2C, &I2C_InitStructure);// Initial and Enable I2Cx
    /* int enable */
    I2C_Interrupts_Enable(I2C, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR);
    I2C_NVIC_Config();// #define I2Cx_UseIT
    I2C_ON(I2C);//power on the I2C module
}

/***************************************************************************//**
* @brief   Initialize I2C module in slave mode.
* @param   I2CSlave_OwnAddress The slave device's own address.
* @returnNone
******************************************************************************/
void I2CSlave_Init(uint16_t I2CSlave_OwnAddress)
{
    I2C_Config(I2CSlave_OwnAddress);
    //I2C_NVIC_Config();
    //I2C_ResetBusy();
}

/***************************************************************************//**
* @brief   Configure the N32G031C8 device as a PMBus slave.
* @param   PMBusSlave_DeviceAddress The slave device's own address.
* @returnNone
******************************************************************************/
#warning "Change the GPIOs used for Alert and Control lines to match the desired GPIOs for the application."
void PMBusSlave_Init(unsigned char PMBusSlave_DeviceAddress)
{
    //StatusRegs.StatusWord.all = 0; //Clear status bits for the status registers we are using
    //StatusRegs.StatusCml.all = 0;
    //StatusRegs.StatusVout.all = 0;
    //StatusRegs.StatusIout.all = 0;
    //StatusRegs.StatusInput.all = 0;
    //StatusRegs.StatusTemperature.all = 0;
    slave_address = PMBusSlave_DeviceAddress;
    I2CTimeout = I2CT_LONG_TIMEOUT * 1000;
    I2CSlave_Init(slave_address);   // Initialize USCI module
}

/***************************************************************************//**
* @brief   Handles timeouts. Triggered if BYTESENT held low > 2ms.
*          Need to reset the I2C
* @param   None
* @returnNone
******************************************************************************/


#if PEC
unsigned char PMBusSlave_CrcMsgSize = 0;
//unsigned char PMBusSlave_CrcMsg;
unsigned char PMBusSlave_CrcMsg;//the maximum data length is 18
unsigned char PMBusSlave_CrcMasterGenerated = 0;
unsigned char PMBusSlave_CrcSlaveGenerated = 0;
//PMBusSlave_CrcMsg = slave_address << 1;
#endif

static unsigned char PMBusMaster_RWFlag = 1;//0: write, 1: read

/**
* @briefI2C interrupt callback function
* @param I2Cx I2C
*/
void I2C_EV_IRQHandler(void)
{
    uint8_t timeout_flag = 0;
    uint32_t last_event = I2C_Last_Event_Get(I2C);
#if PEC
    //unsigned char PMBusSlave_CrcMsgSize = 0;
    //unsigned char PMBusSlave_CrcMsg;
    //unsigned char PMBusSlave_CrcMasterGenerated = 0;
    //unsigned char PMBusSlave_CrcSlaveGenerated = 0;
    //PMBusSlave_CrcMsg = slave_address << 1;
    PMBusSlave_CrcMsg = slave_address;
#endif
    StartCpuTimer0(); // each time the interrupt is triggered, we start the timer to check the timeout event.
    if ((last_event & I2C_ROLE_MASTER) != I2C_ROLE_MASTER)
    {
      switch (last_event) {
            /* Slave Tx */
            case I2C_EVT_SLAVE_SEND_ADDR_MATCHED:// 0x00060082.EV1.EV3_1 (ADDRF TXDATE), the slave address(read) sent by the master is matched, then we can send out data to the master.
                iicSlave.ptr2 = 0;
                iicSlave.t_data_len = 0;
                PMBusMaster_RWFlag = 1;
                                StopCpuTimer0();       //No timeout, so stop the timer
                ReloadCpuTimer0();   //Reload the period value (35 ms timeout)
                                PMBusSlave_CommandType = PMBusSlave_DecodeCommand(PMBusSlave_Command);//return length: 0, 1, 2
                if (PMBusSlave_CommandType > 0)//send/receive according to command
                {
                                        switch(PMBusSlave_CommandType)
                                        {
                                                case READ_BYTE_LEN:
                                                        PMBusSlave_CrcMsg = PMBusSlave_Command;            // store first rx byte
                            PMBusSlave_CrcMsg = slave_address + 1;                                // store slave address + R/W=1
                            PMBusSlave_CrcMsg = PMBusSlave_TransmitBuffer;    // store tx byte 1
                            PMBusSlave_CrcMsgSize = 4;
                                                        PMBusSlave_CrcSlaveGenerated = CRC8_Calculate(PMBusSlave_CrcMsg, PMBusSlave_CrcMsgSize);
                            //PMBusSlave_CrcSlaveGenerated = CRC8(PMBusSlave_CrcMsg, PMBusSlave_CrcMsgSize);                           
                            //iicSlave.t_data_len = 2;
                            PMBusSlave_TransmitBuffer = PMBusSlave_CrcSlaveGenerated;
                                                        iicSlave.t_data_len = PMBusSlave_CommandType;
                                                        I2C_Data_Send(I2C, PMBusSlave_TransmitBuffer);//send the first byte
                                                        break;
                                               
                                                case READ_WORD_LEN:
                                                        PMBusSlave_CrcMsg = PMBusSlave_Command;            // store first rx byte      
                            PMBusSlave_CrcMsg = slave_address + 1;                                // store slave address + R/W=1
                            PMBusSlave_CrcMsg = PMBusSlave_TransmitBuffer;    // store tx byte 1
                            PMBusSlave_CrcMsg = PMBusSlave_TransmitBuffer;    // store tx byte 2
                            PMBusSlave_CrcMsgSize = 5;
                                                        PMBusSlave_CrcSlaveGenerated = CRC8_Calculate(PMBusSlave_CrcMsg, PMBusSlave_CrcMsgSize);
                            //PMBusSlave_CrcSlaveGenerated = CRC8(PMBusSlave_CrcMsg, PMBusSlave_CrcMsgSize);                           
                            //iicSlave.t_data_len = 3;
                            PMBusSlave_TransmitBuffer = PMBusSlave_CrcSlaveGenerated;
                                                        iicSlave.t_data_len = PMBusSlave_CommandType;
                                                        I2C_Data_Send(I2C, PMBusSlave_TransmitBuffer);//send the first byte
                                                        break;
                                               
                                                case READ_MFR_ID_LEN://const value                                                       
                                                        //PMBusSlave_TransmitBuffer = 0xF7;//the last byte, i.e, the CRC byte
                                                        //iicSlave.t_data_len = PMBusSlave_CommandType;
                                                        I2C_Data_Send(I2C, _MFR_ID);//send the first byte
                                                        break;
                                               
                                                case READ_MFR_MODEL_LEN:
                                                        //PMBusSlave_TransmitBuffer = 0xDD;//const value
                                                        I2C_Data_Send(I2C, _MFR_MODEL);//send the first byte
                                                        break;
                                               
                                                case READ_MFR_REV_LEN:                                                       
                            //PMBusSlave_TransmitBuffer = 0xFD;//the last byte, i.e, the CRC byte
                                                        I2C_Data_Send(I2C, _MFR_REVISION);//send the first byte
                                                        break;
                                               
                                                default:
                                                        break;
                                        }
                }
                else
                {                  
                  I2C_Data_Send(I2C,0xFF);
                                        iicSlave.ptr2 = 0;
                  PMBusSlave_DummyCommand = 0;
                }
                break;

            //The master wants to read the next byte from slave device
            case I2C_EVT_SLAVE_DATA_SENDING:       // 0x00060080.EV3 (TXDATE)
                //I2C_Flag_Status_Clear(I2C, I2C_FLAG_TXDATE);
                StopCpuTimer0();       //No timeout, so stop the timer
                ReloadCpuTimer0();   //Reload the period value (35 ms timeout)
                break;

            case I2C_EVT_SLAVE_DATA_SENDED:      // 0x00060084.EV3_2 (TXDATE BSF)
                StopCpuTimer0();       //No timeout, so stop the timer
                ReloadCpuTimer0();   //Reload the period value (35 ms timeout)
                if (iicSlave.ptr2 < IIC_Slave_BufSize)//IIC_Slave_BufSize
                {
                                        switch(PMBusSlave_CommandType)
                                        {
                                                case READ_BYTE_LEN:
                                                case READ_WORD_LEN:
                                                        I2C_Data_Send(I2C, PMBusSlave_TransmitBuffer); //send the next data byte and clear TXDATE flag
                                                        break;
                                               
                                                case READ_MFR_ID_LEN:
                                                        I2C_Data_Send(I2C, _MFR_ID);
                                                        break;
                                               
                                                case READ_MFR_MODEL_LEN:
                                                        I2C_Data_Send(I2C, _MFR_MODEL);
                                                        break;
                                               
                                                case READ_MFR_REV_LEN:
                                                        I2C_Data_Send(I2C, _MFR_REVISION);
                                                        break;
                                               
                                                default:
                                                        iicSlave.ptr2 = 0;
                                                        I2C_Data_Send(I2C, 0xFF);//clear TXDATE flag
                                                        break;
                                        }
                }               
                else
                {
                  iicSlave.ptr2 = 0;
                  I2C_Data_Send(I2C, 0xFF);//clear TXDATE flag
                }
                break;

            /* Slave Rx */
            //The slave address(write) sent by the master is matched, here we are prepare for the coming data.
            case I2C_EVT_SLAVE_RECV_ADDR_MATCHED:// 0x00020002.EV1 (ADDRF),
                StopCpuTimer0();       //No timeout, so stop the timer
                ReloadCpuTimer0();   //Reload the period value (35 ms timeout)
                iicSlave.ptr = 0;      //Clear the receive pointer and transmit pointer
                iicSlave.ptr2 = 0;
                PMBusMaster_RWFlag = 0;//write mode
                PMBusSlave_Command = I2C_Data_Recv(I2C);
                break;

            //The slave has received data from the master and all here we need to do is to save all of them.
            case I2C_EVT_SLAVE_DATA_RECVD:// 0x00020040.EV2 (RXDATNE)
                StopCpuTimer0();       //No timeout, so stop the timer
                ReloadCpuTimer0();   //Reload the period value (35 ms timeout)
                if (iicSlave.ptr < IIC_Slave_BufSize)
                {
                  iicSlave.buf = I2C_Data_Recv(I2C);//receive data and clear RXDATNE flag
                  //iicSlave2.bufSt                = 1;   //data is updating now...
                }
                else
                {
                  I2C_Data_Recv(I2C);// clear RXDATNE flag
                }
                PMBusSlave_Command = iicSlave.buf;
                break;

            //The master sends out a STOP condition to end the communication and release the BUS.
            //The STOPF bit is not set after a NACK reception
            case I2C_EVT_SLAVE_STOP_RECVD:   // 0x00000010.EV4 (STOPF)
                StopCpuTimer0();       //No timeout, so stop the timer
                ReloadCpuTimer0();   //Reload the period value (35 ms timeout)
                I2C_ON(I2C);         // clear STOPF
                                if(PMBusSlave_Command == CLEAR_FAULTS)
                                {                                       
                                        StatusRegs.StatusWord.all = 0;
                                        StatusRegs.StatusCml.all = 0;
                                        StatusRegs.StatusVout.all = 0;
                                        StatusRegs.StatusIout.all = 0;
                                        StatusRegs.StatusInput.all = 0;
                                        StatusRegs.StatusTemperature.all = 0;
                                }
                iicSlave.ptr2 = 0;//send pointer
                iicSlave.ptr = 0;//receive pointer
                PMBusMaster_RWFlag = 0;//write mode by default
                break;

            case I2C_EVT_SLAVE_ACK_MISS:
                //I2C_ON(I2C);
                StopCpuTimer0();       //No timeout, so stop the timer
                ReloadCpuTimer0();   //Reload the period value (35 ms timeout)
                I2C_Flag_Status_Clear(I2C, I2C_FLAG_ACKFAIL);
                break;

            default:
                I2C_ON(I2C);
                StopCpuTimer0();         //No timeout, so stop the timer
                ReloadCpuTimer0();         //Reload the period value (35 ms timeout)               
                CommTimeOut_CallBack(SLAVE_UNKNOW);
                //PMBusMaster_RWFlag = 1;//write mode by default
                break;
      }
    }

    if ((last_event & 0x00000100) == 0x00000100)
    {
      CommTimeOut_CallBack(SLAVE_UNKNOW);
    }

    /*
    if (timeout_flag)
    {
      if ((I2CTimeout--) == 0)
      {
            CommTimeOut_CallBack(SLAVE_UNKNOW);
      }
    }
    else
    {
      I2CTimeout = I2CT_LONG_TIMEOUT;
    }*/

    //EV3_2: When the master sends a NACK in order to tell slave that data transmission
    //shall end (before sending the STOP condition). In this case slave has to stop sending
    //data bytes and expect a Stop condition on the bus.
    /*
    if (last_event == I2C_EVT_SLAVE_ACK_MISS)
    {
      I2C_ClrFlag(I2C2, I2C_FLAG_ACKFAIL);
      if (iicSlave.ptr2 != 0)
      {
            //flag_slave_send_finish = 1;
      }
      else
      {
      }
    }
    */

    //StopCpuTimer0();                //No timeout, so stop the timer
    //ReloadCpuTimer0();            //Reload the period value (35 ms timeout)
}

/**
*\*\name    PMBus_Error_Detect.
*\*\fun   check whether the I2C module is timeout.
*\*\param   none
*\*\returnnone
**/
void PMBus_Error_Detect(void)
{
    if (PMBus_timeout >= PMBUS_TIME_OUT)
    {
      PMBus_timeout = 0;
      CommTimeOut_CallBack(SLAVE_UNKNOW);
    }
}

/**
*\*\name    I2C_ER_IRQHandler.
*\*\fun   i2c error interrupt service function.
*\*\param   none
*\*\returnnone
**/
void I2C_ER_IRQHandler(void)
{
    uint32_t last_event;
    last_event = I2C_Last_Event_Get(I2C);

    /*
    EV3_2: When the master sends a NACK in order to tell slave that data transmission
    shall end (before sending the STOP condition). In this case slave has to stop sending
    data bytes and expect a Stop condition on the bus.
    */
    if (last_event == I2C_EVT_SLAVE_ACK_MISS)
    {
      I2C_Flag_Status_Clear(I2C, I2C_FLAG_ACKFAIL);

      if (iicSlave.ptr2 != 0) /*slave send the last data and recv NACK*/
      {
            //flag_slave_send_finish = 1;
      }
      else /*not the last data recv nack, send fail */
      {

      }
    }
}

/**
*\*\name    SystemNVICReset.
*\*\fun   System software reset.
*\*\param   none
*\*\returnnone
**/
void SystemNVICReset(void)
{
    __disable_irq();
    //log_info("***** NVIC system reset! *****\r\n");
    NVIC_SystemReset();
}

/**
*\*\name    IIC_RCCReset.
*\*\fun   RCC clock reset.
*\*\param   none
*\*\returnnone
**/
void IIC_RCCReset(void)
{
    RCC_Peripheral_Reset(RCC_RST_I2CRST);
    RCC_APB_Peripheral_Clock_Disable(RCC_APB_PERIPH_I2C);
    GPIOy->PMODE &= 0xFFFFF0FF; /*input */
    //RCC_APB_Peripheral_Clock_Disable(RCC_APB_PERIPH_AFIO);
    //RCC_APB_Peripheral_Clock_Disable(RCC_APB_PERIPH_IOPA);
    RCC_Peripheral_Reset(RCC_RST_I2CRST);
    PMBusSlave_Init(I2C_SLAVE_ADDR);

    /*
    if (RCC_RESET_Flag >= 3)
    {
      SystemNVICReset();
    }
    else
    {
      RCC_RESET_Flag++;
      RCC_Peripheral_Reset(RCC_RST_I2CRST);
      RCC_APB_Peripheral_Clock_Disable(RCC_APB_PERIPH_I2C);
      GPIOx->PMODE &= 0xFFFFF0FF; //input
      RCC_APB_Peripheral_Clock_Disable(RCC_APB_PERIPH_AFIO);
      RCC_APB_Peripheral_Clock_Disable(RCC_APB_PERIPH_IOPA);
      RCC_Peripheral_Reset(RCC_RST_I2CRST);
      //log_info("***** IIC module by RCC reset! *****\r\n");
      i2c_slave_init();
    }
    */
}

/**
*\*\name    IIC_SWReset.
*\*\fun   I2c software reset.
*\*\param   none
*\*\returnnone
**/
void IIC_SWReset(void)
{
    GPIO_InitType i2cx_gpio;

    GPIO_Structure_Initialize(&i2cx_gpio);
    i2cx_gpio.Pin            = I2C_SCL_PIN | I2C_SDA_PIN;
    i2cx_gpio.GPIO_Slew_Rate = GPIO_SLEW_RATE_FAST;
    i2cx_gpio.GPIO_Mode      = GPIO_MODE_OUT_OD;
    GPIO_Peripheral_Initialize(GPIOy, &i2cx_gpio);

    //I2CTimeout = I2CT_LONG_TIMEOUT;

    for (;;)
    {
      if ((I2C_SCL_PIN | I2C_SDA_PIN) == (GPIOy->PID & (I2C_SCL_PIN | I2C_SDA_PIN)))
      {
            I2C->CTRL1 |= 0x8000;
            __NOP();
            __NOP();
            __NOP();
            __NOP();
            __NOP();
            I2C->CTRL1 &= ~0x8000;

            //log_info("***** IIC module self reset! *****\r\n");
            break;
      }
      else
      {
            IIC_RCCReset();
            //if ((I2CTimeout--) == 0)
            //{
            //IIC_RCCReset();
            //}
      }
    }
}

/**
*\*\name    CommTimeOut_CallBack.
*\*\fun   Callback function.
*\*\param   none
*\*\returnnone
**/
void CommTimeOut_CallBack(ErrCode_t errcode)
{
    //log_info("...ErrCode:%d\r\n", errcode);

#if (COMM_RECOVER_MODE == MODULE_SELF_RESET)
    if(++uI2C_error_cnt >= 65530)
    {
      uI2C_error_cnt = 0;
    }
    IIC_RCCReset();//IIC_SWReset();
#elif (COMM_RECOVER_MODE == MODULE_RCC_RESET)
    IIC_RCCReset();
#elif (COMM_RECOVER_MODE == SYSTEM_NVIC_RESET)
    SystemNVICReset();
#endif
}



PMBusSlave.h

/*******************************************************************************
Filename:       PMBusSlave.h
Copyright 2025 ASTO, Inc.
*******************************************************************************/
#ifndef PMBUSSLAVE_H_
#define PMBUSSLAVE_H_

#include "n32g003.h"

//
// I2C GPIO pins
//
//#define I2C_SLAVE_ADDR      0x6AU// I2C Slave Address
//#define GPIO_AF_I2C                 GPIO_AF6_I2C1
#define I2C_SCL_PIN                 GPIO_PIN_4        // GPIO number for I2C SDAA
#define I2C_SDA_PIN                 GPIO_PIN_5        // GPIO number for I2C SCLA
#define GPIOy                      GPIOA
#define GPIO_AF_I2C                GPIO_AF6_I2C
#define I2C_SLAVE_ADDR        (0x21<<1)
//#define I2C_SLAVE_ADDR        (0x21)

#define BUFFER_SIZE                15
#define IIC_Slave_BufSize         20//the package size is 32
#define        PMBUS_TIME_OUT                35//typically 35 milli-seconds
#define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))
#define SCL_STATUS()      GPIO_Input_Pin_Data_Get(GPIOy, I2C_SCL_PIN)

#define        READ_BYTE_LEN                1
#define        READ_WORD_LEN                2
#define        READ_MFR_ID_LEN                7
#define        READ_MFR_MODEL_LEN        15
#define        READ_MFR_REV_LEN        3

typedef enum
{
    MASTER_OK = 0,
    MASTER_BUSY,
    MASTER_MODE,
    MASTER_TXMODE,
    MASTER_RXMODE,
    MASTER_SENDING,
    MASTER_SENDED,
    MASTER_RECVD,
    MASTER_BYTEF,
    MASTER_BUSERR,
    MASTER_UNKNOW,
    SLAVE_OK = 20,
    SLAVE_BUSY,
    SLAVE_MODE,
    SLAVE_BUSERR,
    SLAVE_UNKNOW,

}ErrCode_t;

#define MODULE_SELF_RESET       1
#define MODULE_RCC_RESET      2
#define SYSTEM_NVIC_RESET       3
#define COMM_RECOVER_MODE       1//0

void I2CSlave_Init(uint16_t Own_Address);
void PMBusSlave_Init(unsigned char PMBusSlave_DeviceAddress);
unsigned char PMBusSlave_DecodeCommand(unsigned char PMBusSlave_RxCommand);
void PMBus_Error_Detect(void);

static unsigned short PMBusSlave_Crc8MakeBitwise(
unsigned char PMBusSlave_CRC, unsigned char PMBusSlave_Poly,
unsigned char *PMBusSlave_Pmsg, unsigned int PMBusSlave_MsgSize);
void CommTimeOut_CallBack(ErrCode_t errcode);

extern volatile struct STATUS_REGS StatusRegs;

typedef struct {
    uint8_t buf;//receive and send buffer
    uint16_t ptr;                  //data receive pointer
    uint16_t ptr2;                   //data transmit pointer
    uint8_t bufSt;                  
    uint8_t ptrSt;
        uint8_t t_data_len;                               //transmit data length
} _iicSlave;


#endif /*PMBUSSLAVE_H_*/



PMBus.h

/*******************************************************************************
    Filename:       PMBus.h
    Copyright 2025 ASTO, Inc.
*******************************************************************************/
#ifndef PMBUS_H
#define PMBUS_H

#include "n32g003.h"
////////////USER CODE////////////
// The user should change the value of PEC to match their implementation's
// desired Packet Error Checking capability. This will cause all associated
// files to build either with or without PEC capability, accordingly.
#warning        "User should change PEC value to build with or without PEC capability."
#define PEC                1        //PEC 0: Disable Packet Error Checking
                                        //PEC 1: Enable Packet Error Checking
////////////END USER CODE////////                                       
                                       
#if PEC
        #define PEC_PASS                 1
        #define PEC_FAIL                 0
        #define CRC8_POLY                           0x07
        #define CRC8_INIT_REM              0x0
#endif

#define READBYTE      0
#define READWORD      2
#define WRITEBYTE   4
#define SENDBYTE      6
#define WRITEWORD   8
#define RWBYTE                  1
#define RWWORD                  3

#define READBLOCK          5//New customized group

//GROUP#0
#define CAPABILITY                        0x19 //1
#define STATUS_BYTE                         0x78 //2
#define STATUS_VOUT                         0x7A //3
#define STATUS_IOUT                         0x7B //4
#define STATUS_INPUT                        0x7C //5
#define STATUS_TEMPERATURE                  0x7D //6
#define STATUS_CML                        0x7E //7
#define STATUS_OTHER                        8
#define STATUS_MFR_SPECIFIC               0x80 //9
#define STATUS_FANS_1_2                     10
#define STATUS_FANS_3_4                     11
#define PMBUS_REVISION                      0x98 //12

// GROUP #2
#define STATUS_WORD                         0x79 //13
#define READ_VIN                            0x88 //14
#define READ_IIN                            0x89 //15
#define READ_VCAP                           16
#define READ_VOUT                           0x8B //17
#define READ_IOUT                           0x8C //18
#define READ_TEMPERATURE_1                  19       //Primary side heat sink temperature by degree C
#define READ_TEMPERATURE_2                  20       //Inlet ambient temperature by degree C
#define READ_TEMPERATURE_3                  0x8F //21 //Secondary side heat sink temperature by degree C
#define READ_FAN_SPEED_1                  22
#define READ_FAN_SPEED_2                  23
#define READ_FAN_SPEED_3                  24
#define READ_FAN_SPEED_4                  25
#define READ_DUTY_CYCLE                     26
#define READ_FREQUENCY                      27
#define READ_POUT                           0x96 //28
#define READ_PIN                            0x97 //29
#define MFR_VIN_MIN                         0xA0 //30
#define MFR_VIN_MAX                         0xA1 //31
#define MFR_IIN_MAX                         0xA2 //32
#define MFR_PIN_MAX                         0xA3 //33
#define MFR_VOUT_MIN                        0xA5 //34
#define MFR_VOUT_MAX                        0xA4 //35
#define MFR_IOUT_MAX                        0xA6 //36
#define MFR_POUT_MAX                        0xA7 //37
#define MFR_TAMBIENT_MAX                  0xA8 //38
#define MFR_TAMBIENT_MIN                  0xA9 //39

// GROUP #4
#define STORE_DEFAULT_CODE                  40
#define RESTORE_DEFAULT_CODE                41
#define STORE_USER_CODE                     42
#define RESTORE_USER_CODE                   43

// GROUP#6
#define CLEAR_FAULTS                        0x03 //44        //If the fault is still present after 03h is issued, the system needs to be able to report the fault and set the corresponding register.
#define STORE_DEFAULT_ALL                   45
#define RESTORE_DEFAULT_ALL               46
#define STORE_USER_ALL                      47
#define RESTORE_USER_ALL                  48

//GROUP#8

//#define PAGE/ GENERAL CALL                  49//The old statement
#define PAGE                                                  0x00 //49        //GENERAL CALL
#define OPERATION                           50        //switch machine flag,0x80: means power on (default),0x00: means power off.
#define ON_OFF_CONFIG                     51        //command code: 0x02, parameter, 0x15: means PSU on/off only by PS_ON# control(default).0x19: means PSU on/off only by PMBus control.
#define PHASE                               52
#define WRITE_PROTECT                     53
#define VOUT_MODE                           0x20 //54
#define FAN_CONFIG_1_2                      55
#define FAN_CONFIG_3_4                      56
#define VOUT_OV_FAULT_RESPONSE            57
#define VOUT_UV_FAULT_RESPONSE            58
#define IOUT_OC_FAULT_RESPONSE            59
#define IOUT_OC_LV_FAULT_RESPONSE         60
#define IOUT_UC_FAULT_RESPONSE            61
#define OT_FAULT_RESPONSE                   62
#define UT_FAULT_RESPONSE                   63
#define VIN_OV_FAULT_RESPONSE               64
#define VIN_UV_FAULT_RESPONSE               65
#define IIN_OC_FAULT_RESPONSE               66
#define TON_MAX_FAULT_RESPONSE            67
#define POUT_OP_FAULT_RESPONSE            68

//GROUP#10
#define VOUT_COMMAND                        0x21 //69
#define VOUT_TRIM                           70
#define VOUT_CAL_OFFSET                     71
#define VOUT_MAX                            72
#define VOUT_MARGIN_HIGH                  73
#define VOUT_MARGIN_LOW                     74
#define VOUT_TRANSITION_RATE                75
#define VOUT_DROOP                        76
#define VOUT_SCALE_LOOP                     77
#define VOUT_SCALE_MONITOR                  78
#define POUT_MAX                            79
#define MAX_DUTY                            80
#define FREQUENCY_SWITCH                  81
#define VIN_ON                              82
#define VIN_OFF                           83
#define INTERLEAVE                        84
#define IOUT_CAL_GAIN                     85
#define IOUT_CAL_OFFSET                     86
#define FAN_COMMAND_1                     87
#define FAN_COMMAND_2                     88
#define FAN_COMMAND_3                     89
#define FAN_COMMAND_4                     90
#define VOUT_OV_FAULT_LIMIT               91
#define VOUT_OV_WARN_LIMIT                  92
#define VOUT_UV_WARN_LIMIT                  93
#define VOUT_UV_FAULT_LIMIT               94
#define IOUT_OC_FAULT_LIMIT               95
#define IOUT_OC_LV_FAULT_LIMIT            96
#define IOUT_OC_WARN_LIMIT                  0x4A //97
#define IOUT_UC_FAULT_LIMIT               98
#define OT_FAULT_LIMIT                      99
#define OT_WARN_LIMIT                     0x51 //100
#define UT_WARN_LIMIT                     101
#define UT_FAULT_LIMIT                      102
#define VIN_OV_FAULT_LIMIT                  103
#define VIN_OV_WARN_LIMIT                   104
#define VIN_UV_WARN_LIMIT                   105
#define VIN_UV_FAULT_LIMIT                  106
#define IIN_OC_FAULT_LIMIT                  107
#define IIN_OC_WARN_LIMIT                   108
#define POWER_GOOD_ON                     109
#define POWER_GOOD_OFF                      110
#define TON_DELAY                           111
#define TON_RISE                            112
#define TON_MAX_FAULT_LIMIT               113
#define TOFF_DELAY                        114
#define TOFF_FALL                           115
#define TOFF_MAX_WARN_LIMIT               116
#define POUT_OP_FAULT_LIMIT               117
#define POUT_OP_WARN_LIMIT                  118
#define PIN_OP_WARN_LIMIT                   119
#define        MFR_ID                                                                0x99 //120 //New added command
#define        MFR_MODEL                                                        0x9A //121
#define        MFR_REVISION                                                0x9B //122

/*
struct VOUT_MODE_BITS
{
        signed Parameter:5;                                                // 0 - 4
        unsigned Mode:3;                                      // 5 - 7
};
union VOUT_MODE_REG
{
        unsigned char                                         all;
        struct VOUT_MODE_BITS         bit;
};
union VOUT_MODE_REG VoutMode;
*/

//----------------------------------------------------
//        Status registers
//        STATUS_BYTE and STATUS_WORD register bit definitions */
//lower byte
struct STATUS_LOWER_BITS {                // bits
   uint16_t NONE_OF_THE_ABOVE:1;// 0
   uint16_t CML:1;                      // 1 A communications, memory or logic fault has occurred
   uint16_t TEMPERATURE:1;      // 2 A temperature fault or warning has occurred
   uint16_t VIN_UV:1;              // 3 An input under voltage fault has occurred
   uint16_t IOUT_OC:1;              // 4 An output over current fault has occured
   uint16_t VOUT_OV:1;              // 5 An output over voltage fault has occured
   uint16_t OFF:1;                      // 6 This bit is asserted if the unit is not providing power to the output, regardless of the reason, including simply not being enabled.
   uint16_t BUSY:1;                      // 7
};
//upper byte
struct STATUS_UPPER_BITS {                // bits
   uint16_t UNKNOWN:1;                // 8
   uint16_t OTHER:1;              // 9
   uint16_t FANS:1;                      // 10 A fan or airflow fault or warning has occurred
   uint16_t POWER_GOOD:1;       // 11 The POWER_GOOD signal, if present, is negated
   uint16_t MFR:1;                      // 12 A manufacturer specific fault or warning has occurred(PS ON or soft power off)
   uint16_t INPUT:1;              // 13 An input voltage, input current, or input power fault or warning has occurred
   uint16_t IOUT_POUT:1;      // 14 An output current or output power fault or warning has occurred
   uint16_t VOUT:1;                      // 15 An output voltage fault or warning has occurred

};

union STATUS_BYTE_REG {
        uint16_t            all;
        struct STATUS_LOWER_BITS bit;
};

struct STATUS_WORD_BITS
{
   uint16_t NONE_OF_THE_ABOVE:1;// 0
   uint16_t CML:1;                      // 1 A communications, memory or logic fault has occurred
   uint16_t TEMPERATURE:1;      // 2 A temperature fault or warning has occurred
   uint16_t VIN_UV:1;              // 3 An input under voltage fault has occurred
   uint16_t IOUT_OC:1;              // 4 An output over current fault has occured
   uint16_t VOUT_OV:1;              // 5 An output over voltage fault has occured
   uint16_t OFF:1;                      // 6 This bit is asserted if the unit is not providing power to the output, regardless of the reason, including simply not being enabled.
   uint16_t BUSY:1;                      // 7
   uint16_t UNKNOWN:1;                // 8
   uint16_t OTHER:1;              // 9
   uint16_t FANS:1;                      // 10 A fan or airflow fault or warning has occurred
   uint16_t POWER_GOOD:1;       // 11 The POWER_GOOD signal, if present, is negated
   uint16_t MFR:1;                      // 12 A manufacturer specific fault or warning has occurred(PS ON or soft power off)
   uint16_t INPUT:1;              // 13 An input voltage, input current, or input power fault or warning has occurred
   uint16_t IOUT_POUT:1;      // 14 An output current or output power fault or warning has occurred
   uint16_t VOUT:1;                      // 15 An output voltage fault or warning has occurred   
};

//New defined struct
union STATUS_WORD_REG {
   uint16_t            all;      
   struct STATUS_WORD_BITS bit;
};

/*
union STATUS_WORD_REG {
   uint16_t            all;
   union STATUS_BYTE_REG StatusByte;   
   struct STATUS_UPPER_BITS bit;
};
*/

//----------------------------------------------------
//        STATUS_VOUT register bit definitions */
struct STATUS_VOUT_BITS {                // bits
   uint16_t POWER_ON_TRACKING_ERROR:1;        // 0
   uint16_t TOFF_MAX_WARNING:1;              // 1
   uint16_t TON_MAX_WARNING:1;              // 2
   uint16_t VOUT_MAX_WARNING:1;              // 3 NOT USED, VOUT_MAX Warning (An attempt has been made to set the output voltage to value higher than allowed by the VOUT_MAX command
   uint16_t VOUT_UV_FAULT:1;              // 4 VOUT Under voltage Fault
   uint16_t VOUT_UV_WARNING:1;              // 5 VOUT Under voltage Warning
   uint16_t VOUT_OV_WARNING:1;              // 6 VOUT Over voltage Warning
   uint16_t VOUT_OV_FAULT:1;              // 7 VOUT Over voltage Fault
};

union STATUS_VOUT_REG {
   uint16_t            all;
   struct STATUS_VOUT_BITS bit;
};

//----------------------------------------------------
//        STATUS_IOUT register bit definitions */
struct STATUS_IOUT_BITS {                // bits
   uint16_t POUT_OP_WARNING:1;                        // 0 POUT Overpower Warning
   uint16_t POUT_OP_FAULT:1;              // 1 POUT Overpower Fault
   uint16_t IN_POWER_LIMITING_MODE:1;   // 2
   uint16_t CURRENT_SHARE_FAULT:1;      // 3
   uint16_t IOUT_UC_FAULT:1;              // 4
   uint16_t IOUT_OC_WARNING:1;              // 5 IOUT Over current Warning
   uint16_t IOUT_OC_FAULT_LV_SHUTDOWN:1;// 6 NOT USED, IOUT Over current And Low Voltage Shutdown Fault
   uint16_t IOUT_OC_FAULT:1;              // 7 IOUT Over current Fault
};

union STATUS_IOUT_REG {
   uint16_t            all;
   struct STATUS_IOUT_BITS bit;
};

//----------------------------------------------------
//        STATUS_TEMPERATURE register bit definitions */
struct STATUS_TEMPERATURE_BITS {                // bits
   uint16_t rsvd:4;                                                // 0:3
   uint16_t UT_FAULT:1;                                        // 4
   uint16_t UT_WARNING:1;                                // 5
   uint16_t OT_WARNING:1;                           // 6 Over temperature Warning
   uint16_t OT_FAULT:1;                               // 7 Over temperature Fault
};

union STATUS_TEMPERATURE_REG {
   uint16_t            all;
   struct STATUS_TEMPERATURE_BITS bit;
};

//----------------------------------------------------
//        STATUS_CML register bit definitions */
struct STATUS_CML_BITS {                // bits
   uint16_t OTHER_MEMORY_OR_LOGIC_FAULT:1;        // 0
   uint16_t OTHER_COMMUNICATION_FAULT:1;    // 1
   uint16_t rsvd:1;                                              // 2
   uint16_t PROCESSOR_FAULT_DETECTED:1;                // 3
   uint16_t MEMORY_FAULT_DETECTED:1;                // 4
   uint16_t PACKET_ERROR_CHECK_FAILED:1;        // 5
   uint16_t INVALID_DATA:1;                              // 6
   uint16_t INVALID_COMMAND:1;                      // 7
};

union STATUS_CML_REG {
   uint16_t            all;
   struct STATUS_CML_BITS bit;
};

//----------------------------------------------------
//        STATUS_INPUT register bit definitions */
struct STATUS_INPUT_BITS {                // bits
   uint16_t PIN_OP_WARNING:1;                                        // 0
   uint16_t IIN_OC_WARNING:1;                              // 1
   uint16_t IIN_OC_FAULT:1;                                      // 2
   uint16_t UNIT_OFF_FOR_LOW_INPUT_VOLTAGE:1;   // 3 Unit is Off For Insufficient Input Voltage
   uint16_t VIN_UV_FAULT:1;                                      // 4 VIN under voltage Fault
   uint16_t VIN_UV_WARNING:1;                              // 5 VIN under voltage Warning
   uint16_t VIN_OV_WARNING:1;                              // 6 VIN Over voltage Warning
   uint16_t VIN_OV_FAULT:1;                                      // 7 VIN Over voltage Fault
};

union STATUS_INPUT_REG {
   uint16_t            all;
   struct STATUS_INPUT_BITS bit;
};

//----------------------------------------------------
//        STATUS_FANS_1_2 register bit definitions */
struct STATUS_FANS_1_2_BITS {                        // bits
   uint16_t AIR_FLOW_WARNING:1;                        // 0
   uint16_t AIR_FLOW_FAULT:1;              // 1
   uint16_t FAN2_SPEED_OVERRIDE:1;                // 2
   uint16_t FAN1_SPEED_OVERRIDE:1;            // 3 Fan 1 Speed Overridden, which means that the Fan is controlled by external means, not the one designated
   uint16_t FAN2_WARNING:1;                      // 4
   uint16_t FAN1_WARNING:1;                      // 5
   uint16_t FAN2_FAULT:1;                      // 6 Fan 1 Warning
   uint16_t FAN1_FAULT:1;                      // 7 Fan 1 Fault
};

union STATUS_FANS_1_2_REG {
   uint16_t            all;
   struct STATUS_FANS_1_2_BITS bit;
};

//----------------------------------------------------
//        STATUS_FANS_3_4 register bit definitions */
struct STATUS_FANS_3_4_BITS {                // bits
   uint16_t rsvd:2;                                        // 0:1
   uint16_t FAN4_SPEED_OVERRIDE:1;        // 2
   uint16_t FAN3_SPEED_OVERRIDE:1;    // 3
   uint16_t FAN4_WARNING:1;              // 4
   uint16_t FAN3_WARNING:1;              // 5
   uint16_t FAN4_FAULT:1;                      // 6
   uint16_t FAN3_FAULT:1;                      // 7
};

union STATUS_FANS_3_4_REG {
   uint16_t            all;
   struct STATUS_FANS_3_4_BITS bit;
};

//----------------------------------------------------
//        STATUS_OTHER register bit definitions */
struct STATUS_OTHER_BITS {                // bits
   uint16_t rsvd1:1;                                                // 0
   uint16_t OUTPUT_OR_DEVICE_FAULT:1;   // 1
   uint16_t INPUTB_OR_DEVICE_FAULT:1;                // 2
   uint16_t INPUTA_OR_DEVICE_FAULT:1;        // 3
   uint16_t INPUTB_FUSE_FAULT:1;              // 4
   uint16_t INPUTA_FUSE_FAULT:1;              // 5
   uint16_t rsvd2:2;                                      // 6:7
};

union STATUS_OTHER_REG {
   uint16_t            all;
   struct STATUS_OTHER_BITS bit;
};

//----------------------------------------------------
//        STATUS registers */
struct STATUS_REGS {
   unionSTATUS_WORD_REG                        StatusWord;
   unionSTATUS_VOUT_REG                        StatusVout;
   unionSTATUS_IOUT_REG                        StatusIout;
   unionSTATUS_TEMPERATURE_REG        StatusTemperature;
   unionSTATUS_CML_REG                   StatusCml;
   unionSTATUS_INPUT_REG                   StatusInput;
   unionSTATUS_FANS_1_2_REG           StatusFans12;
   unionSTATUS_FANS_3_4_REG           StatusFans34;
   unionSTATUS_OTHER_REG                        StatusOther;
};

//---------------------------------------------------------------------------
// Status Registers External References & Function Declarations:
//


/*------------------------------------------------------------------------------
ISR for the XINT1 interrupt triggered from GPIO2 (Alert line)
SMBAlert interrupt
------------------------------------------------------------------------------*/
//interrupt void xint1_isr(void);

#endif



主机程序:

iic.c

#include "iic.h"
#include "stm32f10x_tim.h"

/*
设置SDA信号为输出模式
*/
void IIC_SDA_OUTMODE(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = IIC_SDA;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*
设置SDA信号为输入模式
*/
void IIC_SDA_INPUTMODE(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = IIC_SDA;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*
void delay_us(u32 nus)
{
    u32 temp;
    nus -= 1;
    SysTick->LOAD=nus*9;
    SysTick->VAL=0x00;
    SysTick->CTRL = 0X01 ;
    do
    {
      temp=SysTick->CTRL;
    } while((temp&0x01)&&!(temp&(1<<16)));
    SysTick->CTRL = 0X00;
    SysTick->VAL =0X00;
}
*/

/*
If DELAY_TIME is equal to 300, the delay time is about 72us
*/
static void I2C_Delay(void)
{
    uint16_t i;
    for (i = 0; i < DELAY_TIME; i++);
}

/*
函数功能:IIC接口初始化
硬件连接:
SDA:PB7
SCL:PB6
*/
void IIC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin = IIC_SDA | IIC_SCL;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/*
函数功能:IIC总线起始信号
*/
void IIC_Start(void)
{
    IIC_SDA_OUTMODE();//初始化SDA为输出模式
    IIC_SDA_OUT(1);   //数据线拉高
    IIC_SCL_OUT(1);   //时钟线拉高
    I2C_Delay();      //电平保持时间
    IIC_SDA_OUT(0);   //数据线拉低
    I2C_Delay();      //电平保持时间
    IIC_SCL_OUT(0);   //时钟线拉低
}


/*
函数功能:IIC总线停止信号
*/
void IIC_Stop(void)
{
    IIC_SDA_OUTMODE();//初始化SDA为输出模式
    IIC_SDA_OUT(0);   //数据线拉低
    IIC_SCL_OUT(0);   //时钟线拉低
    I2C_Delay();      //电平保持时间
    IIC_SCL_OUT(1);   //时钟线拉高
    I2C_Delay();      //电平保持时间
    IIC_SDA_OUT(1);   //数据线拉高
}

/*
函数功能:获取应答信号
返 回 值:1表示失败,0表示成功
*/
u8 IIC_GetACK(void)
{
    u8 cnt = 0;
    IIC_SDA_INPUTMODE();    //初始化SDA为输入模式
    IIC_SDA_OUT(1);         //数据线上拉
    I2C_Delay();            //电平保持时间
    IIC_SCL_OUT(0);         //时钟线拉低,告诉从机,主机需要数据
    I2C_Delay();            //电平保持时间,等待从机发送数据
    IIC_SCL_OUT(1);         //时钟线拉高,告诉从机,主机现在开始读取数据
    while (IIC_SDA_IN)      //等待从机应答信号
    {
      cnt++;
      if (cnt > 250)return 1;
    }
    IIC_SCL_OUT(0);         //时钟线拉低,告诉从机,主机需要数据
    return 0;
}


/*
函数功能:主机向从机发送应答信号
函数形参:0表示应答,1表示非应答
*/
void IIC_SendACK(uint8_t stat)
{
    IIC_SDA_OUTMODE();          //初始化SDA为输出模式
    IIC_SCL_OUT(0);             //时钟线拉低,告诉从机,主机需要发送数据
    if (stat)IIC_SDA_OUT(1);    //数据线拉高,发送非应答信号
    else IIC_SDA_OUT(0);      //数据线拉低,发送应答信号
    I2C_Delay();                //电平保持时间,等待时钟线稳定
    IIC_SCL_OUT(1);             //时钟线拉高,告诉从机,主机数据发送完毕
    I2C_Delay();                //电平保持时间,等待从机接收数据
    IIC_SCL_OUT(0);             //时钟线拉低,告诉从机,主机需要数据
}


/*
函数功能:IIC发送1个字节数据
函数形参:将要发送的数据
*/
void IIC_WriteOneByteData(uint8_t data)
{
    u8 i;
    IIC_SDA_OUTMODE();                  //初始化SDA为输出模式
    IIC_SCL_OUT(0);                     //时钟线拉低,告诉从机,主机需要发送数据
    for (i = 0; i < 8; i++)
    {
      if (data & 0x80)IIC_SDA_OUT(1); //数据线拉高,发送1
      else IIC_SDA_OUT(0);            //数据线拉低,发送0
      IIC_SCL_OUT(1);               //时钟线拉高,告诉从机,主机数据发送完毕
      I2C_Delay();                  //电平保持时间,等待从机接收数据
      IIC_SCL_OUT(0);               //时钟线拉低,告诉从机,主机需要发送数据
      I2C_Delay();                  //电平保持时间,等待时钟线稳定
      data <<= 1;                     //先发高位
    }
}


/*
函数功能:IIC接收1个字节数据
返 回 值:收到的数据
*/
u8 IIC_Read_One_Byte(void)
{
    u8 i, data;
    IIC_SDA_INPUTMODE();            //初始化SDA为输入模式
    for (i = 0; i < 8; i++)
    {
      IIC_SCL_OUT(0);             //时钟线拉低,告诉从机,主机需要数据
      I2C_Delay();                //电平保持时间,等待从机发送数据
      IIC_SCL_OUT(1);             //时钟线拉高,告诉从机,主机现在正在读取数据
      data <<= 1;
      if (IIC_SDA_IN)data |= 0x01;
      I2C_Delay();                //电平保持时间,等待时钟线稳定
    }
    IIC_SCL_OUT(0);               //时钟线拉低,告诉从机,主机需要数据 (必须拉低,否则将会识别为停止信号)
    return data;
}

/**
@funcation name: pmbus_read_byte
@description: read one byte from PMBus slave device
@param: cmd_code: PMBus command
@buff: to store the data
@return: 0: OK, 1: error
*/
uint8_t pmbus_read_byte(uint8_t cmd_code, uint8_t* buff)
{
    IIC_Start();
    IIC_WriteOneByteData(PMBus_SLAVE_ADDR);//send the slave address, W/R = 0
    if (IIC_GetACK() == 1) //error
    {
      IIC_Stop();
      return IIC_ERROR;
    }
    IIC_WriteOneByteData(cmd_code);//send the command
    if (IIC_GetACK() == 1) //error
    {
      IIC_Stop();
      return IIC_ERROR;
    }
    IIC_Start();
    IIC_WriteOneByteData(PMBus_SLAVE_ADDR | 0x01);//send the slave address, W/R = 1
    if (IIC_GetACK() == 1) //error
    {
      IIC_Stop();
      return IIC_ERROR;
    }

    //*buff = 2;//total data length: one byte + the PEC byte

    *(buff + 0) = IIC_Read_One_Byte();
    IIC_SendACK(0);//0:ACK, 1: NACK

    *(buff + 1) = IIC_Read_One_Byte();
    IIC_SendACK(1);//0:ACK, 1: NACK

    IIC_Stop();

    return IIC_OK;
}

/**
@funcation name: pmbus_write_byte
@description: write one byte to the PMBus slave device
@param: cmd_code: PMBus command, param: the parameter of the current command
@return: 0: OK, 1: error
*/
uint8_t pmbus_write_byte(uint8_t cmd_code, uint8_t param)
{
    IIC_Start();
    IIC_WriteOneByteData(PMBus_SLAVE_ADDR);//send the slave address, W/R = 0
    if (IIC_GetACK() == 1) //error
    {
      IIC_Stop();
      return IIC_ERROR;
    }
    IIC_WriteOneByteData(cmd_code);//send the command
    if (IIC_GetACK() == 1) //error
    {
      IIC_Stop();
      return IIC_ERROR;
    }
        IIC_WriteOneByteData(param);//send the parameter
    if (IIC_GetACK() == 1) //error
    {
      IIC_Stop();
      return IIC_ERROR;
    }

    IIC_Stop();

    return IIC_OK;
}

/**
@funcation name: pmbus_read_word
@description: read one word from PMBus slave device
@param: cmd_code: PMBus command
@buff: to store the data
@return: 0: OK, 1: error
*/
uint8_t pmbus_read_word(uint8_t cmd_code, uint8_t* buff)
{
    IIC_Start();
    IIC_WriteOneByteData(PMBus_SLAVE_ADDR);//send the slave address, W/R = 0
    if (IIC_GetACK() == 1) //error
    {
      IIC_Stop();
      return IIC_ERROR;
    }
    IIC_WriteOneByteData(cmd_code);//send the command
    if (IIC_GetACK() == 1) //error
    {
      IIC_Stop();
      return IIC_ERROR;
    }
    IIC_Start();
    IIC_WriteOneByteData(PMBus_SLAVE_ADDR | 0x01);//send the slave address, W/R = 1
    if (IIC_GetACK() == 1) //error
    {
      IIC_Stop();
      return IIC_ERROR;
    }

    //*buff = 3;//total data length: one word + the PEC byte

    *(buff + 0) = IIC_Read_One_Byte();
    IIC_SendACK(0);//0:ACK, 1: NACK

    *(buff + 1) = IIC_Read_One_Byte();
    IIC_SendACK(0);//0:ACK, 1: NACK

    *(buff + 2) = IIC_Read_One_Byte();
    IIC_SendACK(1);//0:ACK, 1: NACK

    IIC_Stop();

    return IIC_OK;
}


iic.h

#include "stm32f10x.h"

#define IIC_SDA GPIO_Pin_7
#define IIC_SCL GPIO_Pin_6

#define IIC_SDA_OUT(a)if (a)\
                  GPIO_SetBits(GPIOB, IIC_SDA);\
                  else      \
                  GPIO_ResetBits(GPIOB, IIC_SDA)

#define IIC_SCL_OUT(a)if (a)\
GPIO_SetBits(GPIOB, IIC_SCL);\
else      \
GPIO_ResetBits(GPIOB, IIC_SCL)

#define IIC_SDA_IN GPIO_ReadInputDataBit(GPIOB, IIC_SDA)

#define PMBus_SLAVE_ADDR (0x21<<1)
#define IIC_OK      0
#define IIC_ERROR   1
#define DELAY_TIME100

////////////USER CODE////////////
// The user should change the value of PEC to match their implementation's
// desired Packet Error Checking capability. This will cause all associated
// files to build either with or without PEC capability, accordingly.
#warning    "User should change PEC value to build with or without PEC capability."
#define PEC   1   //PEC 0: Disable Packet Error Checking
//PEC 1: Enable Packet Error Checking
////////////END USER CODE////////

#if PEC
#define PEC_PASS            1
#define PEC_FAIL            0
#define CRC8_POLY         0x07
#define CRC8_INIT_REM       0x0
#endif

#define READBYTE      0
#define READWORD      2
#define WRITEBYTE   4
#define SENDBYTE      6
#define WRITEWORD   8
#define RWBYTE      1
#define RWWORD      3

#define READBLOCK   5//New customized group

//GROUP#0
#define CAPABILITY                        0x19 //1
#define STATUS_BYTE                         0x78 //2
#define STATUS_VOUT                         0x7A //3
#define STATUS_IOUT                         0x7B //4
#define STATUS_INPUT                        0x7C //5
#define STATUS_TEMPERATURE                  0x7D //6
#define STATUS_CML                        0x7E //7
#define STATUS_OTHER                        8
#define STATUS_MFR_SPECIFIC               0x80 //9
#define STATUS_FANS_1_2                     10
#define STATUS_FANS_3_4                     11
#define PMBUS_REVISION                      0x98 //12

// GROUP #2
#define STATUS_WORD                         0x79 //13
#define READ_VIN                            0x88 //14
#define READ_IIN                            0x89 //15
#define READ_VCAP                           16
#define READ_VOUT                           0x8B //17
#define READ_IOUT                           0x8C //18
#define READ_TEMPERATURE_1                  19   //Primary side heat sink temperature by degree C
#define READ_TEMPERATURE_2                  20   //Inlet ambient temperature by degree C
#define READ_TEMPERATURE_3                  0x8F //21 //Secondary side heat sink temperature by degree C
#define READ_FAN_SPEED_1                  22
#define READ_FAN_SPEED_2                  23
#define READ_FAN_SPEED_3                  24
#define READ_FAN_SPEED_4                  25
#define READ_DUTY_CYCLE                     26
#define READ_FREQUENCY                      27
#define READ_POUT                           0x96 //28
#define READ_PIN                            0x97 //29
#define MFR_VIN_MIN                         0xA0 //30
#define MFR_VIN_MAX                         0xA1 //31
#define MFR_IIN_MAX                         0xA2 //32
#define MFR_PIN_MAX                         0xA3 //33
#define MFR_VOUT_MIN                        0xA5 //34
#define MFR_VOUT_MAX                        0xA4 //35
#define MFR_IOUT_MAX                        0xA6 //36
#define MFR_POUT_MAX                        0xA7 //37
#define MFR_TAMBIENT_MAX                  0xA8 //38
#define MFR_TAMBIENT_MIN                  0xA9 //39

// GROUP #4
#define STORE_DEFAULT_CODE                  40
#define RESTORE_DEFAULT_CODE                41
#define STORE_USER_CODE                     42
#define RESTORE_USER_CODE                   43

// GROUP#6
#define CLEAR_FAULTS                        0x03 //44   //If the fault is still present after 03h is issued, the system needs to be able to report the fault and set the corresponding register.
#define STORE_DEFAULT_ALL                   45
#define RESTORE_DEFAULT_ALL               46
#define STORE_USER_ALL                      47
#define RESTORE_USER_ALL                  48

//GROUP#8

//#define PAGE/ GENERAL CALL                  49//The old statement
#define PAGE                              0x00 //49   //GENERAL CALL
#define OPERATION                           50//switch machine flag,0x80: means power on (default),0x00: means power off.
#define ON_OFF_CONFIG                     51//command code: 0x02, parameter, 0x15: means PSU on/off only by PS_ON# control(default).0x19: means PSU on/off only by PMBus control.
#define PHASE                               52
#define WRITE_PROTECT                     53
#define VOUT_MODE                           0x20 //54
#define FAN_CONFIG_1_2                      55
#define FAN_CONFIG_3_4                      56
#define VOUT_OV_FAULT_RESPONSE            57
#define VOUT_UV_FAULT_RESPONSE            58
#define IOUT_OC_FAULT_RESPONSE            59
#define IOUT_OC_LV_FAULT_RESPONSE         60
#define IOUT_UC_FAULT_RESPONSE            61
#define OT_FAULT_RESPONSE                   62
#define UT_FAULT_RESPONSE                   63
#define VIN_OV_FAULT_RESPONSE               64
#define VIN_UV_FAULT_RESPONSE               65
#define IIN_OC_FAULT_RESPONSE               66
#define TON_MAX_FAULT_RESPONSE            67
#define POUT_OP_FAULT_RESPONSE            68

//GROUP#10
#define VOUT_COMMAND                        0x21 //69
#define VOUT_TRIM                           70
#define VOUT_CAL_OFFSET                     71
#define VOUT_MAX                            72
#define VOUT_MARGIN_HIGH                  73
#define VOUT_MARGIN_LOW                     74
#define VOUT_TRANSITION_RATE                75
#define VOUT_DROOP                        76
#define VOUT_SCALE_LOOP                     77
#define VOUT_SCALE_MONITOR                  78
#define POUT_MAX                            79
#define MAX_DUTY                            80
#define FREQUENCY_SWITCH                  81
#define VIN_ON                              82
#define VIN_OFF                           83
#define INTERLEAVE                        84
#define IOUT_CAL_GAIN                     85
#define IOUT_CAL_OFFSET                     86
#define FAN_COMMAND_1                     87
#define FAN_COMMAND_2                     88
#define FAN_COMMAND_3                     89
#define FAN_COMMAND_4                     90
#define VOUT_OV_FAULT_LIMIT               91
#define VOUT_OV_WARN_LIMIT                  92
#define VOUT_UV_WARN_LIMIT                  93
#define VOUT_UV_FAULT_LIMIT               94
#define IOUT_OC_FAULT_LIMIT               95
#define IOUT_OC_LV_FAULT_LIMIT            96
#define IOUT_OC_WARN_LIMIT                  0x4A //97
#define IOUT_UC_FAULT_LIMIT               98
#define OT_FAULT_LIMIT                      99
#define OT_WARN_LIMIT                     0x51//100
#define UT_WARN_LIMIT                     101
#define UT_FAULT_LIMIT                      102
#define VIN_OV_FAULT_LIMIT                  103
#define VIN_OV_WARN_LIMIT                   104
#define VIN_UV_WARN_LIMIT                   105
#define VIN_UV_FAULT_LIMIT                  106
#define IIN_OC_FAULT_LIMIT                  107
#define IIN_OC_WARN_LIMIT                   108
#define POWER_GOOD_ON                     109
#define POWER_GOOD_OFF                      110
#define TON_DELAY                           111
#define TON_RISE                            112
#define TON_MAX_FAULT_LIMIT               113
#define TOFF_DELAY                        114
#define TOFF_FALL                           115
#define TOFF_MAX_WARN_LIMIT               116
#define POUT_OP_FAULT_LIMIT               117
#define POUT_OP_WARN_LIMIT                  118
#define PIN_OP_WARN_LIMIT                   119
#define MFR_ID                              0x99 //120 //New added command
#define MFR_MODEL                           0x9A //121
#define MFR_REVISION                        0x9B //122

void IIC_Init(void);

uint8_t pmbus_read_word(uint8_t cmd_code, uint8_t* buff);
uint8_t pmbus_read_byte(uint8_t cmd_code, uint8_t* buff);
uint8_t pmbus_write_byte(uint8_t cmd_code, uint8_t param);



从机程序只实现了部分PMBus指令,使用中断方法来处理PMBus通信,主要是以下几个事件的处理:

从机接收有关的事件:

I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:

主机要向从机发送数据,这里一般作一些初始化操作,例如:接收数据长度指针清零,数据接收缓冲初始化等等

I2C_EVENT_SLAVE_BYTE_RECEIVED:

从机接收到主机发送来的数据,从机此时一般的操作就是使用接收缓存保存所有数据

从机发送有关的事件:
I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:

主机读取从机数据,此时可以将数据发送指针清零,准备好要发送的数据缓冲并且发送第一个数据


I2C_EVENT_SLAVE_BYTE_TRANSMITTED:

从机发送的数据主机已收到并收到ACK,主机如果没有发送NACK则从机继续发送下一个数据

I2C_EVT_SLAVE_ACK_MISS:

主机在接收最后一个数据后会发送一个NACK,通知从机数据已全部接收完成,准备要发送STOP释放I2C总线,这里要做的就是清除这个中断标志位


I2C_EVENT_SLAVE_STOP_DETECTED:

收到主机发送来的停止信号,这里有两种情况:

1、如果主机向从机发送数据,这里表示从机接收数据完成了,此时从机可根据主机发送的寄存器信息及参数执行相应的操作;
2、如果主机读取从机数据,主机发送STOP信号之前会发送一个NACK,表示主机已最读取最后一个数据,从机不必要再发送数据

其实理解了I2C的通信时序后这些都很简单了,换了其他的单片机也是类似的原理。

之前使用TI提供的PMBus例程来设计从机程序,在使用N32G003上发现了如下几个问题:

1、在解析命令时,它每次都会使用for循环在指令表数组中搜索匹配指令,如果指令表数组很长这样会消耗很多时间;

2、如果程序要求PEC校验,使用之前的CRC8方法,即从机代码中的PMBusSlave_Crc8MakeBitwise函数,执行速度也会慢很多;

3、最终的结果就是:从机设置的速率为100Kbit/s,实际客户验证(使用模拟I2C)只能在低于14Kbit/s左右时才可正确读取数据,远远低于他们要求的60-80Kbit/s速率

于是对代码进行了如下优化:

1、修改函数PMBusSlave_DecodeCommand,只列出小部分需要的指令并准备好相应的数据,Linear16或Linear11格式,对于一些固定的字符串数据,例如:厂家信息,模块型号等,则直接使用单独的发送缓存并计算好PEC值;

2、由于N32G003系列RAM一般都较充足,CRC8使用了查表方法,对比测试通信速率大大提高;

3、通信出错机制除了TI提供的35毫秒超时重启,还有厂家推荐的出错机制,确保从机在出现异常时可以自行恢复。

主机端程序使用的是STM32F103系列,并且使用模拟方式(我们必须要模拟客户的真实使用环境),有以下要注意:

1、I2C_Delay()函数我测试过,如果宏DELAY_TIME设置为300,在STM32F103主频72MHz的情况下,函数的延时时间约为71微秒,用逻辑分析仪查看此时的I2C时钟频率约为14KHz;

2、IIC_GetACK函数必须要加超时出错机制,如果从机在指定时间内没有将SDA拉低,则认为通信有异常,主机必须结束此次通信;

3、主机程序中的pmbus_read_word写读函数也是和客户那边的软件工程师沟通得知是他们常用的方法,经测试时序完全没问题;

4、调整DELAY_TIME的值为50-60之间,实际观察到的频率约为70KHz,通信稳定。

以下是STM32F103主机使用的实例,通过USART接收上位机发送来的PMBus指令,通过I2C总线读写从机并将数据通过USART1返回给上位机。

main.c

/******************** (C) COPYRIGHT 2023 **************************
* @name: main.c
* @brief: RS485 and PMBus Communication
* @platform: STM32F103RBT6 Series MCU
* @library version: ST3.5.0
* @author: Power
**********************************************************************************/

#include "BSP.H"
#include "key.h"
#include "iic.h"
#include "usart1.h"

//TIM3 prescaler
#define TIM3_DIV1   (1-1)
#define TIM3_DIV18    (18-1)
#define TIM3_DIV72    (72-1)

#define I2C_Slave_Address   (0x54)

uint16_t led_cnt = 0;//LED test
volatile uint8_t Flag_1ms;      //1 milli-second overflow flag
volatile uint16_t cDelay_1Ms;   //80 milli-seconds counter
volatile struct UART_Data_Struct UART1_STRUCT;//USART interface

uint16_t i2c_time = 0;
uint8_t pmbus_buff = {0, 0, 0};
uint16_t no_data_error = 0;
uint8_t key = 0xFF;

//*************************************
// 函数名:TIM3_NVIC_Configuration
// 描述:TIM3中断优先级配置
// 输入:无
// 输出:无
//*************************************
void TIM3_NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/**
*@name: TIM3_BASEInitSys
*@description: timer3 initialization
*@params: none
*@return: none
*/
void TIM3_Configuration(int16_t tcon, uint16_t psc)
{
    TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    TIM_DeInit(TIM3);
    TIM_TimeBaseStructure.TIM_Period = tcon;//period
    TIM_TimeBaseStructure.TIM_Prescaler = psc ;         //prescaler
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //counter mode
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    TIM_ClearFlag(TIM3, TIM_FLAG_Update);
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM3, ENABLE);
}

/**
*@name: Process_1MS
*@description: 1 milli-second overflow process
*@params: none
*@return: none
*/
static void Process_1Ms(void)
{
    if (Flag_1ms == 0)
    {
      return;
    }

    Flag_1ms = 0;
    if (++i2c_time >= 300)
    {
      i2c_time = 300;
    }
    if (++no_data_error > 1000) no_data_error = 1000;
    if (++led_cnt >= 500) //LED Toggle Test
    {
      led_cnt = 0;
      GPIO_WriteBit(GPIOB, GPIO_Pin_0, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0)));
    }
}

void Key_Process(void)
{
    //key = KEY_Scan(1);
    key = Key_Scan(GPIOC, GPIO_Pin_13);
    if (key == 1)
    {
      key = 0;
      //can_id_index++;
      //if (++can_id_index > 6)
      //{
      //can_id_index = 1;
      //}
      //AlarmStatus.alarm.KEY_ERASE_PRESSED = 1;
      //AlarmStatus.alarm.CLONE_DONE = 0;
      //Copy_Data_Process();
    }
}

/**
*@name: Parse_PMBus_Command
*@description: Parse the PMBus command from the UART interface and send the command to the PMBus slave
*@params: none
*@return: none
*/
static uint8_t Parse_PMBus_Command(uint8_t command, uint8_t param)
{
    uint8_t result = IIC_ERROR;
    switch (command)
    {
      case STATUS_BYTE://read byte
      case CAPABILITY:
      case STATUS_VOUT:
      case STATUS_IOUT:
      case STATUS_INPUT:
      case STATUS_MFR_SPECIFIC:
      case STATUS_TEMPERATURE:
      case PMBUS_REVISION:
      case VOUT_MODE:
            result = pmbus_read_byte(command, pmbus_buff);
            break;

      case STATUS_WORD://read word
      case READ_VIN:
      case READ_IIN:
      case READ_VOUT:
      case READ_IOUT:
      case READ_TEMPERATURE_3:
      case READ_POUT:
      case READ_PIN:
      case MFR_VIN_MIN:
      case MFR_VIN_MAX:
      case MFR_IIN_MAX:
      case MFR_PIN_MAX:
      case MFR_VOUT_MIN:
      case MFR_VOUT_MAX:
      case MFR_IOUT_MAX:
      case MFR_POUT_MAX:
      case MFR_TAMBIENT_MAX:
      case MFR_TAMBIENT_MIN:
      case VOUT_COMMAND:
      case IOUT_OC_WARN_LIMIT:
      case OT_WARN_LIMIT:
            result = pmbus_read_word(command, pmbus_buff);
            break;

      case MFR_ID://block read, NOT processed here
      case MFR_MODEL:
      case MFR_REVISION:
            result = IIC_OK;
            break;

      case PAGE://write byte
                        result = pmbus_write_byte(command, param);
            break;

      case CLEAR_FAULTS://clear the error flags
            break;

      default:
            result = IIC_ERROR;
            break;
    }

    return result;
}



/**
*@name: PMBus_Process
*@description: Process the data received from the upper machine
*@params: none
*@return: none
*/
void PMBus_Process(void)
{
    uint8_t i;
    uint8_t counter = 0;
    if (UART1_STRUCT.R_Flag == 1) //send out the command received from UART to the PMBus slave
    {
                uint8_t result = Parse_PMBus_Command(UART1_STRUCT.R_Buffer, UART1_STRUCT.R_Buffer);
      if (result == IIC_OK)
      {
                        //0x7E 0xD0 0xEA 0xEA 0x00 0x0D, for example
            UART1_STRUCT.T_Buffer = UART_START;
                        UART1_STRUCT.T_Buffer = UART1_STRUCT.R_Buffer;//the command
            for (i = 0; i < 3; i++)
            {
                UART1_STRUCT.T_Buffer = pmbus_buff;
            }
                        UART1_STRUCT.T_Buffer = result;
            UART1_STRUCT.T_Buffer = UART_STOP;
      }
      else
      {
            UART1_STRUCT.T_Buffer = UART_START;
                        UART1_STRUCT.T_Buffer = UART1_STRUCT.R_Buffer;
            for (i = 0; i < 3; i++)
            {
                UART1_STRUCT.T_Buffer = 0xEE;
            }
                        UART1_STRUCT.T_Buffer = result;
            UART1_STRUCT.T_Buffer = UART_STOP;
      }

      USART1_SendData((uint8_t*)UART1_STRUCT.T_Buffer, counter);//send the data back to the upper machine, total 6 bytes

      UART1_STRUCT.R_Flag = 0;
    }
}

/**
@* name:main
@* description: main process
@* The hardware IC2 slave mode has been proved correct, and now
    we are testing the PMBus slave.
@* input: none
@* output: none
*/
int main(void)
{
    BSP_Init();
    IIC_Init();
    SysTick_Init();
    TIM3_Configuration(1000, TIM3_DIV72) ; //72M/72 = 1M 1m中断一次
    TIM3_NVIC_Configuration();
    while (1)
    {
      Process_1Ms();      
      PMBus_Process();
    }
}



usart1.c

#include "usart1.h"

extern uint8_t send_buffer;
extern uint8_t Tx_flag;
volatile uint8_t Rx_ptr = 0;
volatile uint8_t UART_Rx_flag = 0;

extern volatile struct UART_Data_Struct UART1_STRUCT;//USART interface

/**
@* 函数名:USART1_Config
@* 描述:USART1 GPIO 配置,工作模式配置。9600 8-N-1
@* 输入:无
@* 输出: 无
@* 调用:外部调用
*/
void USART1_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* config USART1 clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    /* USART1 GPIO config */
    /* Configure USART1 Tx (PA.09) as alternate function push-pull */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* Configure USART1 Rx (PA.10) as input floating */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* USART1 mode config */
    USART_InitStructure.USART_BaudRate = 9600;//115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART1, &USART_InitStructure);

    //中断优先级:
    /* Configure the NVIC Preemption Priority Bits */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

    // Enable the USART1 Interrupt
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开串口1接收中断
    USART_Cmd(USART1, ENABLE);
}

void USART1_Data_Receive(void)
{
        uint8_t data = USART_ReceiveData(USART1);
    if (UART1_STRUCT.R_Flag) return;

    if (UART1_STRUCT.R_InPtr == 0 && data != UART_START) {
      return;
    }

    if (UART1_STRUCT.R_InPtr < RECEIVE_DATA_LEN) {
      UART1_STRUCT.R_Buffer = data;
    }

    if (UART1_STRUCT.R_InPtr >= RECEIVE_DATA_LEN) {
      if (UART1_STRUCT.R_Buffer == UART_START &&
                UART1_STRUCT.R_Buffer == UART_STOP) {
            UART1_STRUCT.R_Flag = 1;
      }
      UART1_STRUCT.R_InPtr = 0;
    }
}

/**
@* 函数名:USART1_IRQHandler
@* 描述:串口1中断接收到数据后保存在数据缓冲区
@* 输入:无
@* 输出:无
@* 注意:中断处理函数除了在stm32f10x_it.c中还可以放在其他位置,只要名称不重复
*/
void USART1_IRQHandler(void)
{
    uint8_t cTemp = cTemp;

    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//接收到数据
    {
      USART_ClearITPendingBit(USART1, USART_IT_RXNE);   //清除中断标志位,以免重复进入中断
      USART1_Data_Receive();
    }

    //溢出-如果发生溢出需要先读SR,再读DR寄存器 则可清除不断入中断的问题
    if (USART_GetFlagStatus(USART1, USART_FLAG_ORE) == SET)
    {
      USART_ClearFlag(USART1, USART_FLAG_ORE);//读SR
      USART_ReceiveData(USART1);//读DR
    }
}

/**
@* 函数名:USART1_SendData
@* 描述: 利用串口1发送数据,主要是调试用
@* 输入:data:要发送的数据
@*         len:要发送数据长度
@* 输出: 无
*/
void USART1_SendData(uint8_t* data, uint8_t len)
{
    while (len > 0) //开始发送数据
    {
      while (RESET == USART_GetFlagStatus(USART1, USART_FLAG_TC));
      USART_SendData(USART1, *data++);
      len--;
    }
    while (RESET == USART_GetFlagStatus(USART1, USART_FLAG_TC)); //等待发送完成*/

    //u8 i=0;
    //for(i=0;i<len;i++)//开始发送数据
    //{
    //      while(RESET == USART_GetFlagStatus(USART1, USART_FLAG_TXE));
    //      USART_SendData(USART1, data);
    //}
    //while(RESET == USART_GetFlagStatus(USART1, USART_FLAG_TXE));//等待发送完成*/
}



usart1.h

#ifndef __USART1_H
#define __USART1_H

#include "stm32f10x.h"
#include <stdio.h>
#include <stdarg.h>

/*串口1初始化*/
void USART1_Config(void);

/*串口发送数据*/
void USART1_SendData(u8* data, u8 len);

int fputc(int ch, FILE* f);

/*重定向系统的printf方法*/
void USART_printf(USART_TypeDef* USARTx, uint8_t* Data, ...);

#define RECEIVE_DATA_LEN    4      //接收数据最大长度
#define TRANSMIT_DATA_LEN   10   //发送数据最大长度

/*
======================================================
            起始位和结束位及一些固定值定义
======================================================
*/
#define   UART_START               0x7E    //固定起始位
#define   UART_STOP                  0x0D    //固定结束位


struct        UART_Data_Struct
{
    uint8_t R_Buffer; //接收缓冲
    uint8_t R_Length;                   //接收数据的总长度
    uint8_t R_InPtr;                  //已接收的数据长度
    uint8_t R_Flag;                     //接收是否完成标志
    uint8_t R_DTSime;                   //发送超时处理,当需要给主机发送数据时会启用

    uint8_t T_Buffer;//发送缓冲
    uint8_t T_Length;                   //发送数据总长度,在使用串口发送时的数据长度
    uint8_t T_OutPtr;                   //正在发送数据的指针
    uint8_t T_Flag;                     //发送是否完成标志
    uint8_t T_DTSime;                   //发送超时处理,如果在指定时间内没有发送,则证明有问题,重新初始化串口

    uint8_t T_Time;
    uint8_t Frame_Buffer;//以下几个成员暂未使用
    uint8_t Frame_InPtr;
    uint8_t Frame_OutPtr;
    uint8_t Frame_Length;
};

#endif /* __USART1_H */


————————————————
版权声明:本文为CSDN博主「名人堂再聚首」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jmmx/article/details/149960999

页: [1]
查看完整版本: 国民技术N32G003实现PMBus从机及使用STM32F103模拟I2C主机访问从机