- uart通用异步传输器(异步通信串口)
由于FW环境下debug手段有限,因此大多fw会采用uart手段debug。uart包含rs232,rs485等总线接口规范,是一种广泛应用的低速通信协议。uart的debug只需要RX、TX和GND既可。串口读取数据发送至内部fifo通道,FIFO通道满数据后会逐帧发送bit送往总线。波特率常见有300,1200,2400,9600,19200,38400,115200等。
uboot的printf代码:
#define CONFIG_IS_ENABLED(option, ...) \
__concat(__CONFIG_IS_ENABLED_, __count_args(option, ##__VA_ARGS__)) (option, ##__VA_ARGS__)
static int vsnprintf_internal(char *buf, size_t size, const char *fmt,
va_list args)//解析fmt字符并导入buf
{
u64 num;
int base;
char *str;
int flags; /* flag计数 */
int field_width; /* 输出字符宽度 */
int precision; /* 整数的最小位数;来自字符串的最大字符数 */
int qualifier; /*'h', 'l',或'L'用于整数字段 */
/* 'z' support added 23/7/1999 S.H. */
/*增加了'z'支持23/7/1999 S.H. */
/*为ptrdiff_t添加't'*/
char *end = buf + size;//设置结尾
/* 确保end总是>= buf -我们想在U-Boot中这样吗? */
if (end < buf) {//如果超出长度
end = ((void *)-1);//end为NULL
size = end - buf;//重置大小为负数
}
str = buf;//指向数组头部
for (; *fmt ; ++fmt) {
if (*fmt != '%') {
ADDCH(str, *fmt);//如果在end范围之内,fmt不为%则赋值跳转下个字符并跳出本次循环
continue;
}
/* 过程flag */
flags = 0;
repeat:
++fmt; /*这也会首先跳过'%' */
switch (*fmt) {//判断格式输出符号条件
case '-':
flags |= LEFT;//宏定义标志位值16(左对齐)
goto repeat;
case '+':
flags |= PLUS;//4(输出符号)
goto repeat;
case ' ':
flags |= SPACE;/8(输出为正是空格,负数为负号)
goto repeat;
case '#':
flags |= SPECIAL;//64(对c,s,d,u类无影响;对o类,在输出时加前缀0;对x类,在输出时加前缀0x或者0X;对g,G 类防止尾随0被删除 对于所有的浮点形式,#保证了即使不跟任何数字,也打印一个小数点字符)
goto repeat;
case '0':
flags |= ZEROPAD;//1( 对于所有的数字格式,用前导0填充字段宽度,若出现-标志或者指定了精度(对于整数),忽略)
goto repeat;
}
//没有选择退出循环
/* 获取字段宽度 */
field_width = -1;
if (is_digit(*fmt))//判断字符串为数字
field_width = skip_atoi(&fmt);//循环判断并转化为数字
else if (*fmt == '*') {//有则跳过该形参,转为下一个形参
++fmt;
/*这是下一个论点*/
field_width = va_arg(args, int);
if (field_width < 0) {//将数字转正
field_width = -field_width;
flags |= LEFT;//从左往右输出
}
}
/*确认精度*/
precision = -1;
if (*fmt == '.') {//如果有小数点
++fmt;
if (is_digit(*fmt))//计算精度
precision = skip_atoi(&fmt);
else if (*fmt == '*') {//跳过该形参
++fmt;
/*这是下一个论点 */
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
/* 获取转换限定符 */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
*fmt == 'Z' || *fmt == 'z' || *fmt == 't') {
qualifier = *fmt;
++fmt;
if (qualifier == 'l' && *fmt == 'l') {//如果是ll则相当于L
qualifier = 'L';
++fmt;
}
}
/* 默认的基础*/
base = 10;//十进制
switch (*fmt) {
case 'c'://字符
if (!(flags & LEFT)) {//如果左对齐
while (--field_width > 0)
ADDCH(str, ' ');//拼接空格
}
ADDCH(str, (unsigned char) va_arg(args, int));//拼接字符
while (--field_width > 0)
ADDCH(str, ' ');//拼接字符
continue;
case 's':
/* U-Boot仅在EFI上下文中使用UTF-16字符串。*/
#if (CONFIG_IS_ENABLED(EFI_LOADER) || CONFIG_IS_ENABLED(EFI_APP)) && \
!defined(API_BUILD)//判断启用EFI装载或者EFI应用并且未定义API构建
if (qualifier == 'l') {//utf16字符串拼接
str = string16(str, end, va_arg(args, u16 *),
field_width, precision, flags);
} else
#endif
{
str = string(str, end, va_arg(args, char *),//utf8字符串拼接
field_width, precision, flags);
}
continue;
case 'p'://地址
str = pointer(fmt + 1, str, end,
va_arg(args, void *),
field_width, precision, flags);
if (IS_ERR(str))//判断是否错误
return PTR_ERR(str);
/* 跳过所有字母数字指针后缀 */
while (isalnum(fmt[1]))//判断字符类型
fmt++;
continue;
case 'n'://统计输出字符数
if (qualifier == 'l') {
long *ip = va_arg(args, long *);
*ip = (str - buf);
} else {
int *ip = va_arg(args, int *);
*ip = (str - buf);
}
continue;
case '%'://百分号
ADDCH(str, '%');
continue;
/*整数格式——设置标志和“break” */
case 'o'://输出八进制
base = 8;
break;
case 'x'://输出小16进制
flags |= SMALL;
case 'X'://输出16进制
base = 16;
break;
case 'd'://输出10进制
case 'i'://输出10进制
flags |= SIGN;//有符号
case 'u'://无符号
break;
default://否则当做非格式化字符输出
ADDCH(str, '%');
if (*fmt)
ADDCH(str, *fmt);
else
--fmt;
continue;
}
if (qualifier == 'L') /*"quad"表示64位变量 *//判断形参大小(偏移地址)
num = va_arg(args, unsigned long long);
else if (qualifier == 'l') {
num = va_arg(args, unsigned long);
if (flags & SIGN)
num = (signed long) num;
} else if (qualifier == 'Z' || qualifier == 'z') {
num = va_arg(args, size_t);
} else if (qualifier == 't') {
num = va_arg(args, ptrdiff_t);
} else if (qualifier == 'h') {
num = (unsigned short) va_arg(args, int);
if (flags & SIGN)
num = (signed short) num;
} else {
num = va_arg(args, unsigned int);//返回下一个的值
if (flags & SIGN)//判断符号
num = (signed int) num;
}
str = number(str, end, num, base, field_width, precision,
flags);//得到相应数值
}
if (size > 0) {
ADDCH(str, '\0');//输出结束
if (str > end)
end[-1] = '\0';
--str;
}
/* 后面的空字节不计入总数 */
return str - buf;
}
int vsnprintf(char *buf, size_t size, const char *fmt,
va_list args)
{
return vsnprintf_internal(buf, size, fmt, args);//返回大小
}
int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
int i;
i = vsnprintf(buf, size, fmt, args);//把要存放的数组,大小,原字符串,可变形参传入
if (likely(i < size))//宏定义likely(x),i<size很可能成立,成立为1执行if,unlikely(x)的x很可能不成立,大概率为0执行else
return i;
if (size != 0)
return size - 1;//如果size不等于0则返回size-1
return 0;
}
#if CONFIG_IS_ENABLED(PRINTF)//如果配置启用printf
int printf(const char *fmt, ...)
{
va_list args;//创建可变形参列表
uint i;
va_start(args, fmt);//初始化
i = vprintf(fmt, args);//计数,调用vprintf
va_end(args);//关闭列表
return i;
}
int vprintf(const char *fmt, va_list args)//传递可变形参列表和fmt
{
uint i;
char printbuffer[CONFIG_SYS_PBSIZE];//创建临时变量存储
/*
* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);//将结果
/* Handle error */
if (i <= 0)
return i;//假设返回错误则返回,否则输出
/* Print the string */
puts(printbuffer);//根据芯片配置决定输出方向(stdout/引脚)
return i;
}
#endif
- SPI串行外围总线
Serial Peripheral Interface Bus串行外围总线由摩托罗拉公司自动的四线全双工同步串行数据通行标准,传输速率比较高能达到几Mbps。采用一主多从模式。硬件SPI有四根总线分别为:MISO(SDI)、MOSI(SDO),SCLK(SPICLK/SCK)、SPICS#(SCS#/CS#).其中SCK为时钟信号,CS#为片选信号,MISO为主入从出、MOSI为主出从入。笔记本电脑的常用BIOS ROM通过SPI接在南桥或者EC,为单对单模式,有可能采用w25xxx flash rom。
从机设定指令方便操作,如:读数据、写数据、擦除数据等,如w25x80的spec说明书定义:写使能、写禁用、读状态寄存器、写状态寄存器、读数据、快速读取、页写入、扇区擦除、块擦除、片擦除、断电、释放断电、读配置、读ID。对flash操作常用刷BIOS、而刷BIOSspec规定要擦除置位1,因为已写入的部分有些无法单独改变(0值)只能区擦除。有时候无法排挤需要读取ROM判断错误原因。(操作可以参考W25Q64/08/16或者EEPROM的AT24C02/04/08).
EC芯片有一个索引输入输出一说,这个东西被BIOS称为后门。一旦芯片组和EC打开这个功能以后,BIOS就能通过后门随便访问EC寄存器和内存,且EC丝毫不知情。
- Deep Sleep Mode深度睡眠
深度睡眠能让电池省电,达到更长续航的目的。因为S4、S5状况下只有电池则会ECFW会备份寄存器内容清除等待标志关闭中断设置唤醒中断源以后让51内核进入深度睡眠模式此时能耗极地,直到外部事件触发继续工作。
- SMBUS系统管理总线
system management bus由Intel于1995年定制,基于菲利普I2C总线协议发展,通过两线制将各种芯片连接并且相互通信传递信息。时钟频率在10-100kHz。SMBUS设计是为了低速率信息通讯。在台式机中主要采集电源管理,在笔记本中则是非常多芯片的总线,且笔记本电源管理更加复杂,更加需要smbus。在ec端主要用于:battery info电池信息、cpu/gpu thermal info温度信息、SMBUS debug card调试(EC、BIOS),触摸板等等。
smbus设备作为一个从设备挂载需要有唯一标识符7bit的从机地址,设备厂商必须向SMBUS工作组申请专用地址。为防止同一地址冲突,必须提供某种机制解决地址冲突问题。从设备获取信息的方法是按照协议发送命令而后传回信息,要注意clock信号必须同步。
- Brightness亮度
LCD Brightness笔记本亮度变化功能Fn+F1/F2/3、插拔AC、电量变化、软件调节都可可以变化亮度。这是因为EC实现了控制亮度的DA/PWM波。
亮度列表由EC在亮度变化时会索引相关亮度值转化为DA或PWM输出LVDS驱动亮度变化
Auto Dim自动亮度是AC插拔时提供命令保存到相关内存,根据读取数据发命令返回给EC调整亮度。
- 参考资料
我所知道的EC
bilibili:cv2158209 up主:外星人转Blade
笔记本上电时序(PC不开机了可以看下) - 哔哩哔哩 (bilibili.com)