Step to UEFI (273) 打包为 EFI Application 的 MEMTEST86

前面介绍了最新的 MemTest86 ,美中不足的是这个版本需要制作启动盘,这次介绍一种将它打包为一个 EFI 的方法。

基本的思路是:将完整的 MemTest86 磁盘镜像按照资源打包到一个 EFI 文件中,然后再配合之前的RamDisk 知识将这个镜像加载到内存中。这样就相当于制作的镜像文件,跳进去就可以执行了。

代码要点:

1.将之前介绍的 MemTest86 制作成一个硬件镜像,然后将这个镜像命令为MemTest2023.png

2.MyRamDisk2.inf 中给出用到的文件如下

[Sources]
  MyRamDisk2.c
  MyRamDisk2.idf
  MemTest2023.png

3. MyRamDisk2.idf 文件内容如下

#image IMG_LOGO MemTest2023.png

4.主程序

a.首先找到当前 EFI 中的资源

//
        // Retrieve HII package list from ImageHandle
        //
        Status = gBS->OpenProtocol (
                        gImageHandle,
                        &gEfiHiiPackageListProtocolGuid,
                        (VOID **) &PackageListHeader,
                        gImageHandle,
                        NULL,
                        EFI_OPEN_PROTOCOL_GET_PROTOCOL
                        );
        if (EFI_ERROR (Status)) {
          Print(L"HII Image Package with logo not found in PE/COFF resource section\n");
          return Status;
        }

b.取得资源

//Step2. Parser HII Image
        ImageHeader=(EFI_HII_IMAGE_PACKAGE_HDR*)(PackageListHeader+1);
		ImageData=(UINT8 *)(ImageHeader+1);

c.解析资源之后拷贝到分配的内存中

	// Look for Ram Disk Protocol
        Status = gBS->LocateProtocol (
                        &gEfiRamDiskProtocolGuid,
                        NULL,
                        &MyRamDisk
                 );
        if (EFI_ERROR (Status)) {
            Print(L"Couldn't find RamDiskProtocol\n");
            return EFI_ALREADY_STARTED;
        }
					
        //Allocate a memory for Image
        Status = gBS->AllocatePool (
                    EfiReservedMemoryType,
                    (UINTN)FileSize,
                    (VOID**)&StartingAddr
                    ); 
        if(EFI_ERROR (Status)) {
                Print(L"Allocate Memory failed!\n");
                return EFI_SUCCESS;
        } 
		
		CopyMem(StartingAddr,&ImageData[5],FileSize);
        
        //
        // Register the newly created RAM disk.
        //
        Status = MyRamDisk->Register (
             ((UINT64)(UINTN) StartingAddr),
             FileSize,
             &gEfiVirtualDiskGuid,
             NULL,
             &DevicePath
             );
        if (EFI_ERROR (Status)) {
                Print(L"Can't create RAM Disk!\n");
                return EFI_SUCCESS;
        }

上述代码编译运行之后,会在当前系统中生成一个新的盘符,进入这个盘符之后就可以运行 MemTest86 进行内存测试了。

文本完整代码如下:

生成的 EFI 如下:

做一个低成本的USB LED 变色灯

这次制作的目标是:一个插在USB接口就能不停变化颜色的灯。

为了尽可能的压低成本,使用印刷在PCB上的USB。为了低成本实现不停变化颜色,选择淘宝上“F3圆头5mm草帽七彩慢闪led灯珠雾状装饰3v5v12伏F5七彩闪烁渐变色”这款彩灯。这款LED内置了IC,所以能够实现颜色的不停切换变化。

接下来开始电路设计,非常简单就是将LED引脚接在USB的 VCC 和GND即可。

对应的USB1 封装如下:

最终设计的PCB 如下:

为了尽可能降低成本,特别使用V-Cut拼版,这样我们可以在一块PCB上容纳尽量多的PCB。同时需要注意的是:对于嘉立创来说,如果每一个PCB长度或者宽度小于15mm,那么会加收100元的费用,因此,如果有可能尽量不要过窄或者过短。

PCB制作之后是这样的:

需要注意的是:理论上如果直接制作厚度为2.0mm的PCB 那么直接可以插入USB接口的。但是制作PCB时需要增加费用,因此选择制作1.6mm常规厚度的PCB ,拿到手后在USB线路位置自行粘贴剪裁后的信用卡、SIM卡之类的塑料卡片增加厚度。

我使用502胶水粘贴固定

正面:

工作的视频可以在下面看到:

总结省钱妙招:

  1. PCB印刷USB 公头;
  2. 拼版,并且保证长和宽都大于15mm;
  3. 制作 1.6mm的PCB, 拿到手后自行增厚;

最终花费:47.43 元制作12*6=60个PCB, LED 费用为 18元100个。因此,平均下来每个0.97元。

完整的电路图和PCB 下载(立创EDA)

usbled下载

参考:

1. https://detail.tmall.com/item.htm?_u=dkf8s92d31&id=624355880993&spm=a1z09.2.0.0.26642e8dewUEbx

FireBeetle 制作一个LED矩阵

这次介绍的项目是通过 FireBeetle ESP32 实现一个 8*16的单色LED矩阵,可以在上面实现一些简单的图形和动画效果。

在开始之前,首先介绍LED 的静态驱动和动态驱动的概念。当我们在一个发光二极管两端加上一个电压的时候,发光二极管即可工作。理论上,如果驱动N个共阴极的LED那么需要N个提供正电压。这种带来一个问题,如果需要驱动大量的LED,那么就同样需要同样数量的引脚作为正极。对于单片机来说,会遇到IO引脚不够的问题。

发光二极管/LED符号

这种直接驱动的方式称作“静态显示驱动”。与之相对,还可以通过构成矩阵的方式来进行驱动。以3×3的LED矩阵为例,通过6个IO 引脚可以驱动9个LED。

3×3 LED 矩阵

可以看到这种电路,我们可以一次性点亮一行或者一列上的LED,但是如果要点亮的位于不同的行列就会出现问题。例如:我们希望在矩阵上点亮LED0和LED6, 那么需要Y0、Y2为高,X0 为低,这种情况比较简单;但是如果需要同时点亮LED0和 LED4 问题就变得麻烦。因为 LED0 要求Y0 为高X0为低才能点亮,LED4 要求Y1为高X1为低,但是Y1为高X0为低时LED3也会同时亮起。因此,这里需要引入一个分时点亮的方法,比如,先设置Y0 为高X0为低点亮LED0,再设置Y0为低熄灭LED0,设置Y1为高X1为低点亮LED4,只要点亮速度足够快眼睛无法分辨出他们不是同时点亮的。这就是所谓的“动态扫描”。

如果使用 Arduino 编写代码,通常需要使用一个定时中断:

Void timerInterrupt()
{
熄灭上一次的行,点亮第x行
x=x+1
}

可以看出,这样的方法会使得程序复杂度上升,同样的,对于N 个灯需要根号N 个 IO。经过研究发现一个好玩的 IC: WCH 的 CH423。它是IC I/O 扩展芯片,功能如下【参考1】:

CH423 功能
  • 通过两线串行接口远程扩展出8 个通用输入输出引脚GPIO 和16 个通用输出引脚GPO。
  • 内置电流驱动级,连续驱动电流不小于15mA,OC 引脚输出1/16 脉冲灌电流不小于120mA。
  • 静态显示驱动方式支持24 只发光管LED 或者3 位共阳数码管。
  • 分时动态扫描显示驱动方式支持128 只发光管LED 或者16 位共阴数码管,支持亮度控制。
  • 双向I/O 引脚在输入方式下具有输入电平变化时产生中断的功能,中断输出低电平有效。
  • 16 个通用输出引脚可以选择推挽输出或者开漏输出。
  • 支持3V~5V 电源电压,支持低功耗睡眠,可以被输入电平变化唤醒。
  • 高速2 线串行接口,时钟速度从0 到1MHz,兼容两线I2C 总线,节约引脚。
  • 提供SDIP28 和SOP28 两种无铅封装,兼容RoHS。

这次的试验就使用这个芯片来实现一个 8×16的LED点阵。

首先进行电路设计:

控制电路

芯片是I2C 接口,控制线路非常简单:SCl和SDA就好了。CH423是SOP28封装,为了便于试验我从淘宝购买了一个SOP28转DIP的小PCB,焊接之后将CH423插入到PCB上。接下来是LED矩阵的设计:

LED 矩阵 8×16

其中 SE[N] 信号能够输出高低电平,DEG[M] 只用作吸收电流使用。对CH423 发送命令,告知我现在要做动态扫描使用,然后告知SE[N]和DEG[M] 的组合即可。例如:告知SE1 输出高,DIG0 吸收电流和SE7输出高,DIG15吸收电流,之后芯片本身会动态控制,肉眼看起来就是 L00和L7F 点亮【参考2】。

因为都是低速信号,没有太多限制,摆放好 LED后直接使用立创自动布线走的通即可。

PCB设计

焊接后的样子:

焊接后的实物

编写一个测试代码如下:

#include <Wire.h>
 
// CH423接口定义
#define     CH423_I2C_ADDR1     0x20         // CH423的地址
#define     CH423_I2C_MASK      0x3E         // CH423的高字节命令掩码
 
#define CH423_SYSON1    0x0417    //开启自动扫描显示
 
unsigned char CH423_buf[16];    //定义16个数码管的数据映象缓存区
const unsigned char BCD_decode_tab[ 0x10 ] = { 0X3F, 0X06, 0X5B, 0X4F, 0X66, 0X6D, 0X7D, 0X07, 0X7F, 0X6F, 0X77, 0X7C, 0X58, 0X5E, 0X79, 0X71 };
 
 
void CH423_Write( uint32_t cmd )    // 写命令
{
  Serial.print("Address ");
  Serial.print(( unsigned char )(cmd >> 8), HEX);
  Serial.print("  command  ");
  Serial.print(( unsigned char ) (cmd & 0xff), HEX);
  Wire.beginTransmission (( unsigned char )(cmd >> 8));
  Wire.write( ( unsigned char ) (cmd & 0xff) );  // 发送数据
  // 结束总线
  if (Wire.endTransmission() == 0) {
    Serial.println(" I2C Success!");
  } else {
    Serial.println("I2C error!");
  }
}
 
// 向CH423输出数据或者操作命令,自动建立数据映象
void CH423_buf_write( uint32_t cmd )
{
  if ( cmd & 0x1000 )
  { // 加载数据的命令,需要备份数据到映象缓冲区
    CH423_buf[ (unsigned char)( cmd >> 8 ) & 0x0F ] = (unsigned char)( cmd & 0xFF );    // 备份数据到相应的映象单元
  }
  CH423_Write( cmd );    // 发出
}
 
void setup() {
  Serial.begin (115200);
  Wire.begin (21, 22);   // sda= GPIO_21 /scl= GPIO_22.
  /* INTENS [00-11]
     OD_EN 使能开漏
     X_INT 0x08
     DEC_H 0x04
     DEC_L 0x02
     IO_OE 0x01
  */
  CH423_buf_write( 0x2417 );
  /* OC_L_DAT  OC7-OC0 电平控制
  */
  CH423_buf_write( 0x2200 );
  /* OC_H_DAT  OC15-OC8 电平控制
  */
  CH423_buf_write( 0x2300 );
 
  // 初始化时保持全灭
  uint32_t i;
  for (i = 0; i < 16; i++) {
    CH423_buf_write(((0x30 + i) << 8) + 0x00);
  }
}
 
// 要显示的字符取模, DFRobot 字样
// 来自 https://www.zhetao.com/fontarray.html
const unsigned char bitmap_bit_bytes[] = {
  0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  0b11111000, 0b11111100, 0b11111100, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  0b01000100, 0b01000010, 0b01000010, 0b00000000, 0b11000000, 0b00000000, 0b00000000,
  0b01000010, 0b01001000, 0b01000010, 0b00000000, 0b01000000, 0b00000000, 0b00010000,
  0b01000010, 0b01001000, 0b01000010, 0b00000000, 0b01000000, 0b00000000, 0b00010000,
  0b01000010, 0b01111000, 0b01111100, 0b00111100, 0b01011000, 0b00111100, 0b01111100,
  0b01000010, 0b01001000, 0b01001000, 0b01000010, 0b01100100, 0b01000010, 0b00010000,
  0b01000010, 0b01001000, 0b01001000, 0b01000010, 0b01000010, 0b01000010, 0b00010000,
  0b01000010, 0b01000000, 0b01000100, 0b01000010, 0b01000010, 0b01000010, 0b00010000,
  0b01000010, 0b01000000, 0b01000100, 0b01000010, 0b01000010, 0b01000010, 0b00010000,
  0b01000100, 0b01000000, 0b01000010, 0b01000010, 0b01100100, 0b01000010, 0b00010010,
  0b11111000, 0b11100000, 0b11100011, 0b00111100, 0b01011000, 0b00111100, 0b00001100,
  0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
};
 
// 显示一个动画效果
uint16_t buf[8] = {
  0b0100000000000000,
  0b0010000000000000,
  0b0001000000000000,
  0b0000100000000000,
  0b0000010000000000,
  0b0000100000000000,
  0b0001000000000000,
  0b0010000000000000,
};
 
void loop() {
  uint16_t i, j, m;
  char c, v;
  while (Serial.available()) {
    c = Serial.read();
    // 显示卡面定义的字符
    if (c == '1') {
      for (i = 0; i < 7 ; i++) { //一共有7个字符
        for (j = 0; j < 16; j++) { // 每个字符有16个1Byte数据
          CH423_buf_write( ((0x30 + j) << 8) + bitmap_bit_bytes[i + j * 7] );
        }
        delay(500);
      }
    }
    // 随机点亮测试
    if (c == '2') {
      for (i = 0; i < 16; i++) {
        for (j = 0; j < 16; j++) {
          CH423_buf_write( ((0x30 + j) << 8) + random(0, 256) );
        }
        delay(500);
      }
    }
    // 移动的动画效果
    if (c == '3') {
      for (m = 0; m < 32; m++) {
        // 显示 buf 定义的图形
        for (i = 0; i < 16; i++)
        {
          v = 0;
          for (j = 0; j < 8; j++) {
            if ((buf[j] & (1 << i)) == 0) {
              v = v << 1;
            }
            else {
              v = (v << 1) + 1;
            }
          }
          CH423_buf_write( ((0x30 + i) << 8) + v );
        }
        // 移动 buf 字符
        for (i = 0; i < 8; i++) {
            if ((buf[i]&1)!=0) {buf[i]=buf[i]|0x8000;}
            buf[i]=buf[i]>>1;
          }
        delay(100);  
 
         
      }
    }
  }
 
}

根据串口输入,进行不同的测试:

  1. 输入1 会逐个显示 DFRobot 字样
  2. 输入2会随机点亮
  3. 输入3会显示一个方向的简单动画效果。

参考:

  1. http://www.wch.cn/products/CH423.html
  2. 这部分在DataSheet有描述 “8.2. 动态显示驱动 CH423 的动态显示驱动方式用于驱动 128 只 LED 或者 16 只共阴数码管,由 IO7~IO0 引脚分别驱动共阴数码管的各个段引脚(各数码管并联),由 OC15~OC0 引脚分别驱动各个共阴数码管的公共端。单片机在加载完所有字数据后,开启 DEC_L 和 DEC_H 控制位由 CH423 自动地进行分时动态显示扫描。如果只需要驱动 8 只数码管,那么可以只开启 DEC_L 或者 DEC_H 其中的一个控制位,剩余的另CH423 中文手册”

工作的测试视频

MemTest86 Free 最新版

最近在测试 Memory 的时候偶然发现老版本的 MemTest86 在运行时会发生死机,新版本可以正常使用。于是,动手制作了一个精简版:

MemTest2023下载

解压之后得到 MemTest2023.IMG 文件,可以直接放在Ventoy 制作出来的启动盘上,开机启动之后从菜单选择这个文件即可进入 Memtest86。

参考:

1.https://www.memtest86.com/ 官方网站

C# 读取物理硬盘的例子

本质上和VC 写的没有区别,都是使用 CreateFile 打开 PhysicalDrive 然后进行操作。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices;
 
namespace RW_PyhsicalDisk
{
    class Program
    {
        [DllImport("kernel32", SetLastError = true)]
        static extern IntPtr CreateFile(
        string FileName,
        uint DesiredAccess,
        uint ShareMode,
        IntPtr SecurityAttributes,
        uint CreationDisposition,
        int FlagsAndAttributes,
        IntPtr hTemplate
        );
 
        static class DESIREDACCESS
        {
            public const uint GENERIC_READ = 0x80000000;
            public const uint GENERIC_WRITE = 0x40000000;
            public const uint GENERIC_EXECUTE = 0x20000000;
            public const uint GENERIC_ALL = 0x10000000;
        }
        /// <summary>
        /// Sharing mode of the file or object
        ///</summary>
        static class SHAREMODE
        {
            public const uint FILE_SHARE_READ = 0x00000001;
            public const uint FILE_SHARE_WRITE = 0x00000002;
            public const uint FILE_SHARE_DELETE = 0x00000004;
        }
        /// <summary>
        /// Action to take on files that exist, and which action to take when files do not exist.
        /// </summary>
        static class CREATIONDISPOSITION
        {
            public const uint CREATE_NEW = 1;
            public const uint CREATE_ALWAYS = 2;
            public const uint OPEN_EXISTING = 3;
            public const uint OPEN_ALWAYS = 4;
            public const uint TRUNCATE_EXISTING = 5;
        }
        /// <summary>
        /// File attributes and flags for the file.
        /// </summary>
        static class FLAGSANDATTRIBUTES
        {
            public const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
            public const uint FILE_FLAG_OVERLAPPED = 0x40000000;
            public const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
            public const uint FILE_FLAG_RANDOM_ACCESS = 0x10000000;
            public const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
            public const uint FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
            public const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
            public const uint FILE_FLAG_POSIX_SEMANTICS = 0x01000000;
            public const uint FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
            public const uint FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
            public const uint FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000;
        }
 
        static void Main(string[] args)
        {
            IntPtr handle = CreateFile(@"\\.\PHYSICALDRIVE1",
                            DESIREDACCESS.GENERIC_READ | DESIREDACCESS.GENERIC_WRITE,
                            SHAREMODE.FILE_SHARE_READ | SHAREMODE.FILE_SHARE_WRITE,
                            IntPtr.Zero,
                            CREATIONDISPOSITION.OPEN_EXISTING,
                           0,
                IntPtr.Zero);
            FileStream disk = new FileStream(handle, FileAccess.ReadWrite);
            byte[] bt = new byte[512];
            disk.Seek(0, SeekOrigin.Begin);
            disk.Read(bt, 0, 512);
            for (int i = 0; i < bt.Length; i++)
            {
                Console.Write(bt[i].ToString("x2") + " ");
            }
 
            Console.ReadLine();
        }
    }
}

运行结果:

EDK2 202302 来了

今年的3月3日,edk2-stable202302 版本正式发布,需要的朋友可以在下面的链接下载到

https://github.com/tianocore/edk2/releases/tag/edk2-stable202302

新增了如下功能:

看起来和我们 X86 UEFI 开发者关系不大。

前面介绍过下载之后手工补充编译一些第三方依赖库的方法,此外,可以使用下面的方法直接下载完整的代码:

git clone -b edk2-stable202302 https://github.com/tianocore/edk2.git --recursive

这里放置一个下载好的完整代码还包括了AppPkg,第一次运行时最好先使用下面的命令重新编译Build代码所需工具:

edksetup.bat ForceRebuild

我测试了如下几个 Package 都可以正常编译通过:

build -a X64 -t VS2019 -p AppPkg\AppPkg.dsc 
build -a X64 -t VS2019 -p OvmfPkg\OvmfPkgX64.dsc
build -a X64 -t VS2019 -p EmulatorPkg\EmulatorPkg.dsc

这里放一个完整版本,有需要的朋友可以取用。

链接: https://pan.baidu.com/s/1vm1SDSe7PB_MtntsZIZyiQ?pwd=LABZ 提取码: LABZ

CH32V208 USB上手指南

本文介绍 CH32V208 评估板、开发板如何进行 USB 方面的测试。CH32V208 支持2个USB 接口,可以独立工作,其中一个可以作为Host和Device(USB2),另外一个只能作为Device 使用(USB1)。

一.硬件接口介绍。板子上的 USB 接口分布如下:

CH32V208 评估板

需要注意的是如下几点:

  1. P5和J1 是连通的(USB2),换句话它两个实际上是同一个。虽然 P5 是USB 母头,但是可以通过转接为公头作为设备使用:
CH32V208 J1和P5 是连通的

2.P1(USB1),虽然是USB母头,但是只能当作 USB Device 使用,不支持 USB Host功能;

CH32V208 P1 USB TypeA Port

两个USB 都可以作为USB下载接口使用。

二.代码的下载

官方提供了下面的例程:

|-- USBD

|      |-- CH372:模拟自定义USB设备(CH372设备),端点1,3下传,2,4上传,端点1下传的数据从端点3上传,不取反,端点2下传的数据从端点4上传,取反。

|      |-- Compatibility_HID:模拟HID设备,数据通过串口上下传

|      |-- CompositeKM:模拟键盘和鼠标,使用IO模拟按键,同时模拟数据可以通过串口2上传。

|      |-- MSC_U-Disk: 模拟简单U盘,可选使用片内Flash或外部SPI-Flash

|-- USBFS

|      |-- DEVICE

|      |      |-- CH372:模拟自定义USB设备(CH372设备),端点1,3下传,2,4上传,端点1下传的数据从端点3上传,不取反,端点2下传的数据从端点4上传,取反。

|      |      |-- Compatibility_HID:模拟HID设备,数据通过串口上下传。

|      |      |-- CompositeKM:模拟键盘和鼠标,使用IO模拟按键,同时模拟数据可以通过串口2上传。

|      |      |-- MSC_U-Disk: 模拟简单U盘,可选使用片内Flash或外部SPI-Flash

|      |      |-- SimulateCDC:模拟一个CDC串口,使用串口1收发。

|      |      |-- SimulateCDC-HID:模拟一个CDC串口,使用串口1收发,HID中断端点下发数据取反上传。

|      |-- HOST_IAP

|      |      |-- APP: 与HOST_IAP配套使用的APP,工程修改了程序起始位置,编译后需自行将文件转化为bin文件并重命名为APP.bin

|      |      |-- HOST_IAP:  基于U盘读取文件例程整理的主机U盘IAP例程,从U盘内读取名称位APP.bin的文件,写入内部flash,校验后自动跳转。

|      |-- HOST_KM: 主机操作键鼠,获取键鼠上传的端点的数据并打印,支持U口下1级hub

|      |-- HOST_MTP_FileSystem:枚举过程的USB主机到一个支持MTP和PTP协议的设备,支持MTP和PTP协议,并读取其文件

|      |-- Udisk_Lib:U盘文件系统库文件

可以根据需要使用  IDE 打开,编译完成后即可进行烧写,个人推荐方法如下:

  1. 使用USB线连接主机到任意一个USB接口;
  2. 使用关闭开发板供电开关
CH32V208 电源开关

3.按住Download按钮,拨动开关重新上电

CH32V208 下载按钮

4.这时设备中就会出现如下设备

5.使用WCHISPTOOL 选中 CH32V208,需要注意第一次需要去掉保护,之后重复上面步骤2-5即可下载

WCHISPTool 下载工具

下载之后,重启评估板即可工作

Step2FPGA(1) 环境的搭建

一直有学习 FPGA的想法,这次下定决心花时间来学习FPGA。因为 FPGA 相关知识能够帮助更好的理解硬件知识,同时可以使用FPGA来实现验证自己的想法。我选择的开发板和教材是 “至芯携手特权同学Altera Cyclone IV EP4CE6 FPGA开发板NIOSII”:

勇敢的心 伴你玩转 Altera FPGA 书籍和开发板

教材是《勇敢的芯伴你玩转 Altera FPGA》《例说FPGA》《FPGA设计 实战演练(逻辑篇)》,作者都是吴厚航先生(特权同学)。

勇敢的心 伴你玩转 Altera FPGA 和其余两本内容上有所重复

这套书籍和套件是我在 2018年购买的,但是一直没有坚持下去。和学习单片机一样,最大的敌人并不是内容的难度,而是自己是否能够不断坚持学习。因此,这次开始新的系列。

教材上使用的软件比较老(Quartus 13),在新的操作系统上有问题必须升级,这次选择 Quartus 18 的版本。安装文件是QuartusLiteSetup-18.1.0.625-windows.exe

Quartus 18 安装界面

 推荐使用默认路径进行安装,一路Next即可

安装占用大约7G 的空间。

安装选项,推荐默认选项即可

点击 “Finish” 按钮后会自动继续安装 USB-Blaster(下载器):

下载器安装,我购买开发板也选购了配套的下载器

接下来可以从 Windows 菜单中启动 Quartus:

安装后从 Start Menu 上启动

启动后会提示目前没有安装任何 Devices ,需要安装Device Package(这个类似于 Keil 安装好之后,需要安装某一个型号的单片机的支持文件;又好比在 Arduino IDE 上使用 ESP32 需要先从 Board Manger上安装 ESP32支持包):

提示需要安装 Device

关闭上面的软件再回到 Start Menu 选择“Device Installer”

Start menu 启动 “Device Installer”

在这个页面选择存放着 Devices 支持文件的路径(文件后缀是 .qdz)

接下来需要选择要安装的功能如下:

  1. Cyclone IV (其中有开发板的FPGA型号)
  2.  ModelSim (模拟仿真工具,Starter Edition 是免费的,下面的是需要额外购买的收费版本)
这个两个功能会占用硬盘 4.5G 的空间。

再次启动 Quartus,选择 File->Open  Project 打开开发板例子 cy4ex2 项目 cy4.qpf 文件。项目中的各种设置都已经准备好了,使用 Processing -> Start Compilation 即可直接编译:

开始编译
编译成功

接下来使用 “Programmer” 功能将编译后的结果下载到开发板中:

选择这里的 programmer
第一次使用需要用 Setup进行设置
在弹出的界面下拉菜单选择 USB-Blaster
点击 Start 即可下载,右上角会提示当前进度

特别注意,烧写时需要给开发板上电。成功之后,板子上的蜂鸣器一直会发出Beep声。

至此,已经踏出了第一步开始了 FPGA之旅。

一个非常简单的UEFI Application开发框架

通常情况下,我们需要使用 EDK2 来进行 UEFI Shell Application 的开发。这次介绍的是一个用来开发 UEFI Shell Application的框架。项目地址如下:

https://github.com/VioletGiraffe/UEFI-Bootloader

从名字可以看出,作者的目标是用来开发BootLoader,类似的我们可以用来开发UEFI Shell Application。

相比EDK2,这种方法的优点主要是:

  1. 小巧简单。容量只有十几兆,编译起来非常快,另外方便我们研究具体的实现;
  2. 编译环境时 Visual Studio,方便我们阅读代码

具体使用方法是:

1.在 https://github.com/VioletGiraffe/UEFI-Bootloader 下载代码

2.在https://github.com/VioletGiraffe/UEFI-CPP-headers 下载需要的头文件

3.将2下载的内容解压到 UEFI-CPP-headers目录下

4.Visual Studio 打开bootloader.sln文件

5.这个项目作者使用 VS2017,所以需要修改一下 Platform Toolset 为你当前使用的编译器。这里我是用的是 VS2019,修改如下:

6.直接 Build即可生成 BootX64.efi

在WinHost 模拟环境中运行这个程序可以看到屏幕上输出了信息:

有兴趣的朋友可以直接去前面提到的项目主页或者在这里直接下载:

UEFI-CPP-headers-master下载

UEFI-Bootloader-master下载