可以看到,上面的 Descriptor 红色部分和其余部分的区别只是 Report ID 不同。 Report ID 是USB HID 设备用来区分功能的一个设计。例如,我们经常遇到同时带有键盘和鼠标的混合设备。通常它的Descriptor 写法就是:
Report ID 1
鼠标数据[0], 鼠标数据[1], ……鼠标数据[M-1],
Report ID 2
键盘数据[0], 键盘数据[1],……键盘数据[N-1],
当设备上有鼠标动作发生时,鼠标动作描述为 :M 长度的鼠标数据;设备对主机的报告是:
1, 鼠标数据[0], 鼠标数据[1],…… 鼠标数据[M-1]
当设备上有键盘动作发生时,键盘动作描述为 :N 长度的键盘数据;设备对主机的报告是:
2,键盘数据[0], 键盘数据[1],…… 键盘数据[N-1]
当然,你还可以继续给这个设备加入功能。比如:Report ID 3 , 键盘数据[0], 键盘数据[1],……键盘数据[P-1] 这样就又加入了一个键盘的功能(实际上这样做是有意义的,比如,通常的USB键盘数据包只有8个按键信息,如果我们同时按下9个按键就会超出它的运载能力。这就是一种“键位冲突”。如果你的设备同时声明了2个键盘,那么可以用将第九个按键的信息放置在第二个键盘的数据中发出去。如果你肯声明三个键盘设备,每个带有8个按键,理论上对于人类是完全够用的)。显而易见,通过 Report ID 主机能够分清楚当收到的数据属于哪个设备。
这次的设计,USB 手柄产生数据如下:
1, 手柄数据[0], 手柄数据[1],…… 手柄数据[M-1]
(第一个1是来自USB 手柄 Descriptor 的 Report ID 1)
我们无需解析了解每一位的含义,去掉最前面的 Report ID 然后将剩余数据转发出去即可。具体代码如下:
基本原理:通过 GetSystemPowerStatus 这个 API 获得当前系统的电池电量信息,以1秒为间隔进行查询,查询结果保存到文件中同时输出到屏幕上。收到按键后退出。
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
namespace GeetBatter
{
class Program
{
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_POWER_STATUS
{
public byte ACLineStatus;
public byte BatteryFlag;
public byte BatteryLifePercent;
public byte Reserved1;
public int BatteryLifeTime;
public int BatteryFullLifeTime;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool GetSystemPowerStatus([In, Out] ref SYSTEM_POWER_STATUS systemPowerStatus);
static void Main(string[] args)
{
// The system power charger struct
SYSTEM_POWER_STATUS status = new SYSTEM_POWER_STATUS();
Boolean Running = true;
DateTime currentTime;
String Result;
String Filename = DateTime.Now.ToString("HHmmss")+".txt";
FileStream fs = new FileStream(Filename, FileMode.Append);
StreamWriter wr = new StreamWriter(fs);
while (Running)
{
Thread.Sleep(1000);
while (Console.KeyAvailable)
{
Console.ReadKey(true);
Running = false;
}
// Get Power status from Kernell
GetSystemPowerStatus(ref status);
currentTime = DateTime.Now;
Result = currentTime.ToString("HH:mm:ss") + "," + status.BatteryLifePercent;
Console.WriteLine(Result);
wr.WriteLine(Result);
}
wr.Close();
Console.WriteLine("Program would exit");
Console.ReadLine();
}
}
}
#include <arduinoFFT.h>
#include "fabgl.h"
// VGA 显示
fabgl::VGA16Controller DisplayController;
Canvas cv(&DisplayController);
//ZivDebug #define SAMPLES 1024 // Must be a power of 2
#define SAMPLES 256 // Must be a power of 2
#define SAMPLING_FREQ 40000 // Hz, must be 40000 or less due to ADC conversion time. Determines maximum frequency that can be analysed by the FFT Fmax=sampleF/2.
#define AMPLITUDE 1000 // Depending on your audio source level, you may need to alter this value. Can be used as a 'sensitivity' control.
#define AUDIO_IN_PIN A0 // Signal in on this pin
#define NOISE 500 // Used as a crude noise filter, values below this are ignored
const uint8_t kMatrixWidth = 16; // Matrix width
const uint8_t kMatrixHeight = 16; // Matrix height
#define NUM_BANDS 16 // To change this, you will need to change the bunch of if statements describing the mapping from bins to bands
#define BAR_WIDTH (kMatrixWidth / (NUM_BANDS - 1)) // If width >= 8 light 1 LED width per bar, >= 16 light 2 LEDs width bar etc
#define TOP (kMatrixHeight - 0) // Don't allow the bars to go offscreen
// Sampling and FFT stuff
unsigned int sampling_period_us;
byte peak[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // The length of these arrays must be >= NUM_BANDS
int oldBarHeights[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int bandValues[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime;
arduinoFFT FFT = arduinoFFT(vReal, vImag, SAMPLES, SAMPLING_FREQ);
unsigned long int Elsp1;
int h[16];
int Height,Width;
void setup() {
Serial.begin(115200);
sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQ));
DisplayController.begin();
// 设定分辨率
DisplayController.setResolution(VGA_640x400_60Hz);
Height=cv.getHeight();
Width=cv.getWidth();
cv.setBrushColor(Color::Red );
// get a font for about 40x14 text screen
cv.selectFont(&fabgl::FONT_8x8);
cv.setGlyphOptions(GlyphOptions().FillBackground(true));
}
void loop() {
static int64_t stime = esp_timer_get_time();
static int FPS = 0;
static int FPSCounter = 0;
// Reset bandValues[]
for (int i = 0; i < NUM_BANDS; i++) {
bandValues[i] = 0;
}
// Sample the audio pin
for (int i = 0; i < SAMPLES; i++) {
newTime = micros();
vReal[i] = analogRead(AUDIO_IN_PIN); // A conversion takes about 9.7uS on an ESP32
vImag[i] = 0;
while ((micros() - newTime) < sampling_period_us) {
/* chill */
}
}
// Compute FFT
FFT.DCRemoval();
FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(FFT_FORWARD);
FFT.ComplexToMagnitude();
// Analyse FFT results
for (int i = 2; i < (SAMPLES / 2); i++) { // Don't use sample 0 and only first SAMPLES/2 are usable. Each array element represents a frequency bin and its value the amplitude.
if (vReal[i] > NOISE) { // Add a crude noise filter
//16 bands, 12kHz top band
if (i <= 2 ) bandValues[0] += (int)vReal[i];
if (i > 2 && i <= 3 ) bandValues[1] += (int)vReal[i];
if (i > 3 && i <= 5 ) bandValues[2] += (int)vReal[i];
if (i > 5 && i <= 7 ) bandValues[3] += (int)vReal[i];
if (i > 7 && i <= 9 ) bandValues[4] += (int)vReal[i];
if (i > 9 && i <= 13 ) bandValues[5] += (int)vReal[i];
if (i > 13 && i <= 18 ) bandValues[6] += (int)vReal[i];
if (i > 18 && i <= 25 ) bandValues[7] += (int)vReal[i];
if (i > 25 && i <= 36 ) bandValues[8] += (int)vReal[i];
if (i > 36 && i <= 50 ) bandValues[9] += (int)vReal[i];
if (i > 50 && i <= 69 ) bandValues[10] += (int)vReal[i];
if (i > 69 && i <= 97 ) bandValues[11] += (int)vReal[i];
if (i > 97 && i <= 135) bandValues[12] += (int)vReal[i];
if (i > 135 && i <= 189) bandValues[13] += (int)vReal[i];
if (i > 189 && i <= 264) bandValues[14] += (int)vReal[i];
if (i > 264 ) bandValues[15] += (int)vReal[i];
}
}
// Process the FFT data into bar heights
for (byte band = 0; band < NUM_BANDS; band++) {
// Scale the bars for the display
int barHeight = bandValues[band] / AMPLITUDE;
if (barHeight > TOP) barHeight = TOP;
// Small amount of averaging between frames
barHeight = ((oldBarHeights[band] * 1) + barHeight) / 2;
// Move peak up
if (barHeight > peak[band]) {
peak[band] = min(TOP, barHeight);
}
h[band] = barHeight;
// Save oldBarHeights for averaging later
oldBarHeights[band] = barHeight;
}
if (millis() - Elsp1 > 10) {
for (byte band = 0; band < NUM_BANDS; band++)
if (peak[band] > 0) peak[band] -= 1;
cv.setBrushColor(Color::Black );
cv.clear();
cv.setBrushColor(Color::Red );
for (int i = 0; i < 16; i++) {
if (h[i] != 0) {
//cv.fillRectangle(cv.getWidth()*i / 16, 0, cv.getWidth() * (i + 1) / 16, cv.getHeight() *h[i] / 16);
cv.fillRectangle(Width*i / 16, Height -1 , Width * (i + 1) / 16-1, (Height-1) *(16-h[i]) / 16);
}
Serial.print(h[i], HEX);
Serial.print("");
}
Serial.println("");
Elsp1 = millis();
}
if (esp_timer_get_time() - stime > 1000000) {
// calculate FPS
FPS = FPSCounter;
stime = esp_timer_get_time();
FPSCounter = 0;
}
++FPSCounter;
// display test state and FPS
cv.setPenColor(Color::Blue);
cv.setBrushColor(Color::Yellow);
cv.drawTextFmt(80, 5, "%d FPS ", FPS);
}
Visual Studio 的 C 支持 #pragma message() 宏可以用来输出一些信息。于是编写一个代码进行测试:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
/***
Print a welcoming message.
Establishes the main structure of the application.
@retval 0 The application exited normally.
@retval Other An error occurred.
***/
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
)
{
#pragma message (__FILE__)
return(0);
}