SMBus介绍及移植指南

415 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

简介

EC里的SMbus主要有下面两个作用,一是进行CPU、GPU等的温度读取与控制,二是对智能电池的相关信息进行读取。另外SMBus也会把相关信息上报给操作系统,所以SMBus的作用非常重要,本文主要介绍下SMBus的基本概念以及移植方法。

一、SMBus介绍

SMBus分为主机和从机。

SMBus主机接口

当检测到INTC中断(设计A、B、C、D、E、F分别为INT9、INT10、INT16、INT4、INT160、INT161)时,软件可以通过读取Host Status Register来了解中断源。有5种中断条件:字节完成、失败、总线错误、设备错误和完成。

SMBus从机接口

slave支持以下三种消息类型:Byte Write、Byte Read和Host Notify。当检测到INTC中断(设计A、B、C分别为INT9、INT10、INT16)时,软件可以读取Slave Status Register来了解中断源。有4种中断条件:STOP Condition Detect Status、Slave Timeout Status、Slave Data Status和Host Notify Status。在“Byte Write/Byte Read”命令中,软件必须在从数据寄存器中写/读两次数据,才能释放SMCLK行。第一次,软件在从机数据寄存器中设置/获取数据,但SMCLK线不会被释放。SMCLK线将被保持在低位,直到软件第二次在从数据寄存器中读写数据,之后SMCLK线将被释放。

二、移植指南

编写步骤

下面是软件为各种命令编写寄存器的步骤。

  1. 快捷命令
  • 使能SMBus主机控制器(主机控制寄存器2中的SMBus Host Enable位设置为1)。该命令中I2C_EN位必须为0。
  • 在快速命令中,发送发送从地址寄存器(软件需要将数据写入发送从地址寄存器)。
  • 启动事务(向主机控制寄存器写入41h,选择“Quick Command”,启用中断,启动事务)。
  • 当数据传输完成时,会产生一个中断。软件可以读取主机状态寄存器来知道中断的来源。 注意:读取状态寄存器后,软件必须写入1以清除状态寄存器。
  1. 发送字节命令
  • 使能SMBus主机控制器(主机控制寄存器2中的SMBus Host Enable位设置为1)。该命令中I2C_EN位必须为0。
  • 在发送字节命令中,发送发送从地址寄存器和主机命令寄存器(软件将数据写入发送从地址寄存器和主机命令寄存器)(主机命令寄存器用于发送数据)。发送从地址寄存器的第0位必须是0。
  • 启动事务(向主机控制寄存器写入45h,它将选择“发送字节/接收字节命令”,启用中断,并启动事务)。
  • 当数据传输完成时,会产生一个中断。软件可以读取主机状态寄存器来知道中断的来源。

流程示例

下面是主机接口发送Byte的流程

屏幕截图 2022-05-27 123909.png


BYTE bSMBusSendByte(BYTE Channel,BYTE Addr,BYTE SData,BYTE PECSupport)
{
    BYTE error;
    BYTE result;
    BYTE status;
    if(1)
    {
        error = 0xEE;                               // Pre-set error
        result = FALSE;                             // Pre-set result is fail
        SMBCRC8_A=0x00;                             // Clear CRC variable

        *asSMBus[Channel].SMBusADR = Addr;          // set address with writing bit
        *asSMBus[Channel].SMBusCMD = SData;         // Set command

        if(PECSupport)
        {
            CalcCRC8(Addr);                         // To calculate crc8
            CalcCRC8(SData);                        // To calculate crc8
        }

        *asSMBus[Channel].SMBusSTA = 0xFE;          // clear bits
        if(PECSupport)
        {
            *asSMBus[Channel].SMBusPEC = SMBCRC8_A;
            *asSMBus[Channel].SMBusCTL = (0x04|HOCTL_SRT|HOCTL_PEC_EN);   // Start transaction
        }
        else
        {
            *asSMBus[Channel].SMBusCTL = (0x04|HOCTL_SRT);  // Start transaction
        }
        
        TR1 = 0;                                    // Disable timer1
        ET1 = 0;                                    // Disable timer1 interrupt
        _nop_();

        TH1 = Timer_26ms>>8;                        // Set timer1 counter 26ms
        TL1 = Timer_26ms;                           // Set timer1 counter 26ms
        TF1 = 0;                                    // Clear overflow flag
        TR1 = 1;                                    // Enable timer1

        while (!TF1)
        {
            status = *asSMBus[Channel].SMBusSTA;    // Read SMBus Host Status
            if(IS_MASK_SET(status, (HOSTA_FINTR+HOSTA_DVER+HOSTA_BSER+HOSTA_FAIL+HOSTA_NACK+HOSTA_TMOE )))
            {
                TR1=0;                              // Disable timer1
                TF1=0;                              // Clear overflow flag
                ET1=1;                              // Enable timer1 interrupt
                break;
            }
        }

        if(TF1)                                     // 26ms time-out and no any status bit is set.
        {
            TR1=0;
            TF1=0;
            ET1=1;                                  // Enable timer1 interrupt
            ResetSMBus(Channel);
            error = 0xEE;
        }
        else
        {
            if(IS_MASK_SET(status, (HOSTA_DVER+HOSTA_BSER+HOSTA_FAIL+HOSTA_NACK+HOSTA_TMOE)))
            {
                if(IS_MASK_SET(status, (HOSTA_BSER+HOSTA_FAIL)))
                {
                    ResetSMBus(Channel);
                }
                error = 0xEE;
            }
            else                                    // Only Finish Interrupt bit is set.
            {
                error = 0x00;
            }
        }

        if(error == 0xEE)
        {
            result = FALSE;
        }
        else
        {
            result = TRUE;
        }
        *asSMBus[Channel].SMBusSTA=0xFE;    // clear bits
    }

    return(result);
}

三、完结

本文结束。