嵌入式控制器EC(Embedded controller)源码解析:Main()启动流程
总览:从复位到主循环
实际上 main() 之前还有一段汇编启动代码,完整链路是:芯片复位 → STARTUP.A51(汇编) → main()(CORE_MAIN.C) → while(1) 主循环。
1.Oem_StartUp() (main()之前)
主要作用是在 C 运行时之前,先初始化键盘扫描硬件,并检测 电源键 + Home 键 是否触发固件 Mirror 恢复。
void Oem_StartUp(void)
{
/* Sample Code: Power,Vol Up/Dn 3 buttons down trigger mirror code. */
#if 0 /* 1:Support "3 Buttons" entry recovery mirror function */
if (IS_EC_PWRSW_DN())
{
if (IS_VOLUME_UP_DN() && IS_VOLUME_DOWN_DN())
{
FLHCTRL3R = 0x28;
//FLHCTRL3R = 0x48; /* Force Mirror active (Max. sizes) */
ETWCFG = EWDKEYEN; /* Force Watchdog active */
EWDCNTLLR = 0x01; // for 1ms counter
EWDKEYR = 0x5A; // External WDTreset not match
/* if FLHCTRL3R=0x28 --> Force Mirror active after WDT */
}
}
#endif
/* Sample Code: Power, Matrix-[Home] Key down trigger mirror code. */
#if 1
KSOCTRL = KSOOD + KSOPU;
KSICTRL = KSIPU;
KSOL = 0xFF;
KSOH1 = 0xBF;
WNCKR = 0x00;
while (IS_EC_PWRSW_DN())
{
if (KSI == 0xFD) //Home Key = KSO14,KSI1
{
ITempB01++;
if (ITempB01 == 0)
{
FLHCTRL3R = 0x28;
//FLHCTRL3R = 0x48; /* Force Mirror active (Max. sizes) */
ETWCFG = EWDKEYEN; /* Force Watchdog active */
EWDCNTLLR = 0x01; // for 1ms counter
EWDKEYR = 0x5A; // External WDTreset not match
/* if FLHCTRL3R=0x28 --> Force Mirror active after WDT */
}
}
else
{
break;
}
}
#endif
}
2.main() 前半段 — 硬件底层配置
主要有关中断、设置栈指针SP、设置ESPI电压和频率、确认与BIOS共享Flash空间等
void main(void)
{
DisableAllInterrupt();
SP = 0xC0;
ResetBANKDATA();
/* eSPI 初始化(本项目 SUPPORT_INTERFACE_eSPI = TRUE) */
SCRA0H = 0x07; // 清 Scratch SRAM
GCR22 |= VCCPDS; // eSPI 总线 1.8V
REG_3105 = _eSPI_MAX_FREQ_66MHZ; // eSPI 66MHz
ChangePLLFrequency(_PLL_FREQ_32_3_MHZ); // PLL 32.3MHz
REG_3115 = _SUPPORT_MAFS; // Flash Access Channel
// EC 间接寻址初始化
ECINDAR3~0 = 0;
ITempB01 = ECINDDR;
Hook_Before_SpiTriState();
// Mirror 一次性标记处理
// ...
// SPI Flash 引脚设为 Tri-State(与 BIOS 共享 Flash)
SET_MASK(FLHCTRL3R, BIT(0));
3.分支判断 — 正常启动或Follow Mode 恢复
if (Hook_ECRetunrMainFuncKeepCondition() == 0x33)
{
// 【分支 A】从 Follow Mode / Scratch ROM 退出(异常恢复路径)
CLEAR_MASK(FBCFG,SSMC);
Hook_ECExitFollowMode();
Init_Timers();
EnableModuleInterrupt();
}
else
{
// 【分支 B】正常冷启动(绝大多数情况走这里)
Core_Initialization();
Oem_Initialization();
InitEnableInterrupt();
}
正常开机走分支B。
4.Core_Initialization() — 内核初始化
void Core_Initialization(void)
{
Init_Kbd(); // 键盘控制器硬件初始化
cold_reset(); // 冷启动变量初始化
common_reset(); // 通用复位(清 Service 标志等)
Init_Mouse(); // 鼠标接口(本项目未用)
Init_Timers(); // 定时器硬件初始化(1ms 中断源)
Core_Init_Regs(); // 内核寄存器表初始化
Core_Init_SMBus(); // SMBus 硬件初始化
ESGCTRL1 = 0x80; // eSPI 中断使能
ESGCTRL2 = 0x10; // eSPI → WUC 唤醒使能
Init_EC_Indirect_Selection(SPI_selection_internal);
}
cold_reset() 清什么?
void cold_reset(void)
{
Ext_Cb2.byte = EXT_CB2_INIT;
Save_Typematic = SAVE_TYPEMATIC_INIT;
Flag.SCAN_INH = 1; // 暂时禁止键盘扫描
Flag.VALID_SCAN = 0;
// ... 键盘/KBC 相关状态清零
}
Core_Init_Regs() 配什么?
void Core_Init_Regs(void)
{
BYTE index = 0x00;
while(index < (sizeof(Core_reg_init_table)/sizeof(sREG_INIT)))
{
Tmp_XPntr = Core_reg_init_table[index].address;
*Tmp_XPntr = Core_reg_init_table[index].initdata;
index ++;
}
CLEAR_MASK(FLHCTRL2R,BIT3);
}
通过 Core_reg_init_table[] 批量写寄存器,主要包括:Host 接口;PS/2;键盘扫描;
5.Oem_Initialization() — 项目初始化
void Oem_Initialization(void)
{
Init_SMBus(); // SMBus 通道时钟/引脚(电池/充电器通信)
Init_GPIO(); // 全部 GPIO 方向/默认值(电源轨控制引脚)
Init_PWM(); // PWM 初始化(风扇)
Init_ADC(); // ADC 初始化(适配器检测等)
Init_Kbd(); // OEM 键盘配置
Init_WDT(); // 看门狗
Reload_EC_Ver(); // 加载 EC 版本号
// 清 BRAM 备份区
for (...) BRAM[...] = 0;
for (...) BRAM2[...] = 0;
xEC_PowerOnFirst = 1;
// 等 500ms,等 3.3V 电源稳定
DelayXms(250);
DelayXms(250);
while(IS_V3P3A_PGOOD_LO())
{
if(IS_EC_PWRSW_DN())
_nop_();
else
EC_PWR_LATCH_LO(); // 没按电源键就关掉电源锁存
}
}
要点:
1.按顺序初始化所有外设(SMBus → GPIO → PWM → ADC → 键盘)
2.等待 500ms + V3P3A_PGOOD 电源好信号
3.若电源键未按下,会拉低 EC_PWR_LATCH 防止误上电
4.初始化结束后,电源时序还没跑——那要等主循环里 Hook_Timer1msEvent 触发
6.InitEnableInterrupt() — 开中断
使能各模块中断,包括:1ms 定时器中断(最重要,驱动整个调度体系);eSPI 中断;键盘扫描中断;SMBus 完成中断。
从此刻起,1ms 定时器开始滴答,Hook 函数开始被周期性调用。
7.while(1) 主循环 — 永不返回
while(1)
{
if(OEM_SkipMainServiceFunc()==Normal_MainService)
{
main_service(); // 处理所有 Service 请求
EnableModuleInterrupt();
// 无 Service 时进入 idle 省电
if((Service==0x00)&&(Service1==0x00))
{
PCON = 1; // 进入 idle,等中断唤醒
_nop_(); _nop_(); ... // 8 个 nop 总线翻转延时
}
}
}
┌─────────────────────────────────────┐
│ while(1) 主循环 │
│ │
│ 1. main_service() 检查 Service 标志 │
│ ├─ F_Service_MS_1 → service_1mS() → Hook_Timer1msEvent
│ ├─ F_Service_KEY → service_scan() → 键盘扫描
│ ├─ F_Service_PCI → service_pci1() → eSPI Host 命令
│ └─ … 其他 Service │
│ │
│ 2. 所有 Service 处理完? │
│ ├─ 是 → PCON=1 进入 idle 省电 │
│ └─ 否 → 继续处理 │
│ │
│ 3. 中断唤醒 → 回到步骤 1 │
└─────────────────────────────────────┘
main_service() 优先级(从高到低)
void main_service(void)
{
while(Service != 0 || Service1 != 0)
{
// eSPI Boot Ready / Warm Boot 处理
if(F_Service_PCI) → service_pci1(); // Host 命令
if(F_Service_UNLOCK) → service_unlock();
if(F_Service_SEND) → service_send(); // KBC 发送
if(F_Service_MS_1) → service_1mS(); // ★ 1ms 定时器
if(F_Service_PCI2) → service_pci2();
if(F_Service_KEY) → service_scan(); // ★ 键盘扫描
Hook_main_service_H();
if(F_Service_Low_LV) → service_Low_LVEvent();
// ... service_pci3/4, service_OEM_1~4
Hook_main_service_L();
}
}
其中 service_1mS() 最重要,它驱动整个定时器分频树,最终调用 Hook_Timer1msEvent() → 电源管理、SMBus 轮询等。
完整启动时序图
[芯片复位]
│
▼
STARTUP.A51
├─ SP=0xC0, Bank0
├─ Oem_StartUp() ← Mirror检测、键盘早期初始化
├─ Core_Init_ClearRam() ← 清 0x000~0x0FF
├─ Init_ClearRam() ← 清 0x100~0xFFF
├─ CheckResetSource()
│
▼
main() ─────────────────────────────────────────────
├─ DisableAllInterrupt()
├─ eSPI 1.8V + 66MHz + PLL 32.3MHz
├─ Flash Tri-State
│
├─ Core_Initialization()
│ ├─ Init_Kbd()
│ ├─ cold_reset() / common_reset()
│ ├─ Init_Timers() ← 1ms 定时器就绪
│ ├─ Core_Init_Regs() ← KBC/PM/eSPI 寄存器
│ └─ Core_Init_SMBus()
│
├─ Oem_Initialization()
│ ├─ Init_SMBus/GPIO/PWM/ADC/Kbd/WDT
│ ├─ Reload_EC_Ver()
│ └─ 等 500ms + V3P3A_PGOOD
│
├─ InitEnableInterrupt() ← ★ 中断开启,系统"活"了
│
▼
while(1) 主循环
├─ main_service()
│ ├─ service_1mS() → Hook_Timer1msEvent()
│ │ ├─ 电源状态分支 (S0/S3/S5/G3)
│ │ ├─ Oem_SysPowSequence() (若 F_PST_BUSY)
│ │ └─ SMBusA/B 轮询
│ ├─ service_scan() → 键盘扫描
│ └─ service_pci1() → Host eSPI 命令
│
└─ PCON=1 idle → 等中断 → 循环
启动后第一件事是什么?
中断开启后,1ms 定时器开始触发 Hook_Timer1msEvent():
void Hook_Timer1msEvent(IBYTE EventId)
{
// 根据 SysPowState 走不同分支
if (SysPowState == S0/S1) → Oem_SysPowState_S0_S1();
if (SysPowState == S3) → Oem_SysPowState_S3();
if (SysPowState == S4/S5) → Oem_SysPowState_S4_S5();
if (F_PST_BUSY) → Oem_SysPowSequence(); // 电源时序
else → SMBus 轮询 + ADP_Control();
刚启动时 SysPowState 通常是 S5,所以一开始走 Oem_SysPowState_S4_S5()。用户按电源键后,F_PST_BUSY 置位,开始执行 TGL_sSEQ_S5_to_S0[] 上电时序表,最终进入 S0。
更多推荐

所有评论(0)