ESP32 IDF 快速上手指南

  1. idf.py menuconfig 可以修改一些编译参数,比较重要的有:
    a. ESP PSRAM 配置是否使用 PSRAM
    b. Log output 配置调试信息输出等级
  2. Idf.py build 编译代码
  3. Idf.py -p 串口 flash 烧写编译后的结果
  4. Idf.py -p 串口 monitor 打开串口监视器【参考1】
    a. Ctrl+] 退出串口监视器
    b. Ctrl+R 重启ESP32
    c. Ctrl+L 开始记录 Log/停止记录 Log
    上述可以写在一行 idp.py -p COM4 build flash monitor
  5. idf.py clean/fullclean 重新构建项目
  6. idf.py size-components 查看内存、Flash 占用情况
  7. idf.py size-files 更详细的占用情况
  8. idf.py set-target esp32s3 这个命令设置当前编译处理器目标

参考:IDF 监视器
1.https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32/api-guides/tools/idf-monitor.html

Step to UEFI (280)计算精确时间

一些情况下,我们需要得知操作消耗的时间,比如:通过硬盘读写操作耗费的时间能够计算出硬盘的速度。针对这个问题,之前有过研究,例如:GetTime 研究【参考1】、EADK clock()【参考2】。这里再次进行研究。

首先,在MdePkg\Include\Library\TimerLib.h给出了下面两个函数:

/**
  Retrieves the current value of a 64-bit free running performance counter.
  The counter can either count up by 1 or count down by 1. If the physical
  performance counter counts by a larger increment, then the counter values
  must be translated. The properties of the counter can be retrieved from
  GetPerformanceCounterProperties().
  @return The current value of the free running performance counter.
**/
UINT64
EFIAPI
GetPerformanceCounter (
  VOID
  );
/**
  Converts elapsed ticks of performance counter to time in nanoseconds.
  This function converts the elapsed ticks of running performance counter to
  time value in unit of nanoseconds.
  @param  Ticks     The number of elapsed ticks of running performance counter.
  @return The elapsed time in nanoseconds.
**/
UINT64
EFIAPI
GetTimeInNanoSecond (
  IN      UINT64  Ticks
  );
/**
  Retrieves the current value of a 64-bit free running performance counter.
  The counter can either count up by 1 or count down by 1. If the physical
  performance counter counts by a larger increment, then the counter values
  must be translated. The properties of the counter can be retrieved from
  GetPerformanceCounterProperties().
  @return The current value of the free running performance counter.
**/
UINT64
EFIAPI
GetPerformanceCounter (
  VOID
  );
/**
  Converts elapsed ticks of performance counter to time in nanoseconds.
  This function converts the elapsed ticks of running performance counter to
  time value in unit of nanoseconds.
  @param  Ticks     The number of elapsed ticks of running performance counter.
  @return The elapsed time in nanoseconds.
**/
UINT64
EFIAPI
GetTimeInNanoSecond (
  IN      UINT64  Ticks
  );

其中的GetPerformanceCounter() 返回CPU 当前经过的计数值或者说多少个 Ticks,GetTimeInNanoSecond() 函数能将经过的计数值转化为纳秒为单位的时间。

编写一个 UEFI Shell代码进行测试:

1. 在AppPkg.dsc 中加入 TimerLib

CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
  TimerLib|UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
###################################################################################################
#
# Components Section - list of the modules and components that will be processed by compilation
#

2.编写代码如下:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Library/TimerLib.h>
#include  <Library/UefiBootServicesTableLib.h>
INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  )
{
	UINT64	Start=GetPerformanceCounter();
	gBS->Stall(1000000UL);
	Print(L"You have dealyed [%llu]ns\n",
			GetTimeInNanoSecond(GetPerformanceCounter()-Start));
  return(0);
}

具体动作就是首先保存当前的 CPU 计数值,然后延时 1s, 最后输出以纳秒为单位的经过时间。可以看到每次运行耗时会有一点波动。

因为没有使用 CLIB , 所以 Build出来代码体积也比较小,只有9KB.

参考:

  1. https://www.lab-z.com/stugt-2/
  2. https://www.lab-z.com/stu14/

可以指定压缩后文件大小的JPEG工具

大部分JPEG 工具都可以指定图片的压缩质量,从0-100 。但是有时候我们需要一个绝对的数值,比如:500KB 的照片我们期望压缩到40KB。经过努力找到了一个这样的开源工具,在

https://github.com/tjko/jpegoptim

命令行方式工作,比如,我找了一副3.39MB的图片:

使用如下命令:

jpegoptim.exe -dnew -S200 Demo.jpeg

其中 -dnew 指定新生成的图片放在new目录下,-S200指定压缩到200KB(特别注意S需要大写)

压缩之后大小为 193KB

只要不是特别夸张的参数,例如:要把上述图片压缩为40KB,个人感觉都还是可以接收的。

UEIF Tips: EDK2 的 AppPkg

最近打算用 EDK2 202308 编写 UEFI Application,忽然发现无法找到 AppPkg,于是花点时间研究这个问题。这个问题在“VS2019 EDK202008 下的 Libc 编译”【参考1】有提及。具体来说 EDK2 在edk2-stable201903 tag升级到edk2-stable201905 tag这个过程中移除的。接下来的问题就是:如何在最新的 EDK2 环境中使用 AppPkg。具体的方法是,在一下网址下载 AppPkg:

https://github.com/tianocore/edk2-libc/tree/master

解压之后放在EDK2 的根目录下,接下来就可以像之前一样使用 AppPkg 编写 Application了。

参考:

1. https://www.lab-z.com/2019libc/

Turbo Vision

和现在的GUI不同,在 Dos 的时代,使用 ASCII 字符同样能够绘制精美的窗口,这种被称作“CUI”。 Turbo Pascal 7.0 就是典型的代表,它基于Turbo Vision来实现的。

最近看到了一个开源的 Turbo Vision项目,让这套界面在 Windows下再次焕发青春。项目地址是

https://github.com/magiblot/tvision

下面介绍一下编译方法:

  1. 需要准备 VS2019 这种 MS 的编译器
  2. 运行 cmake . -B ./build && -A x64

3. 运行 cmake –build ./build –config Release

编译后生成的 exe 在 build 目录下

有兴趣的朋友可以试试。

=============================================================

2024年2月19日

还可以使用下面的命令来生成 nmake 语法的 makefile

cmake . -B ./build2 -G "NMake Makefiles" ..

之后,进入 build2 目录,运行 nmake 也可以生成 exe

CMAKE 打开具体动作显示的方法

有些项目是通过 CMAKE 来实现的,我们可以通过在根目录下CMakeLists.txt文件添加

set(CMAKE_VERBOSE_MAKEFILE ON) 的方法打开显示,这样就可以看到具体使用的编译命令。例如:

cmake_minimum_required (VERSION 3.5)
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.13.0")
    cmake_policy(SET CMP0077 NEW) # 'option()' honors normal variables.
endif()

set(CMAKE_VERBOSE_MAKEFILE ON)

set(MASTER_PROJECT FALSE)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
    set(MASTER_https://zhuanlan.zhihu.com/p/587290104PROJECT )
endif()

上述方法来自 https://zhuanlan.zhihu.com/p/587290104

Ch9350 控制键盘 LED

CH9350 提供了控制键盘LED(就是 Caps Lock、Scroll Lock、 Num Lock) 的方法。不过非常遗憾的是对应的 DataSheet 语言不详,查阅了网上资料【参考1】【参考2】之后我感觉CH9350 可能是不断升级,所以这部分不是很确定。

最终经过实验,我手上的可以通过对CH9350 发送如下11 Bytes 长度的命令来实现:

0x57, 0xAB, 0x12, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0xAC, 0x20

修改其中的 Byte[7] ,可以实现更改Led的目标。对应的,在 DataSheet有如下介绍:

编写一个测试程序,运行在 ESP32 C3 上:

#include <Arduino.h>

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, RX, TX);
}
char LEDbuffer[11] =
{ 0x57, 0xAB, 0x12, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x00, 0xAC, 0x20
};

byte Index = 0;

void loop() {
  // 预留调试使用
  while (Serial.available()) {
    char c = Serial.read();
    // 简单返回
    if (c == '1') {
      Serial.printf("RX:%d TX:%d\n", RX, TX);
      Serial.println("Test Message");
    }
    //重启
    if (c == '2') {
      ESP.restart();
    }
  }
  LEDbuffer[7]=Index;
  Serial1.write(LEDbuffer, sizeof(LEDbuffer));
  Index++;
  if (Index == 8) {
    Index = 0;
  }
  delay(1000);
}

工作的测试视频在下面的链接可以看到:

https://www.bilibili.com/video/BV1K94y1r731/?share_source=copy_web&vd_source=5ca375392c3dd819bfc37d4672cb6d54

有兴趣的朋友可以试试。

参考:

  1. https://www.wch.cn/bbs/thread-69476-1.html “CH9350 如何設置鍵盤的NumLock/CapsLock/ScrollLock” WCH 官方论坛
  2. https://www.cnblogs.com/gooutlook/p/16805870.html 单片机读取键鼠数据串口

ESP32 IDF SDMMC 测试

官方的 sd_card_example_main.c 代码,在末尾添加如下代码:

	int64_t StartTime=esp_timer_get_time()/1000;
	
	char buffer[64];
	int  filesize,total=0,PicIndex=0;
	FILE *fd = NULL;
	
	for (PicIndex=0;PicIndex<100;PicIndex++) {
		sprintf(buffer,MOUNT_POINT"/m/%04d.jpg",PicIndex);	
		fd = fopen(buffer, "r");
		fseek(fd, 0, SEEK_END);  
		filesize = ftell(fd);  
		rewind(fd);
		fread(Buffer, 1, filesize, fd);
		fclose(fd);
		total=total+filesize;
	}
	ESP_LOGI(TAG, "Read %dKB in %llums",
				total/1024,
				(esp_timer_get_time()/1000-StartTime));

        StartTime=esp_timer_get_time()/1000;
	for (size_t i=0;i<100;i++) {
		sdmmc_read_sectors(card, Buffer, i*128, 128);
	}
	ESP_LOGI(TAG, "Read %dKB in %llums",
				64*100,
				(esp_timer_get_time()/1000-StartTime));

一段是从 m 目录下读取从 0000.jpg 到 0100.jpg ;另外一段是读取从0扇区开始的 100个64KB 扇区。最终运行结果如下:

前者花了 1789ms 读取 1.5MB 的内容;后者711ms能够读取 6.4MB 左右的内容。从这里可以看出文件系统的开销比较大。

几个常见的 ACPI Debug 相关 Table

HEST: Hardware Error Source Table

用于报告系统平台能够产生的错误来源。比如,对于 X86 架构的 MCE和CMC,以及PCIe AER, OS 能够处理的 MSI 和 PCI INTs 错误。

The HEST table enables host firmware to declare all errors that platform component can generate and error signaling for those. The host firmware shall create Error source entries in HEST for each component (such as, processor, PCIe device, PCIe bridge, etc) and each type of error with corresponding error notification mechanism (singling) to OS. These error entries include x86 architectural errors, industry standard errors and generic hardware error source for platform errors. The x86 architectural errors, MCE and CMC, and standard errors PCIe AER, MSI and PCI INTx can be handled by OS natively. The generic hardware error source can be used for all firmware 1st errors and platform errors (such as memory, board logic) that do not have OS native signaling, so they have to use platform signaling SCI or NMI


ERST:Error Record Serialization table

通过这个 Table 给 OS 提供一个能够存放错误的接口,通过这个接口,OS可以将一些信息存放在不受断电影响的存储器上,比如: NVRAM。

The ERST table provides a generic interface for the OS to store and retrieve error records in the platform persistent storage, such as NVRAM (Non-volatile RAM). The error records stored through this interface shall persist across system resets till OS clears it. The OS will use this interface store error information during critical error handling for later extensive error analysis. Host firmware shall provide serialization instruction using ACPI specification defined actions to facilitate read, write and clear error records


EINJ:Error Injection Table
通过这个 Table OS 可以给错误处理函数增加限定条件(具体使用场景我没有碰到过,对此不理解,有知道的朋友可以在评论指教一下)。

One of the important functions required in implementing the error is the ability to inject error conditions by the OS to evaluate the correct functionality of the entire error handling in the platform hardware, host firmware and the OS. The EINJ table interface facilitates error injection from the OS. The host firmware shall provide at minimum one error injection method per error type supported in the platform. The host firmware will provide the generic error serialization instructions to trigger the error in the hardware


BERT:Boot Error Record Table

记录启动时的错误信息并报告给OS,对于我们平常使用的X86来说一般是放在内存中的。

The BERT table provides the interface to report errors that occurred system boot time. In
addition BERT also can be used to report fatal errors that resulted in surprise system reset or shutdown before OS had the chance to see the error. The host firmware shall build this table with error record entries for each error that occurred

上述介绍来自下面这个文档