开源的 Windows ACPI Table 查看工具

最近发现一个开源的 Windows 下查看 ACPI table 的工具 firmwaretables 在下面的链接可以下载到:

https://github.com/vurdalakov/firmwaretables

Lists, extracts and decodes system firmware tables. Usage: firmwaretables <-list | -all | -save | -decode <table type> <table id> [filename]> [-silent] Commands: -l – list available system firmware tables -a – save all system firmware tables to files -s – save specific system firmware table to file -d – decode specific system firmware table Options: -silent – no error messages are shown; check exit code Exit codes: 0 – operation succeeded 1 – operation failed -1 – invalid command line syntax

TinkerNode 制作斐波那契时钟

斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368…….. 这个数列从第3项开始,每一项都等于前两项之和: f(n)=f(n-1)+f(n-2)  , n>2, f(1)=1,f(2)=1。

这样的数列非常简单,但是有着很多有趣的特性。比如:而且当n趋向于无穷大时,前一项与后一项的比值越来越逼近黄金分割0.618(或者说后一项与前一项的比值小数部分越来越逼近 0.618)。

可以看到越到后面,的比值越接近黄金比。

人类和禽兽的一个很大的区别在于对颜色的识别。比如,相对于人类来说,狗狗可以称为色弱。宠物眼科医师已经得出结论,狗所看到的颜色与红绿色盲的人有点相似。狗的眼睛有蓝色和绿色的光感受体,但没有红色的光感受体。因此,狗无法轻易区分黄色,绿色和红色,但是他们可以轻松识别不同深浅的蓝色、紫色和灰色。颜色仅仅是狗感知环境的因素之一。亮度,对比度,尤其是物体运动,都是非常重要的因素,并且这些因素更能帮助狗狗来感知它们周围的环境。【参考1】

狗狗看到的颜色:

人类看到的颜色:

据说人眼对于颜色的敏感是千万年进化而来的结果。比如,准确通过识别书上红色的成熟果实,能够大大提升采集工作的效率…….沿着这种理论,女性应该能分辨出更多的红色,而显示确实如此。譬如,在我眼中都是红色的口红,在老婆眼中可能是朱红 (vermeil),粉红 (pink),梅红(plum),玫瑰红(rose),桃红(peach blossom),樱桃红 ( cherry),桔红(reddish orange),石榴红(garnet),枣红(purplish red),莲红(lotus red),浅莲红(fuchsia pink),豉豆红(bean red)…….

将斐波那契数列和颜色结合在一起,就有了斐波那契时钟,这是国外众筹网站上的一个项目:

想读出这个时钟的时间需要一点技巧:钟表面有5个区域,每个区域代表着不同的权值,比如最小的方块表示1 最大的表示5:

表面上有4种颜色:红色,黄色,蓝色和白色。其中白色表示0. 其他的取值是由其所在区域的权值决定的。具体时间的计算如下

小时数 = 红色数值 + 蓝色数值

分钟数 = (绿色数值 + 蓝色数值) x 5

表示范围就是从 00:00到12:55. 同样的时间可能由不同的方式进行表示。比如:12:55 可以由下面2种方式来表示

红色=1  蓝色=1+2+3+5=11 绿色=0

因此,时间计算方法如下:

小时数 = 1+11=12

分钟数 = (0 + 11) x 5=55

同样的,下面这样也表示 12:55

再来一个复杂的,比如表面如下:

红色=1 绿色=2 蓝色=3+5=8

小时数 = 1+8=9

分钟数 = (2 + 8) x 5=50

所以上面表示的是 9:50 (另外,这个时间总共有6种表示方法)

从表示方法上也能看出来时间是以5分钟为单位的。无法表示注诸如  12:01 这样的时间,只有 12:00或者 12:05 这样。

00:00到 12:55一共有 152个时间,有些时间有很多个表示方法总共有992个方案。

上面介绍了最原始的斐波那契时钟, 小的表面并不能满足我的要求,因此,我设计了一个使用 TinkerNode(ESP32)发出 VGA信号的方案,这样可以将带有 VGA 的显示器变成时钟。电路上非常简单,最主要的是 VGA 接口上的 R,G,B 3个Pin和 Vsync 和 Hsync 2个Pin。

PCB 设计如下:

和之前的设计一样,硬件只是整个作品的一小部分,绝大多数工作是软件来完成的。

需要解决的第一个问题是时间的表示,为此,我编写了一个C语言代码将所有的可能时间拆分表示, 最终结果在 clockdata.h 中。比如,当前时间是  12:55 。首先要在 TimeToIndex [] 中查找(12*12+55/5=155), 其中  TimeToIndex [155] 定义如下:

//155  12:55  990

{2,990}

意思是: 12:55 有2种表示方法,在 TimeToSectionColor [] 这个表格中990 开始处:

//155  12:55  990

{SECRED  ,SECBLUE ,SECBLUE ,SECBLUE ,SECBLUE },

{SECBLUE ,SECRED  ,SECBLUE ,SECBLUE ,SECBLUE }

第一种方法, Section 0-4 颜色分别为 RED,BLUE,BLUE,BLUE,BLUE。 第二种方法,Section 0-4 颜色分别为 BLUE,RED,BLUE,BLUE,BLUE。根据不同区域,将对应区域设定设定为指定的颜色即可。

接下来是时间的获取, TinkerNode 支持物联网,可以轻松的获得,在 DFRobot_NBIOT\examples\LocalTime\CalLocalTime_NB 有这样的例子(此外,对于 TinkerNode开发板还可以通过 GPS/ WIFI 来实现自动获得)。完整代码如下:

// TinkerNode Quectel BC20 模块支持
#include "DFRobot_BC20.h"

// VGA 支持库
#include <ESP32Lib.h>
#include <Ressources/Font8x8.h>

// 计算后的的时间对颜色表示方法
#include "clockdata.h"

DFRobot_BC20 myBC20;

// VGA 显示引脚
const int redPin = 22;
const int greenPin = 21;
const int bluePin = 19;
const int vsyncPin = 18;
const int hsyncPin = 23;

const int TIMEX= 560;
const int TIMEY= 450;

// 3 Bit VGA 支持
VGA3BitI vga;

void setup()
{
  Serial.begin(115200);
  while(!myBC20.powerOn()){
    delay(1000);
    Serial.print(".");
  }
  Serial.println("BC20 started!");
  while(!myBC20.checkNBCard()){
    Serial.println("Please insert the NB SIM card !");
    delay(1000);
  }
  Serial.println("Waitting for access ...");
  while(myBC20.getGATT() == 0){
    Serial.print(".");
    delay(1000);
  }
  Serial.println("Waiting for NB time...");
  // 通过 BC20 获得当前时间
  while(myBC20.getCLK()){
    if(sCLK.Year > 2000){
      break;
    }
    Serial.print(".");
    delay(1000);
  }
  Serial.println();
  Serial.println("Configure local time");
  // 将获得的时间设置给ESP32
  configLocalTime(sCLK.Year,sCLK.Month,sCLK.Day,sCLK.Hour,sCLK.Minute,sCLK.Second);
Serial.printf("sdata %d-%02d-02d %02d:%02d:%02d\n",sCLK.Year,sCLK.Month,sCLK.Day,sCLK.Hour,sCLK.Minute,sCLK.Second);
  // 显示模式为  640x480 (主要是内存限制)
	vga.init(vga.MODE640x480, redPin, greenPin, bluePin, hsyncPin, vsyncPin);
	// 使用 8x8 字库
	vga.setFont(Font8x8);
	// 背景为白色
	vga.clear(vga.RGB(0xffffff));
  /*
	// 这里可以再屏幕上显示一下当前内存剩余情况
	// 设置文字颜色
	vga.setTextColor(vga.RGB(0));
  // 设置屏幕输出位置
  vga.setCursor(0, 20);
  //show the remaining memory
  vga.print("free memory: ");
  vga.print((int)heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
  */
}

// 颜色索引到颜色值转换函数
int SectionColor(int ColorIndex) {
  if (ColorIndex==SECRED) {return vga.RGB(0xFF);}
  if (ColorIndex==SECGREEN) {return vga.RGB(0xFF00);}
  if (ColorIndex==SECBLUE) {return vga.RGB(0xFF0000);}  
  return vga.RGB(0xFFFFFF);
}

// 记录上一个秒数
int LastSecond=0xFF;

// 记录上一个颜色
int LastTimeIndex=0xFFFF;

void loop()
{
  int TimeIndex;

  // 取得当前时间
  tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }
  AnalysisTime(&timeinfo);
  Serial.printf("%02d:%02d:%02d\n",timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
  // 如果秒发生变化
  if (timeinfo.tm_sec!=LastSecond) {
      // 串口输出当前时间
      //Serial.printf("%02d:%02d:%02d\n",timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
      // 擦除之前屏幕上绘制的时间
      vga.fillRect(TIMEX,TIMEY,64,64,vga.RGB(0xffffff));
      // 设置文字颜色
      vga.setTextColor(vga.RGB(0));
      vga.setCursor(TIMEX,TIMEY);
      // 如果小时只有1位,那么前面用 0 填充      
      if (timeinfo.tm_hour<10) {vga.print("0");}
      vga.print(timeinfo.tm_hour);
      vga.print(":");
      // 如果分钟只有1位,那么前面用 0 填充
      if (timeinfo.tm_min<10) {vga.print("0");}
      vga.print(timeinfo.tm_min);
      vga.print(":");
      // 如果秒只有1位,那么前面用 0 填充
      if (timeinfo.tm_sec<10) {vga.print("0");}
      vga.print(timeinfo.tm_sec);
      LastSecond=timeinfo.tm_sec;
      
      // 时间表示范围是从 0:0 到 12:55, 所以对于12点特别处理
      if (timeinfo.tm_hour!=12) {TimeIndex=(timeinfo.tm_hour % 12)*12+(timeinfo.tm_min/5);}
      
      // 只有当需要改变屏幕显示的时候才重新绘制
      if (LastTimeIndex!=TimeIndex) {
        // 
        Serial.print("New time index:");
        Serial.printf("%d  [%d %d]\n",TimeIndex,TimeToIndex[TimeIndex].start,TimeToIndex[TimeIndex].number);
        
        // 生成下一个时间的颜色,对于有多个可能性的事件,随机选择一个
        int PoolIndex=TimeToIndex[TimeIndex].start+random(TimeToIndex[TimeIndex].number);
        Serial.print("Pool index:");
        Serial.println(PoolIndex);
        Serial.printf("Pool [%d %d %d %d %d]\n",
                TimeToSectionColor[PoolIndex].timepool[0],
                TimeToSectionColor[PoolIndex].timepool[1],
                TimeToSectionColor[PoolIndex].timepool[2],
                TimeToSectionColor[PoolIndex].timepool[3],
                TimeToSectionColor[PoolIndex].timepool[4]
        );
        
        // 使用圆形表示时间
        if (TimeToSectionColor[PoolIndex].timepool[0]==SECNONE) {
              vga.fillCircle(80,80,80,vga.RGB(0xFFFF));
              vga.fillCircle(80,80,75,SectionColor(TimeToSectionColor[PoolIndex].timepool[0]));
          }else{  
              vga.fillCircle(80,80,80,SectionColor(TimeToSectionColor[PoolIndex].timepool[0]));
        } 
        if (TimeToSectionColor[PoolIndex].timepool[1]==SECNONE) {
              vga.fillCircle(200,40,40,vga.RGB(0xFFFF));
              vga.fillCircle(200,40,35,SectionColor(TimeToSectionColor[PoolIndex].timepool[1]));
        }else {
              vga.fillCircle(200,40,40,SectionColor(TimeToSectionColor[PoolIndex].timepool[1]));
        }
        if (TimeToSectionColor[PoolIndex].timepool[2]==SECNONE) {
              vga.fillCircle(200,120,40,vga.RGB(0xFFFF));
              vga.fillCircle(200,120,35,SectionColor(TimeToSectionColor[PoolIndex].timepool[2]));
        }else {
              vga.fillCircle(200,120,40,SectionColor(TimeToSectionColor[PoolIndex].timepool[2]));
        }
        if (TimeToSectionColor[PoolIndex].timepool[3]==SECNONE) {
              vga.fillCircle(120,280,120,vga.RGB(0xFFFF));
              vga.fillCircle(120,280,115,SectionColor(TimeToSectionColor[PoolIndex].timepool[3]));
        }else {
              vga.fillCircle(120,280,120,SectionColor(TimeToSectionColor[PoolIndex].timepool[3]));
        }
        if (TimeToSectionColor[PoolIndex].timepool[4]==SECNONE) {
              vga.fillCircle(440,200,200,vga.RGB(0xFFFF));
              vga.fillCircle(440,200,195,SectionColor(TimeToSectionColor[PoolIndex].timepool[4]));
        }else {
              vga.fillCircle(440,200,200,SectionColor(TimeToSectionColor[PoolIndex].timepool[4]));
        }
        

        /*
        // 使用正方形表示时间
        vga.fillRect(0,0,160,160,SectionColor(TimeToSectionColor[PoolIndex].timepool[0]));
        vga.fillRect(160,0,80,80,SectionColor(TimeToSectionColor[PoolIndex].timepool[1]));
        vga.fillRect(160,80,80,80,SectionColor(TimeToSectionColor[PoolIndex].timepool[2]));
        vga.fillRect(0,160,240,240,SectionColor(TimeToSectionColor[PoolIndex].timepool[3]));
        vga.fillRect(240,0,400,400,SectionColor(TimeToSectionColor[PoolIndex].timepool[4]));
        */
        
        /*
        // 这里可以预览一下8种颜色
        vga.fillRect(0,0,80,80,  vga.RGB(0x000000));
        vga.fillRect(80,0,80,80, vga.RGB(0x0000FF));
        vga.fillRect(160,0,80,80,vga.RGB(0x00FF00));
        vga.fillRect(240,0,80,80,vga.RGB(0xFF0000));
        vga.fillRect(320,0,80,80,vga.RGB(0xFF00FF));
        vga.fillRect(400,0,80,80,vga.RGB(0xFFFFFF));
        vga.fillRect(480,0,80,80,vga.RGB(0x00FFFF));
        vga.fillRect(560,0,80,80,vga.RGB(0xFFFF00));
        */

        LastTimeIndex=TimeIndex;
      }
   } 
   delay(900);  
}

完整代码下载:

最终,提到颜色,不由得让人想起乐嘉老师的“色彩心理学”,他的学说完全可以作为检验一个人常识和逻辑水平的试金石。

本文首发 https://mc.dfrobot.com.cn/thread-307842-1-1.html

参考:

1. 作者:汪小喵 https://www.zhihu.com/question/22005284/answer/64313689

工作视频在B站可以看到

https://www.bilibili.com/video/BV1df4y1Y7rR

对应的电路图和 PCB

AMD BIOS 工作机会

THE ROLE:
Provide consultation to internal and external customers regarding AMD features and programming requirements.
AMD’s environment is fast paced, results oriented and built upon a legion of forward-thinking people with a passion for winning technology.
This is an extraordinary opportunity to work in an outstanding company like AMD – Join us!

THE PERSON:
Has knowledge with ACPI, USB, PCIE, SATA and other PC industry standard. Has good communication skills and be able to work both independently and in a team.

KEY RESPONSIBILITIES:
Design, develop, and debug BIOS (System Software) for internal/external systems and platforms that use AMD APU/CPUParticipant in day-to-day BIOS development work using PC assembly and C languages; will need to interact with internal organizations, BIOS vendors, and customers.Comfortable working with PC hardware and platform issuesMust have strong system debugging skills.The following are typical tasks that the engineer will be responsible for:Design BIOS features required by AMD APU/CPU;Develop BIOS features for AMD new platforms crafted in house or externally; sustain existing BIOS; debug BIOS and system related issues;Assist APU/CPU validation, platform validation, and debug engineers to develop/debug system and silicon issues; 


PREFERRED EXPERIENCE:
Strong Knowledge about ACPI, USB, PCIE, SATA and other PC industry standardGood at X86 assembly and C languageFamiliar with at least one BIOS code base (AMI, Insyde or Phoenix BIOS).Strong communication skillsExperience in BIOS related tools development is a plus.Fluent in both written and spoken English.Fixes code for firmware (IC embedded code) application.Involves collaboration on or assuming the consultative or leadership responsibilities for a specific project or for product development initiatives.Is required to deal with internal groups on behalf of the group or project.Has accountability for results in a particular area of work.May have limited accountability for a small number of engineers related to projects (2-5), (e.g., be involved in any of the following activities: interview and selection, day-to-day technical supervision or mentoring). 


ACADEMIC CREDENTIALS:
Bachelor or Master, major in EE, CS or related area 
LOCATION:
 上海浦东环科路669


有兴趣的朋友可以联系 Eddy.Gu@amd.com

WinDBG 读取 RTC 的 Script

WinDBG 支持Script,这样用户可以通过编程来实现一些自动控制。本文介绍的就是一个读取 RTC (CMOS)的 Script。

先介绍一下如何使用,将 Script 文件放在 c:\asl\ 下面,文件名是 cmos.txt。在 WinDBG 停下来目标机之后输入 $><c:\asl\cmos.txt 即可运行【参考1】。 WinDBG端结果如下:

kd> $><c:\asl\cmos.txt

    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 
===================================================
 0| 8  0 47  0 16  0  2  1 12 20 26  2  0 80  0  0 
 1| 0  0 F0  0  E 80  2 FF FF 2F  0  0  0  0  0  4 
 2|FF FF FF FF FF 3F  0  0  0  0  0  0  0  0  8 EB 
 3|FF FF 20  0  0 7F  0 20 20  0  0  0  0 31  0  0 
 4| 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 5| 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 6| 1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 7| 0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0 
 8|38  0 48  0 16  0  2  1 12 20 26  2  0 80  0  0 
 9| 0  0 F0  0  E 80  2 FF FF 2F  0  0  0  0  0  4 
 A|FF FF FF FF FF 3F  0  0  0  0  0  0  0  0  8 EB 
 B|FF FF 20  0  0 7F  0 20 20  0  0  0  0 31  0  0 
 C| 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 D| 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 E| 1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 F| 0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0

在虚拟机中使用 RW_Everything 可以看到同样的结果:

RW_EveryThing 读取 CMOS

具体的代码如下:

$$********************************************
$$* WinDBG Script from www.lab-z.com         *
$$*                                          *
$$*   Read CMOS                              *
$$*                           2020 DEC 1st   *
$$********************************************

.printf "   "
.for (r $t0=0; @$t0 &lt; 10;r $t0=$t0+1) {.printf "%2X ",$t0}
.printf "\n"
.for (r $t0=0; @$t0 &lt; 11;r $t0=$t0+1) {.printf "==="}
.printf "\n"

.for (r $t0=0; $t0 &lt; 10;r $t0=$t0+1) {
  .printf "%2X|",$t0
  .for (r $t1=0; $t1 &lt; 10;r $t1=$t1+1) {
     r $t2=$t0*10+$t1
     ob 70 $t2
     .foreach /pS 1 (vv {!ib 71}) {r $t3=${vv}}
     .printf "%2X ",$t3
   }
  .printf "\n"
}

简单介绍一下代码意思:

“$$” 表示注释,所以最开始几行只是用于标记

下面几行是用于输出一个表格头

 “.for (r $t0=0; $t0 < 10;r $t0=$t0+1) ” 这是一个 for 循环,其中使用到了 $t0 这是 WinDBG 内置的虚拟寄存器,在 WinDBG 中有 t0-t19个

计算当前的 Index r $t2=$t0*10+$t1

代码中所有的数值都是十六进制,所以看到 10 意思是0x10, 表示 16(十进制)

 ob 70 $t2 这是 outport 命令,将 $t2 值送到 0x70 Port 中

 前面对 70 Port输出 Index, 接下来需要从 71 Port上读取。端口读取命令是 ib ,但是无法直接取得读到的值。因此这里使用  .foreach 来解析命令返回值。在这里,ib返回值是 00000070:  XXXXXX 这种形式。因为空格的存在,解析结果会分为两次出现在后面的大括号中。/pS 1 的意思是忽略返回值中的第一个,使用这个参数之后我们只能收到 XXXXXX 这个结果,我们将其放入 $t3 中

 .foreach /pS 1 (vv {!ib 71}) {r $t3=${vv}}

8.文件文件使用ASCII 格式,因此文件中最好不要出现中文

上面只是实现了CMOS 的读取,配合 WinDBG 的断电能够实现更多的功能,另外最新的 WinDBG Script 支持 JavaScript 有兴趣的朋友可以更深入的研究。

本文提到的 Script 文件下载:

参考:

  1. https://www.cnblogs.com/softfair/p/windbg-command-ii.html Windbg  脚本命令简介 二, Windbg  script command
  2. https://blog.csdn.net/hgy413/article/details/9073157  50.windbg-.foreach循环输入(windbg script)
  3. https://www.cnblogs.com/awpatp/archive/2010/06/16/1758929.html 在WinDBG中使用foreach
  4. https://www.jianshu.com/p/56ff0bc43d3d Windbg调试笔记

Ampere Computing 招聘(202101)

这是一家做ARM 服务器的公司,网站 :

https://amperecomputing.com/#

DescriptionThe Role:

Our PAE will work with ODM to perform technical supports in software area, specifically in UEFI and BMC related.

What the ODM Management Team wants you to know:

ODM Management is the team to deliver the product from NPI to mass production. A critical function team to make sure the design/manufacture tasks at ODM is completed to deliver the platforms to customers.

What you’ll do:

As a member of the ODM management team, you will support design activities of various ODM projects. And work with sales, AE, and engineering teams worldwide to provide software related support.

What you’ll bring:

Experience of UEFI and BMC porting/testing/debugging either AMI code base or opensourceCoding capability of related software of C+, JAVA, HTML, or othersRequired knowledge of CPU and server system architectureSupport ODM experience is required to demonstrate the skills for design supporting, technical service, and problem-solvingCapable of working independently as well as cross different functional teams’ cooperation within company and customers sideHands on experience in the setup and configuration of server systems

Education & Experience:

Bachelor’s degree in computer science, electrical engineering with minimum 9 years of experienceOur CompanyAmpere is designing the future of hyperscale cloud and edge computing with the world’s first cloud native processor. Built for the cloud with a modern 64-bit Arm server-based architecture, Ampere gives customers the freedom to accelerate the delivery of all cloud computing applications. With industry-leading cloud performance, power efficiency and scalability, Ampere processors are tailored for the continued growth of cloud and edge computing.

有兴趣的朋友请联系 jun.chen@amperecomputing.com

QEMU 下 SecureBoot 和 EFI 签名的测试

本文将介绍如何在 QEMU 上实验 Secure Boot 的功能,以及如何对一个 EFI Application 进行签名。

首先,需要编写一个能在QEMU 上运行的,打开 Secure Boot 功能的 BIOS。编译命令如下:

build -a X64 -p OvmfPkg\OvmfPkgx64.dsc -D SECURE_BOOT_ENABLE=TRUE

即可(在 EDK202008 的版本上测试,打开这个功能后需要用到 OpenSSL 提供的函数,因此需要在 EDK2中加入 OpenSSL 的代码)。接下来,根据【参考1】,我们需要生成下面这些文件用于签名。

生成这些文件必须安装Windows SDK,这里使用的是 Windows 10 的 SDK ,工具在C:\Program Files (x86)\Windows Kits\10\bin\x64 下面:

  1. 生成 TestRoot.pvk 和 TestRoot.cer

命令:makecert -n “CN=TestRoot ” -r -sv TestRoot.pvk TestRoot.cer

运行之后会要求你设置一个密码,这里我们使用  labz 作为密码。

之后会再要求你输入一次 labz

2.生成TestSub.pvk 和 TestSub.cer

命令:makecert -n “CN=TestRoot ” -iv TestRoot.pvk -ic TestRoot.cer -sv TestSub.pvk TestSub.cer

这里会要求你设置另外一个密码,这里我们使用 labzpri

输入你设置的 labzpri

再次弹出的对话框输入之前设置的 labz 这个密码

现在我们就有了前面提到的4个文件。

然后准备一个用于测试的EFI 文件,我们使用 Hell.EFI ,它只是在屏幕上输出字符串。同样的,使用 SDK 中的工具对其进行签名,命令如下:

C:\Program Files (x86)\Windows Kits\10\bin\x64>signtool sign /ac TestSub.cer /f TestSub.pfx /p labzpri /fd sha256 Hello.Efi

运行结果:

签名后的文件名称是 Hello-signed.efi 。到现在我们有4个签名相关的文件和2个 EFI 文件,将他们全部copy到U盘上的QEMU目录中(特别注意 U盘必须FAT32格式)。之后使用下面这个 command 启动 QEMU:

qemu-system-x86_64.exe -pflash ovmf_secureboot.fd -hda fat:rw:j:\QEMU -net none

启动后,默认情况下 Secure Boot 是 Disabled,至少需要从 Setup 加入 PK 和 DB 两组 Key【参考1】才能 Enable这个功能:

a.Device Mnager->Secure Boot Configuration切换为 Custom Mode

b.加载 PK 的操作在  Custom Secure Boot Options 中选择 PK Option

c进入之后选择 Enroll PK 通过文件加载 PK

选择盘符

选择 TestRoot.cer 文件

返回后可以看到这个文件已经加载生效了

d.继续加载 DB文件, 操作如下

一个系统中只能有一个 PK,但是可以有多个 DB 文件。这里需要给加载的DB 指定一个 GUID,随便写编一个即可

e.完成设置后返回即可看到 Secure Boot 已经 Enable了。

在 Shell 下测试,因为 Hello.efi 没有签名,所以无法执行,Hello-signed.efi 正确签名了,所以可以运行。

上面就是在 QEMU 下测试 Secure Boot 功能的过程,有兴趣的朋友可以动手试试。

参考:

  1. http://gschwinds.net/cache/SigningUEFIImages-1.31.pdf
  2. https://www.jianshu.com/p/e892733c6f50
  3. 来源同【1】DB和 PK 对于 Secure Boot  Enabling 是必须的

【参考1】的文档,备份一下

ESP32 作为蓝牙音频接收端

具体方法:

1. https://github.com/tierneytim/btAudio 下载并且安装

2.使用UDA1334A I2S 立体声 解码器 I2S Stereo Decoder, 注意:这个是解码器,并不能直接推动喇叭,所以如果在没有音响的情况下只能使用耳机,

如果你想做一个直接带喇叭的播放器,那么还需要一个功放。

原作使用的是“Adafruit I2S Stereo Decoder – UDA1334A” https://learn.adafruit.com/adafr … er-uda1334a/pinouts

我这个是从长江智动买的,一般来说,如果你看到 Adafriut 的板子都可以尝试在这家搜索是否有相同的设计:

CJMCU-1334 UDA334A I2C Decoder

3.将对应的线连接起来, 这里提到的是 D26/D27/D25 这样的Pin:

int bck = 26;
int ws = 27;
int dout = 25;

ESP32 做蓝牙音频接收,之后从 I2S 接口输出给 Codec

4.运行库里面 minimalAudio

5.手机可以搜索到这个设备将 Audio 发送到 ESP32 上

测试视频可以在下面看到:

https://www.bilibili.com/video/BV1B64y1c7H3

sizeof(int)=?

很长一段时间以来,我理所当然的认为 sizeof(int) 是根据编译目标变化的,比如: x86 下应该为 4 Bytes (32-bits),x64 模式下应该为 8 Bytes(64 bits)。但是近日有朋友遇到了问题,我随手实验,用 printf 输出 sizeof(int) 和 sizeof(int *) 结果令我大吃一惊:

x86 下 sizeof(int) ==4 , sizeof(int *)==4

x64 下 sizeof(int) ==4 , sizeof(int *)==8

就是说, x64 下内存指针变成了8Bytes, 但是 int 仍然是 4bytes。无他,规定如此耳。

所以,在编写 UEFI 代码的时候,尽量不要使用 int 类型,因为你在代码中随手写下的赋值可能会因为编译目标不同而导致令人疑惑的错误。作为 Firmware 工程师,需要时刻了解数据的大小。

参考:

1.https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-160

2.https://www.geeksforgeeks.org/difference-between-sizeofint-and-sizeofint-in-c-c/

3.https://docs.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-160

Sizes of built-in types

Most built-in types have implementation-defined sizes. The following table lists the amount of storage required for built-in types in Microsoft C++. In particular, long is 4 bytes even on 64-bit operating systems.

TypeSize
boolcharchar8_tunsigned charsigned char__int81 byte
char16_t__int16shortunsigned shortwchar_t__wchar_t2 bytes
char32_tfloat__int32intunsigned intlongunsigned long4 bytes
double__int64long doublelong longunsigned long long8 bytes

嘉立创 SMT 指南

相信大多数和我一样的非专业玩家在制作自己的作品时经常会受到焊接方面的困扰。比如,经常因为焊接失误导致芯片报废。曾经有哲人说过:“一个人遇到的99%的问题都可以用钱解决,剩下的1%,需要用更多的钱。”同样的,焊接的问题也可以用钱来解决。这次以设计一块CH558芯片的开发板为例,介绍一下如何在嘉立创直接 SMT。

选择嘉立创的主要原因是:他们有PCB 绘制软件,PCB 制作,SMT焊接和元件商城 。这样涵盖了电子制作的全部过程,可以一站式满足需求,这样在最大程度上避免了生产过程不匹配的问题,尽可能节省了设计者的时间。比如,我曾经碰到做主板时贴片二极管符号和实物方向不匹配的问题,第一次遇到问题时,硬件工程师花了很长时间才确定问题。

第一个目标时是完成电路图和 PCB 的绘制,这个步骤结束后会产生有三个用于生产的文件:

  1. Gerber File (用于制作PCB。电路的设计软件很多,比如 PROTEL, Eagle, Allegro 等等。PCB 生产厂家通常 “不认可”你提交的源文件,但是 Gerber 格式是通用的。)
  2. BOM (元件列表,用于告诉准备SMT的人需要为你的板子准备什么元件。订阅《无线电》杂志的朋友应该看过上面对于波峰焊机的介绍。在SMT准备过程中,工程师就是根据BOM 将需要焊接的元件料盘放置在机器中。)
  3. 坐标文件(告诉贴片机在什么位置贴什么元件。)

前面提到立创有一条龙服务,这里使用的软件是立创EDA。主要优点是支持在线编辑绘制电路图和PCB,这样有网络能打开网页就可以画图,上班摸鱼下班回家后可以继续绘制。缺点主要是:自动布线不太成熟,经常抽风,官方建议手工布线。

绘制电路的过程需要选择元件。诸如USB公头这样的插接件,强烈建议选用商城中的元件,因为这样的元件有对应的元件库,这样能够尽可能避免引脚差异导致的无法使用。

这次绘制的是 CH558的开发板,更具体可以说是将 CH558的封装转成 DIP 封装。电路图很简单,外围元件只有几个电阻,主要工作是将芯片引到排插上,设计如下:

PCB设计如下,特别注意图中的2个USB引脚需要通过差分布线来实现(主要是保证等长):

上述设计完成后即可生成准备用于制造的文件。

在PCB编辑界面上(注意,电路图编辑界面没有这个选项,必须是 PCB编辑界面),选择下面“生成制造文件(Gerber)”;或者在菜单中选择“制造àPCB制版文件(Gerber)”:

在弹出的对话框中使用按钮下载生成的 Gerber package。这样我们就有了 Gerber File有了这样的文件可以提交给 PCB 厂家进行生产。

同样的,可以在菜单—> 制造—>物料清单(BOM)生成物料清单表。

列表如下,使用下面的“导出BOM ”即可生成生产所需 BOM 表

接下来是坐标文件,在同样的位置

我们是单板,在嘉立创生产所以都无须勾选:

选择“导出”后,我们就有了坐标文件。下面就可以提交进行生产了。

有了上面的文件就可以准备生产了,SMT 的目标可以分解为2个小目标:一个是PCB 的制作,另一个是在这个 PCB 上进行SMT。

小目标1:提交嘉立生产PCB

1.1使用嘉立创下单助手(这个软件有网页版和PC版,二者界面是相同的。目前在推广期,建议使用PC版提交生产,有优惠价格)。选择左侧 PCB订单管理à在线下单/计价

1.2 点击 “上传PCB文件” 选中刚才的Gerber Packag-e 文件:

1.3 自动解析,生成预览。可以看到PCB的预览图,建议认真观察过孔位置等等:

1.4 在上面的界面选择“板子数量”,最少5块。建议第一次打选择5块这样最节省。

1.5 续设定参数,如果没有特别要求大部分使用默认值即可。具体尺寸可以在立创 EDA 菜单 ――> 制作 ――> PCB 信息 中看到。目前嘉立创有优惠,用户每个月有两次机会,可以以5元价格制作10x10cm以内的PCB,非常实惠。

如果你是第一次打板子,建议选择“确认生产稿”(多3元),这样可以避免一些问题(比如线距太窄导致PCB 打出来就无法使用这样的问题):

下面的工艺信息中,阻焊颜色决定了 PCB板子的颜色,绿色的打板速度最快,其他颜色加工时间稍微长一点(绿色通常是24小时;我个人比较喜欢红色,承诺交货时间是72小时)。我们打的板子没有拼板(这个可以理解为一个稿件中同时包含了多块板子)。我们打的样板没有拼板(拼版可以理解为一个稿件中同时包含了多块板子,在以面积计算价格的情况下可以节省成本)。

下面这些选项保持默认即可:

我们要求 SMT 因此,这里选择“需要”:

点击后会弹出要求确认,再点需要。

我们这次打的是单片:

选择了 SMT 之后只能选择不需要钢网,另h外也不需要发票

交期根据自己需要选择最快的:

目前的优惠每个用户每个月有两次机会,可以以5元价格制作10x10cm以内的PCB

1.5提交订单,等待审核。建议耐心一点等待审核结果。

1.6上述选择后,在右侧填写地址信息。至此,PCB 的提交已经完成。如果你只是制作PCB 没有 SMT 的要求,在付款之后等待PCB到家即可。

下面是提交 SMT的过程

  1. 审核结束之后,点击确认。会提示继续下 SMT 订单。

2.建议第一次打板选择 2片贴3片不贴

3.接下来继续填写表格,这里需要前面BOM 和 坐标文件,选择文件后即可进行下一步:

4.勾选需要进行贴片的文件,我们这里只贴嘉立创有库存的元件,余下的等板子回来自己手工贴。这里需要注意目前嘉立创无法贴需要手工摆放的元件,比如:USB公头,12MHz晶振这样的东西:

5.接下来会有简单的预览,我们的元件在正面(T层)

6.提示要求确认:

7.继续后需要确认主要芯片的Pin1 ,以及由极性元件的方向。确定后继续

8.最后就是交钱的过程了

经过一段的等待后板子就会飞越千山万水来到你的手中,入下:两片打芯片,三片不打。

板子到手之后即可着手进行检查,可能出现的问题是元件库和实物不符,比如:WS2811 搞错了 Pin1, 这种情况下可以去论坛提出问题,然后会有 PCB 和 EDA 方面的人和你进行接洽。