清醒鼠标

在有些情况下,比如:功耗测试。我们需要让系统一直处于 S0 状态,最好的方法莫过于摇晃鼠标。这次的作品就是一款基于 Dell 廉价鼠标的扩展方案,它每隔10秒摇晃一次鼠标,让你的系统不会休眠。

整体方案设计思路非常简单:鼠标的USB进入CH334 USB HUB 芯片之后,转出来2路USB信号,一路给PAW3515芯片,这是一个鼠标芯片;另外一路给CH552。我们通过编程,让Ch552将自身模拟为鼠标和键盘设备。Ch552键盘设备用于接收主机发过来的键盘LED控制信号;Ch552鼠标设备则是用于模拟鼠标的动作。

电路图如下,左上角是PAW3515鼠标的最小系统,右上角是Ch334 USB Hub芯片的最小系统,下方则是Ch552的最小系统。

PCB 设计如下:


这个的尺寸和戴尔MS116-T 的有线光电鼠标内部的PCB完全相同,用我们的这个PCB替换掉原版即可。焊接之后如下:

编写代码如下:

#ifndef USER_USB_RAM
#error "This example needs to be compiled with a USER USB setting"
#endif

#include "src/userUsbHidKeyboardMouse/USBHIDKeyboardMouse.h"

// 10秒触发一次
#define INTERVAL 10000UL
// 每次动作间隔 20ms
#define ACTION 50

// 触发计时
unsigned long int Elsp = 0;
// 记录当前是否已经触发过
boolean StageAssert[4] = {false, false, false, false};

uint8_t LastLed;
unsigned long LEDAssertElsp = 0;
// 触发状态标志
boolean StartWaken = false;

void setup() {
  USBInit();
  Serial0_begin(115200);
  delay(3000);
  Serial0_println("start");
  LastLed = LedStatus;
}

// 判断是否满足条件
boolean MoveCondition(byte stage) {
  // 条件1. 大于 INTERVAL 给出的时间
  // 条件2. 小于 INTERVAL + (stage + 1)*ACTION 给出的时间
  // 条件3. 之前没有触发过
  if ((millis() - Elsp > INTERVAL + stage * ACTION) &&
      (millis() - Elsp < INTERVAL + (stage + 1)*ACTION) &&
      (StageAssert[stage] == false)) {
    // 标记已经触发过
    StageAssert[stage] = true;
    return true;
  }
  return false;
}


void loop() {
  // 如果发生了 LED 切换
  if (LastLed != LedStatus) {
    Serial0_println("B");
    // 如果 1秒内发生了切换
    if (millis() - LEDAssertElsp < 1000) {
      // 触发状态反转
      StartWaken = !StartWaken;
      Serial0_print(StartWaken);
      Serial0_println("C");
      if (StartWaken == false) {
        // 重置
        for (byte i = 0; i < 4; i++) {
          StageAssert[i] = false;
        }
      } else {
        Elsp = millis();
      }
    }
    // 记录切换时间
    LEDAssertElsp = millis();
    LastLed = LedStatus;
    Serial0_println("E");

  }
  if (StartWaken != false) {
    if (MoveCondition(0) == true) {
      // 向右移动
      Mouse_move(100, 0);
    } else   if (MoveCondition(1) == true) {
      // 向下移动
      Mouse_move(0, 100);
    } else   if (MoveCondition(2) == true) {
      // 向左移动
      Mouse_move(-100, 0);
    } else   if (MoveCondition(3) == true) {
      // 向上移动
      Mouse_move(0, -100);
      // 重置
      for (byte i = 0; i < 4; i++) {
        StageAssert[i] = false;
      }
      Elsp = millis();
    }
  }
}

对应的功能有一个开关,使用连续按下键盘上的 Caps/NumLock/Scroll 两次即可触发。这里使用到了USB键盘的一个有趣的特性:操作系统会在全部的键盘中进行同步。比如,系统中有3个USB键盘,当你在其中一个键盘按下 Caps 按键之后,操作系统会通知其余两个键盘要求更改 Caps LED。使用USB抓包软件可以看到,下图就是 Windows主机端用于通知 Ch552 键盘要求更改LED的命令,我按下2次,Byte0 是Report ID, Byte1 是键盘LED的状态。

对应的代码在 \Ch552MSWaken\src\userUsbHidKeyboardMouse\USBHIDKeyboardMouse.c ,收到来自 EndPoint 1 的 Out 数据后,会更改   LedStatus 数值,以便主程序进行处理。

void USB_EP1_OUT() {

  //Serial0_println("A");

  LedStatus=Ep1Buffer[1]; //LABZ_Debug

  if (U_TOG_OK) // Discard unsynchronized packets

  {

  }

}

经过改造,你得到的是一个表面上看起来和正经鼠标一摸一样的鼠标,同时它也有着和正经鼠标一摸一样的功能,但是当你触发之后,它会每隔10秒晃动一次。

电路图和PCB 下载

完整代码下载

工作的测试视频可以在这里看到

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

一种制作 Memtest86 启动盘的方法

Memtest86 是一个非常优秀的内存测试软件,可以用来测试内存的稳定性。美中不足的是它自带的U盘制作软件存在一些缺陷。

  Memtest86 官方启动盘制作界面

制作完成界面

分区结构

可以看到,对于 64G U盘只使用了最前面的 256MB,后面的完全浪费掉了。

为了避免这种情况,经过研究,可以手工制作 MemTest86 的光盘镜像,然后配合 Ventoy 制作启动盘,在使用时,先启动到 Ventoy ,然后选择启动 MemTest86 的镜像即可。

这样做出来的U盘不会浪费空间,你可以在上面继续放置 Windows 安装文件等等。

当然,这次介绍的方法还是有一定局限性的:因为模拟为光盘,是只读设备,因为无法保存 Log 或者测试结果。如果你有保存结果或者截图的需求,那么还是需要用官方提供的方法。

工作的测试视频

本文提到的 Memtest86 ISO 可以在这里下载:

NAudio 编写指定播放音频的设备

这个例子实现了在用户指定的设备上播放音频。比如,可以选择从耳机中播放。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using NAudio;
using NAudio.Wave;

namespace ConsoleApp1
{
    class Program
    {
        
        static void Main(string[] args)
        {
            for (int n = 0; n < WaveOut.DeviceCount; n++)
            {
                Console.WriteLine(n + " : " + WaveOut.GetCapabilities(n).ProductName);
            }
            while (true)
            {
                int i = 0;
                do
                {
                    Console.WriteLine("Choose device:");
                } while (!int.TryParse(Console.ReadLine(), out i) || i >= WaveOut.DeviceCount || i < 0);

                WaveOutEvent waveOutEvent = new WaveOutEvent();
                waveOutEvent.DeviceNumber = i;

                Console.WriteLine("Play on "+WaveOut.GetCapabilities(i).ProductName);

                using (var audioFileReader = new AudioFileReader(@"c:\temp\1990.mp3"))
                {
                    // Play mp3 in the device
                    waveOutEvent.Init(audioFileReader);
                    waveOutEvent.Play();

                    // Wait until the end
                    while (waveOutEvent.PlaybackState == PlaybackState.Playing)
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                }
            }
        }
    }
}

NAudio 枚举音频输入输出设备

使用 VS2019 C#,安装 NAudio库后在  Console 下面枚举 Audio Input 和 Output 的代码:

完整代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using NAudio;
using NAudio.CoreAudioApi;
using MMDevice = NAudio.CoreAudioApi.MMDevice;
using MMDeviceEnumerator = NAudio.CoreAudioApi.MMDeviceEnumerator;

using NAudio.Wave;

namespace ConsoleApp1
{
    class Program
    {

        static void Main(string[] args)
        {
            //定义一个 dataFlow对象
            MMDeviceEnumerator enumerator = new MMDeviceEnumerator();

            IEnumerable<MMDevice> playBackList;
            //获取声音输出设备
            playBackList = enumerator.EnumerateAudioEndPoints(DataFlow.Render, NAudio.CoreAudioApi.DeviceState.Active).ToArray();

            Console.WriteLine("Output device list:");
            foreach (var ad in playBackList)
            {
                Console.WriteLine("   "+ad.FriendlyName.ToString());
            }

            IEnumerable < MMDevice > captureList;
            captureList = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, NAudio.CoreAudioApi.DeviceState.Active).ToArray();;
            Console.WriteLine("Input device list:");
            foreach (var ad in captureList)
            {
                Console.WriteLine("   " + ad.FriendlyName.ToString());
            }

            // 更简单的方法
for (int n = 0; n < WaveOut.DeviceCount; n++)
            {
                Console.WriteLine(n + " : " + WaveOut.GetCapabilities(n).ProductName);
            }
            for (int n = 0; n < WaveIn.DeviceCount; n++)
            {
                Console.WriteLine(n + " : " + WaveIn.GetCapabilities(n).ProductName);
            }


            Console.ReadLine();

        }
    }
}

运行结果:

在系统中直接查看:

参考:

  1. https://blog.csdn.net/u011465910/article/details/127859286   C# Audio全自动化测试——1. 枚举Audio设备
  2. https://cloud.tencent.com/developer/information/%E5%9C%A8%E6%B7%BB%E5%8A%A0%2F%E5%88%A0%E9%99%A4%E5%A3%B0%E9%9F%B3%E8%AE%BE%E5%A4%87%E5%90%8E%EF%BC%8C%E5%A6%82%E4%BD%95%E5%9C%A8NAudio%E4%B8%AD%E9%80%89%E6%8B%A9%E6%AD%A3%E7%A1%AE%E7%9A%84%E5%A3%B0%E9%9F%B3%E8%BE%93%E5%87%BA%E8%AE%BE%E5%A4%87%EF%BC%9F 在添加/删除声音设备后,如何在NAudio中选择正确的声音输出设备?
  3. https://blog.csdn.net/LiChangGG/article/details/100132901

ESP32Sx USB Host 发送数据

第一步,使用 usb_host_transfer_alloc() 函数创建准备数据的结构体,函数原型如下:

esp_err_tusb_host_transfer_alloc(size_t data_buffer_size, int num_isoc_packets, usb_transfer_t **transfer)

第一个参数是数据大小;第二个是 ISO 数据包的个数,如果不用音频视频这里为0;第三个是数据结构体。

第二步,填写usb_transfer_t对应的结构体;

第三步,发送。可以使用 usb_host_transfer_submit_control 和 usb_host_transfer_submit。

需要特别注意的是,如果你使用 usb_host_transfer_submit_control 发送 Setup数据,那么它的 Size计算上比较特别。例如,我要发送下面 05 83 00 00 00 00 作为有效数据,那么 esp_err_tusb_host_transfer_alloc给的size_t 参数必须是 8+6,而发送的缓冲区中是 21 09 05 03 01 00 06 00 05 83 00 00 00 00。

此外,如果使用 usb_host_transfer_submit(这个功能是对非0的端点发送数据),esp_err_tusb_host_transfer_alloc就无需考虑这么多,要发送多少直接作为参数即可。

参考:

1. https://docs.espressif.com/projects/esp-idf/zh_CN/v5.1/esp32s3/api-reference/peripherals/usb_host.html

Step to UEFI (293)DSDT 是如何打包到 UEFI 中的

UEFI 中的 AML 文件是如何打包的

以OvmfPkg 中的 Bhyve 为研究对象,编译命令:

build -a X64 -p OvmfPkg\Bhyve\BhyveX64.dsc -t VS2019

在\OvmfPkg\Bhyve\AcpiTables\ 目录下我们能看到 DSDT.ASL 这样的文件。对应的,在 \Build\BhyveX64\DEBUG_VS2019\X64\OvmfPkg\Bhyve\AcpiTables\AcpiTables\Makefile 中可以看到编译方法。首先是从 asl生成 aml, 然后是使用 GenSec 成成 RAW 的 SECTION

if exist $(OUTPUT_DIR)\Dsdt.aml GenSec -s EFI_SECTION_RAW -o c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC2.1.raw $(OUTPUT_DIR)\Dsdt.aml

例如:

7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.2.raw : $(OUTPUT_DIR)\Facs.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.3.raw : $(OUTPUT_DIR)\Hpet.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.4.raw : $(OUTPUT_DIR)\Madt.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.5.raw : $(OUTPUT_DIR)\Mcfg.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.6.raw : $(OUTPUT_DIR)\Spcr.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC2.1.raw $(OUTPUT_DIR)\Dsdt.aml

最终使用 GenFFs 把他们打包到一个  FFS 中

	GenFfs -t EFI_FV_FILETYPE_FREEFORM -g 7E374E25-8E01-4FEE-87F2-390C23C606CD -o c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CD.ffs -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.1.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.2.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.3.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.4.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.5.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.6.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC2.1.raw

3.2.3.1. EFI_FFS_FILE_HEADER

Summary

Each file begins with a header that describes the state and contents of the file. The header is 8-byte aligned with respect to the beginning of the firmware volume.

Prototype

typedef struct {
  EFI_GUID                 Name;
  EFI_FFS_INTEGRITY_CHECK  IntegrityCheck; //UINT16
  EFI_FV_FILETYPE          Type;           //UINT8
  EFI_FFS_FILE_ATTRIBUTES  Attributes;     //UINT8
  UINT8                    Size[3];
  EFI_FFS_FILE_STATE       State;          //UINT8
} EFI_FFS_FILE_HEADER;

typedef struct {
  EFI_GUID                 Name;
  EFI_FFS_INTEGRITY_CHECK  IntegrityCheck;
  EFI_FV_FILETYPE          Type;
  EFI_FFS_FILE_ATTRIBUTES  Attributes;
  UINT8                    Size[3];
  EFI_FFS_FILE_STATE       State;
  UINT64                   ExtendedSize;
} EFI_FFS_FILE_HEADER2;

上述的区别在于“EFI_FIRMWARE_FILE_SYSTEM3_GUID indicates support for FFS_ATTRIB_LARGE_SIZE and thus support for files 16MB or larger. EFI_FIRMWARE_FILE_SYSTEM2_GUID volume does not contain large files. Files 16 MB or larger use a EFI_FFS_FILE_HEADER2 and smaller files use EFI_FFS_FILE_HEADER.EFI_FIRMWARE_FILE_SYSTEM2_GUID allows backward compatibility with previous versions of this specification”,这里因为文件小,肯定是EFI_FFS_FILE_HEADER。

其中 0x1B92=7058 就是这个FFS 文件的大小。接下来的就是一个 Section 的内容了。

typedef struct {
  UINT8                    Size[3];
  EFI_SECTION_TYPE         Type;
} EFI_COMMON_SECTION_HEADER;

typedef struct {
  UINT8                    Size[3];
  EFI_SECTION_TYPE         Type;
  UINT32                   ExtendedSize;
} EFI_COMMON_SECTION_HEADER2;

从内容上来说,就是7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.1.raw 这个文件的内容:

有了上述的分析,这里编写一个从 FFS 解析 AML 的工具。使用 VC 编写,VS2019 编译通过

#include <windows.h>  
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 

#pragma warning(disable:4996)

#pragma pack(1)
//
// Basical data type definitions introduced in UEFI.
//
typedef struct {
    UINT32  Data1;
    UINT16  Data2;
    UINT16  Data3;
    UINT8   Data4[8];
} EFI_GUID;

//
// Used to verify the integrity of the file.
//
typedef union {
    struct {
        UINT8   Header;
        UINT8   File;
    } Checksum;
    UINT16    Checksum16;
} EFI_FFS_INTEGRITY_CHECK;

typedef UINT8 EFI_FV_FILETYPE;
typedef UINT8 EFI_FFS_FILE_ATTRIBUTES;
typedef UINT8 EFI_FFS_FILE_STATE;

typedef struct {
    EFI_GUID                 Name;
    EFI_FFS_INTEGRITY_CHECK  IntegrityCheck; //UINT16
    EFI_FV_FILETYPE          Type;           //UINT8
    EFI_FFS_FILE_ATTRIBUTES  Attributes;     //UINT8
    UINT8                    Size[3];
    EFI_FFS_FILE_STATE       State;          //UINT8
} EFI_FFS_FILE_HEADER;

typedef UINT8 EFI_SECTION_TYPE;

typedef struct {
    UINT8                    Size[3];
    EFI_SECTION_TYPE         Type;
} EFI_COMMON_SECTION_HEADER;


#pragma pack()

void SaveBufferToFile(UINT8 Index, char* p, UINT16 Len) {
    FILE* SaveTo;
    char NumBuf[20];
    sprintf(NumBuf, "%d.rom",Index);
    SaveTo=fopen(NumBuf, "wb");
    fwrite(p,1,Len, SaveTo);
    fclose(SaveTo);
}

int main(int argc, char* argv[])
{
    if (argc != 2) {
        printf("Please input the FFS name\n");
        return 1;
    }

    FILE* file;
    char* buffer;
    long fileLen;

    // 打开文件
    file = fopen(argv[1], "rb");  // 以二进制模式读取文件
    if (!file) {
        printf("Unable to open file %s\n", argv[1]);
        return 2;
    }

    // 获取文件大小
    fseek(file, 0, SEEK_END);  // 移动到文件末尾
    fileLen = ftell(file);     // 当前位置即文件大小
    fseek(file, 0, SEEK_SET);  // 移动回文件开头
    printf("%s file size is %d\n", argv[1], fileLen);

    // 分配内存
    buffer = (char*)malloc(fileLen);
    if (!buffer) {
        printf("Memory allocation failed\n");
        fclose(file);
        return 3;
    }

    // 读取文件内容到内存
    fread(buffer, fileLen, 1, file);
    fclose(file);  // 关闭文件

    char* pFile= &buffer[sizeof(EFI_FFS_FILE_HEADER)];
    EFI_COMMON_SECTION_HEADER* pSection;
    UINT8 Index = 0;

    while (pFile - buffer<fileLen) {
        pSection = (EFI_COMMON_SECTION_HEADER*) pFile;
        // 输出起始位置,长度
        printf("Section start at %x %x\n", 
            pFile- buffer,
            (pSection->Size[0])+ (pSection->Size[1]<<8)+(pSection->Size[2]<<16));
        // 这里我们要保存去掉头的 Section
        SaveBufferToFile(Index, &buffer[pFile - buffer]+4, (pSection->Size[0]) + (pSection->Size[1] << 8) + (pSection->Size[2] << 16)-4);
        Index++;
        if ((pFile - buffer + (pSection->Size[0]) + (pSection->Size[1] << 8) + (pSection->Size[2] << 16)) % 4 != 0) {
            pFile = &buffer[((pFile - buffer + (pSection->Size[0]) + (pSection->Size[1] << 8) + (pSection->Size[2] << 16))/4+1)*4];
        }
        else {
            pFile = &buffer[pFile - buffer + (pSection->Size[0]) + (pSection->Size[1] << 8) + (pSection->Size[2] << 16)];
        }
        
    }

    return 0;
}

例如,使用这个工具分解 QEMU中Bhyve 的7E374E25-8E01-4FEE-87F2-390C23C606CD.ffs:

可以生成 0.rom-6.rom,就是打包起来的 AML 文件:

本文提到的可执行程序源代码:

本文提到的编译后的EXE 和FFS文件:

参考:

  1. https://uefi.org/specs/PI/1.8/V3_Code_Definitions.html#firmware-file

Arduino on Ch32V305

这篇介绍如何使用 Arduino 在Ch32V305上编写代码,不过需要明确的一点是:目前对于Ch32V30X系列 Arduino 支持还并不完善,在使用时会遇到各种各样的问题。

项目地址在  https://github.com/openwch/arduino_core_ch32

首先,将这个项目加入到”Additional Boards Managers URLs” 中:

接下来在 Board Manger 中搜索安装这个板子:

之后就可以进行使用了。

原始的库并不支持 USB 设备,这里需要我们直接编程:

#include "src\\userUsbKB\\ch32v30x_usbhs_device.h"
#include "src\\userUsbKB\\usbd_composite_km.h"

uint8_t  KeyPress[8] = {0x08, 0, 0, 0, 0, 0, 0, 0};
uint8_t  KeyRelease[8] = {0, 0, 0, 0, 0, 0, 0, 0};

void setup() {
  Serial.begin(115200);
  Serial.println("Start");
  pinMode(D18, INPUT_PULLUP);
  /* Initialize system configuration */
  SystemCoreClockUpdate( );
  NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
  //Delay_Init( );

  /* Initialize USBHS interface to communicate with the host  */
  USBHS_RCC_Init( );
  USBHS_Device_Init( ENABLE );
  USB_Sleep_Wakeup_CFG( );
}

unsigned long PressElsp = 0;
unsigned long ReleaseElsp = 0;

void loop() {
  if ( USBHS_DevEnumStatus )
  {

    /* Handle keyboard lighting */
    KB_LED_Handle( );

    if ((digitalRead(D18) == LOW) && (PressElsp == 0) &&(millis()-ReleaseElsp>100)) {
      USBHS_Endp_DataUp( DEF_UEP1, KeyPress, sizeof( KeyPress ), DEF_UEP_CPY_LOAD );
      PressElsp = millis();
      Serial.println("a");
    }

    if ((PressElsp != 0) && (millis() - PressElsp > 1000)) {
      USBHS_Endp_DataUp( DEF_UEP1, KeyRelease, sizeof( KeyRelease ), DEF_UEP_CPY_LOAD );
      PressElsp = 0;
      ReleaseElsp=millis();
      Serial.println("b");
    }
  }
}

关键的代码如下,简单的说就是如果D18 触发,那么就发送键盘数据给PC,相当于按下了 Win键;1秒之后,再发送全为0的数据包,这个相当于发送抬起信息:

    if ((digitalRead(D18) == LOW) && (PressElsp == 0) &&(millis()-ReleaseElsp>100)) {
      USBHS_Endp_DataUp( DEF_UEP1, KeyPress, sizeof( KeyPress ), DEF_UEP_CPY_LOAD );
      PressElsp = millis();
      Serial.println("a");
    }

    if ((PressElsp != 0) && (millis() - PressElsp > 1000)) {
      USBHS_Endp_DataUp( DEF_UEP1, KeyRelease, sizeof( KeyRelease ), DEF_UEP_CPY_LOAD );
      PressElsp = 0;
      ReleaseElsp=millis();
      Serial.println("b");
}

有兴趣的朋友不妨尝试一下这个芯片,主要的优点是:速度足够快(最高 144Mhz),资源比较多(128KB Flash,32K 内存),体积小(TSSOP20封装),内置了USB High Speed (真 2.0)。目前的缺点是 Arduino开发环境并不完善,很多需要自己摸索。

RaptorLake_P I2C Shell 测试工具 

这个工具目前可以让你在 UEFI Shell 下扫描 PCH I2C 总线上的设备,给出扫描到的设备地址。可以用来判断 I2C 设备是否正常。

在运行之前请保证

  1. PCH 对应的 I2C 已经 Enable, 在 Shell 下能够看到对应的PCI Controller;
  2. I2C 对应的 GPIO 已经设置为 Native UART 功能。

使用方法:

rpli2ct <需要扫描的 I2C Num>

例如,下面就是一个例子,在 I2C5上发现了一些设备。

UEFI Application 下载(无源代码)

Ch32V305 USART1 重映射

手册上有表明 USART引脚,这里选择PB15 为 TX, PA8 为 RX。

根据【参考1】给出的方法,编写代码:

void setup() {
  GPIO_InitTypeDef GPIO_InitStructure={0};
  
  Serial.begin(115200);

  //打开重映射时钟和USART重映射后的I/O口引脚时钟,
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

  //2. I/O口重映射开启.

  GPIO_PinRemapConfig(GPIO_Remap_USART1_HighBit, ENABLE);

  //3.配制重映射引脚, 这里只需配置重映射后的I/O,原来的不需要去配置.

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void loop() {
  char sMsg1[] = "www.lab-z.com1";
  Serial.println(sMsg1);
  delay(1000);
}

USB 转串口的板子 RX 连接到 PB15上,在PC端即可收到字符串。

参考:

1. https://www.cnblogs.com/jeakon/archive/2012/10/04/2816798.html

SlimBootLoader编译环境的架设

本文根据【参考1】,实验和编写:

  1. 需要安装如下软件
    • VS2019 需要保证安装VC
    • Python 3.8.10 需要安装到 c:\Python38 目录下, 需要选择将路径加入系统变量
  • Nasm 2.16.01 需要安装到 c:\nasm 目录下
  • IASL 20190509 需要安装到 c:\asl 目录下
  • OpenSSL 需要安装到c:\openssl 目录下
  • Git 下面这个设置需要选择为“Checkout as-is, commit as-is” 其余默认即可

2. 解压 slimboot-master 到 c:\buildbs 目录下名为 sbl 的目录下

3.打开 x86 Native Tools Command Prompt for VS2019

4.生成一个 SBLKey

  • 创建一个目录:Mkdir  sblkey   

  • 在这个目录下生成Key:Python BootLoaderCorePkg\Tools\Generatekeys.py -k sblkey

 这样会在sblkey目录下生成一堆 Key

5.接下来给 Qemu 生成一个BIOS 试试

  • 需要指定key目录:set SBL_KEY_DIR=c:\buildbs\sbl\sblkey

  • 使用Python BuildLoader.py build qemu
  • 在这个过程中需要从Git下载一些代码,对于不方便联网的人可以直接将 Download.zip 解压到 c:\BuildBs 下面。第一次使用 git 会提示需要设置邮件地址,可以用  git config –global user.email “you@example.com”  设置一个假地址跳过

6.最终的结果如下,表示编译成功

参考:

1. https://slimbootloader.github.io/developer-guides/build-system.html