using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApp9
{
class Program
{
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
// 让线程暂停5秒(5000毫秒)
Thread.Sleep(5000);
stopwatch.Stop();
// 获取运行时间
TimeSpan ts = stopwatch.Elapsed;
// 格式化并显示时间
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10);
Console.WriteLine("Time: " + elapsedTime);
Console.ReadLine();
}
}
}
作者: ziv2013
Step to UEFI (291)Cpp UEFI 007 类
C++ 的类可以看作是一种特别的数据类型。基本的结构如下:
Class 类名 {
Private:
XXXX //公有成员
Public:
YYYY://私有成员
Protected:
ZZZZ://保护成员
}
Public 成员相当于 Struct 结构体定义的,可以用 a.x 这种形式访问(public访问权限是全局的);
Private 成员不能直接被类的实体访问,也不能被子类的实体访问,但是可以被类的成员函数访问(private访问权限就是对内不设防,对外设防的);
Protected 成员不能被类的实体访问,但是可以被子类访问,也可以被类的成员函数访问(protected访问权限就是对内不受保护,对外受保护的)
这样设计的目的是:
1、访问权限作用,保护内部资源
(1)private的成员是class内部使用,外部没必要直接访问(读取或赋值),所以干脆在语法上让你看不见
(2)访问权限的保护是一种语法层面的保护,而非硬件上限制访问,硬件做不了这么细致
(3)最终目的也是为了整个程序更安全
2、访问权限作用, 隐藏外部无需关心的细节
(1)将class内部使用而外部绝不会用到的成员变量隐藏起来,以免干扰外部或者不小心被外部修改了
(2)隐藏细节可以降低使用类库的人的难度,调用时只看到对我有用的东西
(3)这个隐藏其实是把class的作者和使用者专业分工,是很有必要的
3、这就是面向对象的封装特性
(1)封装特性的体现之一就是抽象,抽象的一层意思就是隐藏掉不必要的细节
(2)封装特性的体现之一就是组织和保护,便于整个整体和外部更合理的交流
参考:
- https://c.biancheng.net/view/2217.html C++类成员的访问权限以及类的封装
- https://blog.csdn.net/weixin_39270987/article/details/108669956 C++类详解(public、private、protected)
- https://blog.csdn.net/qq_45544223/article/details/107043760 【C++的面向对象】——- 类成员的访问权限
Rapoo V820 VS2019 控制 LED 的代码
Rapoo(雷柏) V820是一款游戏机械键盘,采用雷柏自主机械轴,双色注塑键帽,5个独立游戏G键,全尺寸一体式掌托,USB口109键无冲突,109键可编程,12种背光模式,加厚金属上盖。
它自带了一个颜色控制的程序,经过研究总结出了它的控制方法。使用 USBLyzer 抓取它设置的动作发现应用程序会对设备发送几个 Package。

经过多次实验,确定了2个 Pacakge 是必须的,发送之后键盘会改变LED颜色。第一个是下面代码中的cmdBuffer定义的,第二个是LedBuffer中定义的,所有颜色信息都是通过这个提供给键盘的。LedBuffer[8] 开始是按键的Blue;LedBuffer[140]开始是按键的 Red取值;LedBuffer[272] 开始为 Green 取值。
按键偏移总结如下:
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#include "hidapi.h"
int Asc2KeyLEDOffset(char asc) {
int KeyOffset;
switch (asc) {
case '~':
case '`':
KeyOffset = 30;
break;
case '!':
case '1':
KeyOffset = 31;
break;
case '@':
case '2':
KeyOffset = 32;
break;
case '#':
case '3':
KeyOffset = 33;
break;
case '$':
case '4':
KeyOffset = 34;
break;
case '%':
case '5':
KeyOffset = 35;
break;
case '^':
case '6':
KeyOffset = 36;
break;
case '&':
case '7':
KeyOffset = 37;
break;
case '*':
case '8':
KeyOffset = 38;
break;
case '(':
case '9':
KeyOffset = 39;
break;
case ')':
case '0':
KeyOffset = 40;
break;
case '_':
case '-':
KeyOffset = 41;
break;
case '+':
case '=':
KeyOffset = 42;
break;
case 'Q':
case 'q':
KeyOffset = 53;
break;
case 'W':
case 'w':
KeyOffset = 54;
break;
case 'E':
case 'e':
KeyOffset = 55;
break;
case 'R':
case 'r':
KeyOffset = 56;
break;
case 'T':
case 't':
KeyOffset = 57;
break;
case 'Y':
case 'y':
KeyOffset = 58;
break;
case 'U':
case 'u':
KeyOffset = 59;
break;
case 'I':
case 'i':
KeyOffset = 60;
break;
case 'O':
case 'o':
KeyOffset = 61;
break;
case 'P':
case 'p':
KeyOffset = 62;
break;
case '{':
case '[':
KeyOffset = 63;
break;
case '}':
case ']':
KeyOffset = 64;
break;
case '|':
case '\\':
KeyOffset = 65;
break;
case 'A':
case 'a':
KeyOffset = 75;
break;
case 'S':
case 's':
KeyOffset = 76;
break;
case 'D':
case 'd':
KeyOffset = 77;
break;
case 'F':
case 'f':
KeyOffset = 78;
break;
case 'G':
case 'g':
KeyOffset = 79;
break;
case 'H':
case 'h':
KeyOffset = 80;
break;
case 'J':
case 'j':
KeyOffset = 81;
break;
case 'K':
case 'k':
KeyOffset = 82;
break;
case 'L':
case 'l':
KeyOffset = 83;
break;
case ':':
case ';':
KeyOffset = 84;
break;
case '"':
case '\'':
KeyOffset = 85;
break;
case 'Z':
case 'z':
KeyOffset = 98;
break;
case 'X':
case 'x':
KeyOffset = 99;
break;
case 'C':
case 'c':
KeyOffset = 100;
break;
case 'V':
case 'v':
KeyOffset = 101;
break;
case 'B':
case 'b':
KeyOffset = 102;
break;
case 'N':
case 'n':
KeyOffset = 103;
break;
case 'M':
case 'm':
KeyOffset = 104;
break;
case '<':
case ',':
KeyOffset = 105;
break;
case '>':
case '.':
KeyOffset = 106;
break;
case '?':
case '/':
KeyOffset = 107;
break;
case ' ':
KeyOffset = 123;
break;
default:
KeyOffset = 0;
break;
}
return KeyOffset;
}
//#pragma comment (lib,"setupapi.lib")//hidapi所需的lib环境,没有此文件会导致链接错误
// Headers needed for sleeping.
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
// Fallback/example
#ifndef HID_API_MAKE_VERSION
#define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p))
#endif
#ifndef HID_API_VERSION
#define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
#endif
//
// Sample using platform-specific headers
#if defined(__APPLE__) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
#include <hidapi_darwin.h>
#endif
#if defined(_WIN32) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
#include "hidapi_winapi.h"
#endif
#if defined(USING_HIDAPI_LIBUSB) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
#include <hidapi_libusb.h>
#endif
//
int res;
unsigned char cmdBuffer[5] = { 0x05, 0x83, 0x00, 0x00, 0x00 };
unsigned char LedBuffer[1032] = {
0x06, 0x09, 0xac, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const char* hid_bus_name(hid_bus_type bus_type) {
static const char* const HidBusTypeName[] = {
"Unknown",
"USB",
"Bluetooth",
"I2C",
"SPI",
};
if ((int)bus_type < 0)
bus_type = HID_API_BUS_UNKNOWN;
if ((int)bus_type >= (int)(sizeof(HidBusTypeName) / sizeof(HidBusTypeName[0])))
bus_type = HID_API_BUS_UNKNOWN;
return HidBusTypeName[bus_type];
}
void print_device(struct hid_device_info* cur_dev) {
printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
printf("\n");
printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
printf(" Product: %ls\n", cur_dev->product_string);
printf(" Release: %hx\n", cur_dev->release_number);
printf(" Interface: %d\n", cur_dev->interface_number);
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
printf(" Bus type: %d (%s)\n", cur_dev->bus_type, hid_bus_name(cur_dev->bus_type));
printf("\n");
}
void print_hid_report_descriptor_from_device(hid_device* device) {
unsigned char descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
int res = 0;
printf(" Report Descriptor: ");
res = hid_get_report_descriptor(device, descriptor, sizeof(descriptor));
if (res < 0) {
printf("error getting: %ls", hid_error(device));
}
else {
printf("(%d bytes)", res);
}
for (int i = 0; i < res; i++) {
if (i % 10 == 0) {
printf("\n");
}
printf("0x%02x, ", descriptor[i]);
}
printf("\n");
}
void print_hid_report_descriptor_from_path(const char* path) {
hid_device* device = hid_open_path(path);
if (device) {
print_hid_report_descriptor_from_device(device);
hid_close(device);
}
else {
printf(" Report Descriptor: Unable to open device by path\n");
}
}
void print_devices(struct hid_device_info* cur_dev) {
for (; cur_dev; cur_dev = cur_dev->next) {
print_device(cur_dev);
}
}
hid_device* Handle05 = NULL;
hid_device* Handle06 = NULL;
void print_devices_with_descriptor(struct hid_device_info* cur_dev) {
for (; cur_dev; cur_dev = cur_dev->next) {
if (cur_dev->vendor_id == 0x24ae) {
if ((strstr(cur_dev->path, "Col05") != NULL) || (strstr(cur_dev->path, "Col06") != NULL)) {
print_device(cur_dev);
print_hid_report_descriptor_from_path(cur_dev->path);
if (strstr(cur_dev->path, "Col05") != NULL) {
Handle05 = hid_open_path(cur_dev->path);
if (!Handle05) {
printf("unable to open device\n");
system("pause");
hid_exit();
return;
}
}
if (strstr(cur_dev->path, "Col06") != NULL) {
Handle06 = hid_open_path(cur_dev->path);
if (!Handle06) {
printf("unable to open device\n");
system("pause");
hid_exit();
return;
}
}
}
}
}
}
void LightAKey(hid_device* Handle1, hid_device* Handle2, char c, UINT8 value)
{
res = hid_send_feature_report(Handle1, cmdBuffer, 5);
if (res < 0) {
printf("Unable to write() Handle05: %ls\n", hid_error(Handle1));
}
LedBuffer[Asc2KeyLEDOffset(c)] = value;
res = hid_send_feature_report(Handle2, LedBuffer, sizeof(LedBuffer));
if (res < 0) {
printf("Unable to write(): %ls Handle06\n", hid_error(Handle2));
}
}
int main(int argc, char* argv[])
{
(void)argc;
(void)argv;
struct hid_device_info* devs;
printf("hidapi test/example tool. Compiled with hidapi version %s, runtime version %s.\n", HID_API_VERSION_STR, hid_version_str());
if (HID_API_VERSION == HID_API_MAKE_VERSION(hid_version()->major, hid_version()->minor, hid_version()->patch)) {
printf("Compile-time version matches runtime version of hidapi.\n\n");
}
else {
printf("Compile-time version is different than runtime version of hidapi.\n]n");
}
if (hid_init())
return -1;
#if defined(__APPLE__) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
// To work properly needs to be called before hid_open/hid_open_path after hid_init.
// Best/recommended option - call it right after hid_init.
hid_darwin_set_open_exclusive(0);
#endif
devs = hid_enumerate(0x0, 0x0);
print_devices_with_descriptor(devs);
hid_free_enumeration(devs);
/*
res = hid_send_feature_report(Handle05, cmdBuffer, 5);
if (res < 0) {
printf("Unable to write() Handle05: %ls\n", hid_error(Handle05));
}
LedBuffer[Asc2KeyLEDOffset('a')] = 200;
res = hid_send_feature_report(Handle06, LedBuffer, sizeof(LedBuffer));
if (res < 0) {
printf("Unable to write(): %ls Handle06\n", hid_error(Handle06));
}
system("pause");
*/
LightAKey(Handle05, Handle06, 'l', 200);
Sleep(800);
LightAKey(Handle05, Handle06, 'a', 200);
Sleep(800);
LightAKey(Handle05, Handle06, 'b', 200);
Sleep(800);
LightAKey(Handle05, Handle06, '-', 200);
Sleep(800);
LightAKey(Handle05, Handle06, 'z', 200);
Sleep(800);
LightAKey(Handle05, Handle06, '.', 200);
Sleep(800);
LightAKey(Handle05, Handle06, 'c', 200);
Sleep(800);
LightAKey(Handle05, Handle06, 'o', 200);
Sleep(800);
LightAKey(Handle05, Handle06, 'm', 200);
Sleep(800);
LightAKey(Handle05, Handle06, 'm', 0);
Sleep(800);
LightAKey(Handle05, Handle06, 'o', 0);
Sleep(800);
LightAKey(Handle05, Handle06, 'c', 0);
Sleep(800);
LightAKey(Handle05, Handle06, '.', 0);
Sleep(800);
LightAKey(Handle05, Handle06, 'z', 0);
Sleep(800);
LightAKey(Handle05, Handle06, '-', 0);
Sleep(800);
LightAKey(Handle05, Handle06, 'b', 0);
Sleep(800);
LightAKey(Handle05, Handle06, 'a', 0);
Sleep(800);
LightAKey(Handle05, Handle06, 'l', 0);
hid_close(Handle05);
hid_close(Handle06);
/* Free static HIDAPI objects. */
hid_exit();
#ifdef _WIN32
system("pause");
#endif
return 0;
}
DCode/DMU
DMU/DCode
前面提到了 PCode/Punit,这次介绍另外的 DCode/ DMU(Die Management Unit)。 这个 IP 是负责 CPU Die 的功耗的(包括大核和小核)。它会控制休眠和工作时的功耗,温度管理,以及 IccMax。同时会参与 Reset 动作。
和前面的类似,PUnit 上面跑的 Firmware 叫做 PCode。
PCode/PUnit
现代处理器变得越来越复杂,唯一不变的是:性能越强需要的功耗越大。为此,Intel 处理器专门引入了一个控制CPU电力消耗的部件:P-Unit。
P-Unit 是 “P’ower Management ‘Unit’ for the SOC-N(North)”的缩写。主要功能是负责 SoC-N 上面的 IP 供电/温度。这里的 SoC-N 可以理解为之前的 North Bridge , 包括 Memory Controller ,但是不包括 Graphic(目前 Intel 平台这部分独立成一个 Die)。
P-Uint 不会负责 SoC-S(South,相当于 之前的 South Bridge)上面的设备,这个是 PMC 的工作。此外 PUinit 还负责各种重启,MCA和Crashlog流程,能够帮助解决 HW 的Bug。
Intel 处理器上的P-Unit 核心是一个 Xtensa 处理器,负责运行 PCode (Power Managerment Firmware)。 PCode 是通过 mFIT 集成在 IFWI 中的固件。
PCode 不是 PMC Firmware, 对应的它在 mFIT “PUNIT Sub-Partition”中。
USB 键盘整蛊专家
这是一个能够让你整蛊别人的设备,将它串联到对方的USB 键盘和主机之间后,你可以用过手机上的 Blinker蓝牙连接到这个设备,然后在 Blinker中输出的信息就会出现在对方的电脑上。
硬件设计如下:
- 左上角是预留的调试烧录接口,通过这个接口可以进行烧录,同时 Debug信息也可以通过这个接口发送到 PC端;
- 左下角是这个的设计核心,它是一个 ESP32-S3 芯片,通过它实现USB Host 和蓝牙通讯的功能;
- ESP32-S3工作电压是 3.3V,这里使用 TLV1117 来实现,这个芯片外围只需要2个 1uf电容
- 右下角是 Ch9329 芯片,它是一个 HID 转串口芯片,在这个设计中用于实现USB键盘的功能。
CH9326是一款HID转串口免驱芯片。CH9326支持双向数据传输,用于接收串口数据,并按照HID类设备规范,将数据打包通过USB口上传给计算机,或者从计算机接收符合HID类设备的USB数据包,并从串口进行发送。通过提供的上位机软件,用户也可自行配置芯片的VID、PID,以及各种字符串描述符。芯片是 SOP16 封装,容易焊接。
设计的基本思路是:ESP32-S3 负责解析USB键盘数据,用这种方法来获得按键信息。之后,将获得的信息通过串口发送给CH9326, 然后 Ch9326会实现PC端的模拟按键。可以看到,这个设备对于PC端来说是透明的。之后,可以使用 Blinker 的蓝牙功能连接手机和这个设备,之后就可以从手机端发送字符给PC。
PCB 设计如下:

成品如下(彩色丝印,镀金工艺,背面是设计的一个二维码):

编写 Arduino 代码如下:
#include <elapsedMillis.h>
#include <usb/usb_host.h>
#include "show_desc.hpp"
#include "usbhhelp.hpp"
#define BLINKER_PRINT Serial
#define BLINKER_BLE
#include <Blinker.h>
//键盘数据
char keypress[] = {0x57, 0xAB, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
bool isKeyboard = false;
bool isKeyboardReady = false;
uint8_t KeyboardInterval;
bool isKeyboardPolling = false;
elapsedMillis KeyboardTimer;
const size_t KEYBOARD_IN_BUFFER_SIZE = 8;
usb_transfer_t *KeyboardIn = NULL;
// 将 Buffer 指向的内容,size 长度,计算 checksum 之后发送到Serial2
void SendData(byte *Buffer, byte size) {
byte sum = 0;
for (int i = 0; i < size - 1; i++) {
Serial2.write(*Buffer);
sum = sum + *Buffer;
Buffer++;
}
*Buffer = sum;
Serial2.write(sum);
}
// 将ASCII 字符转化为 HID Scancode值
byte Asc2Scancode(byte Asc, boolean *shift) {
if ((Asc >= 'a') && (Asc <= 'z')) {
*shift = false;
return (Asc - 'a' + 0x04);
}
if ((Asc >= 'A') && (Asc <= 'Z')) {
*shift = true;
return (Asc - 'A' + 0x04);
}
if ((Asc >= '1') && (Asc <= '0')) {
*shift = false;
return (Asc - '0' + 0x1E);
}
if (Asc == '>') {
*shift = true;
return (0x37);
}
if (Asc == '.') {
*shift = false;
return (0x37);
}
if (Asc == '_') {
*shift = true;
return (0x2D);
}
if (Asc == '-') {
*shift = false;
return (0x2D);
}
return 0;
}
// 如果未绑定的组件被触发,则会执行其中内容
// 输入框输入都会在这里处理
void dataRead(const String & data)
{
BLINKER_LOG("Blinker readString: ", data);
boolean shift;
byte scanCode;
for (int i = 0; i < data.length(); i++) {
BLINKER_LOG("Key In", data.charAt(1));
// 将收到的 ASCII 转为 ScanCode
scanCode = Asc2Scancode(data.charAt(i), &shift);
// 一些按键当有 Shift 按下时会发生转义
if (scanCode != 0) {
if (shift == true) {
keypress[5] = 0x02;
}
BLINKER_LOG("Scancode", scanCode);
// 填写要发送的 ScanCode
keypress[7] = scanCode;
SendData((byte*)keypress, sizeof(keypress));
delay(10);
keypress[5] = 0x00; keypress[7] = 0;
SendData((byte*)keypress, sizeof(keypress));
delay(10);
}
}
}
void keyboard_transfer_cb(usb_transfer_t *transfer)
{
if (Device_Handle == transfer->device_handle) {
isKeyboardPolling = false;
if (transfer->status == 0) {
if (transfer->actual_num_bytes == 8) {
uint8_t *const p = transfer->data_buffer;
ESP_LOGI("", "HID report: %02x %02x %02x %02x %02x %02x %02x %02x",
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
// USB Host 解析得到的数据,传输给PC
//
memcpy(&keypress[5],p,transfer->actual_num_bytes);
SendData((byte*)keypress, sizeof(keypress));
}
else {
ESP_LOGI("", "Keyboard boot hid transfer too short or long");
}
}
else {
ESP_LOGI("", "transfer->status %d", transfer->status);
}
}
}
void check_interface_desc_boot_keyboard(const void *p)
{
const usb_intf_desc_t *intf = (const usb_intf_desc_t *)p;
if ((intf->bInterfaceClass == USB_CLASS_HID) &&
(intf->bInterfaceSubClass == 1) &&
(intf->bInterfaceProtocol == 1)) {
isKeyboard = true;
ESP_LOGI("", "Claiming a boot keyboard!");
esp_err_t err = usb_host_interface_claim(Client_Handle, Device_Handle,
intf->bInterfaceNumber, intf->bAlternateSetting);
if (err != ESP_OK) ESP_LOGI("", "usb_host_interface_claim failed: %x", err);
}
}
void prepare_endpoint(const void *p)
{
const usb_ep_desc_t *endpoint = (const usb_ep_desc_t *)p;
esp_err_t err;
// must be interrupt for HID
if ((endpoint->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK) != USB_BM_ATTRIBUTES_XFER_INT) {
ESP_LOGI("", "Not interrupt endpoint: 0x%02x", endpoint->bmAttributes);
return;
}
if (endpoint->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) {
err = usb_host_transfer_alloc(KEYBOARD_IN_BUFFER_SIZE, 0, &KeyboardIn);
if (err != ESP_OK) {
KeyboardIn = NULL;
ESP_LOGI("", "usb_host_transfer_alloc In fail: %x", err);
return;
}
KeyboardIn->device_handle = Device_Handle;
KeyboardIn->bEndpointAddress = endpoint->bEndpointAddress;
KeyboardIn->callback = keyboard_transfer_cb;
KeyboardIn->context = NULL;
isKeyboardReady = true;
KeyboardInterval = endpoint->bInterval;
ESP_LOGI("", "USB boot keyboard ready");
}
else {
ESP_LOGI("", "Ignoring interrupt Out endpoint");
}
}
void show_config_desc_full(const usb_config_desc_t *config_desc)
{
// Full decode of config desc.
const uint8_t *p = &config_desc->val[0];
static uint8_t USB_Class = 0;
uint8_t bLength;
for (int i = 0; i < config_desc->wTotalLength; i += bLength, p += bLength) {
bLength = *p;
if ((i + bLength) <= config_desc->wTotalLength) {
const uint8_t bDescriptorType = *(p + 1);
switch (bDescriptorType) {
case USB_B_DESCRIPTOR_TYPE_DEVICE:
ESP_LOGI("", "USB Device Descriptor should not appear in config");
break;
case USB_B_DESCRIPTOR_TYPE_CONFIGURATION:
show_config_desc(p);
break;
case USB_B_DESCRIPTOR_TYPE_STRING:
ESP_LOGI("", "USB string desc TBD");
break;
case USB_B_DESCRIPTOR_TYPE_INTERFACE:
USB_Class = show_interface_desc(p);
check_interface_desc_boot_keyboard(p);
break;
case USB_B_DESCRIPTOR_TYPE_ENDPOINT:
show_endpoint_desc(p);
if (isKeyboard && KeyboardIn == NULL) prepare_endpoint(p);
break;
case USB_B_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
// Should not be config config?
ESP_LOGI("", "USB device qual desc TBD");
break;
case USB_B_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION:
// Should not be config config?
ESP_LOGI("", "USB Other Speed TBD");
break;
case USB_B_DESCRIPTOR_TYPE_INTERFACE_POWER:
// Should not be config config?
ESP_LOGI("", "USB Interface Power TBD");
break;
case 0x21:
if (USB_Class == USB_CLASS_HID) {
show_hid_desc(p);
}
break;
default:
ESP_LOGI("", "Unknown USB Descriptor Type: 0x%x", bDescriptorType);
break;
}
}
else {
ESP_LOGI("", "USB Descriptor invalid");
return;
}
}
}
void setup()
{
// 初始化调试串口
Serial.begin(115200);
// 初始 CH9329 串口
Serial2.begin(9600, SERIAL_8N1, 14, 13, false, 1000, 112);
//Serial2.begin(9600);
#if defined(BLINKER_PRINT)
BLINKER_DEBUG.stream(BLINKER_PRINT);
#endif
// 初始化blinker
Blinker.begin();
Blinker.attachData(dataRead);
usbh_setup(show_config_desc_full);
}
void loop()
{
usbh_task();
Blinker.run();
if (isKeyboardReady && !isKeyboardPolling && (KeyboardTimer > KeyboardInterval)) {
KeyboardIn->num_bytes = 8;
esp_err_t err = usb_host_transfer_submit(KeyboardIn);
if (err != ESP_OK) {
ESP_LOGI("", "usb_host_transfer_submit In fail: %x", err);
}
isKeyboardPolling = true;
KeyboardTimer = 0;
}
while (Serial.available()) {
char c = Serial.read();
if (c == 'q') {
boolean shift = false;
// 填写要发送的 ScanCode
keypress[5] = 0x08;
SendData((byte*)keypress, sizeof(keypress));
delay(20);
keypress[5] = 0;
SendData((byte*)keypress, sizeof(keypress));
}
Serial.print(c);
}
}
将板卡装入外壳后的照片:

完整的代码:
电路图和PCB 下载:
Step to UEFI (290)Cpp UEFI 006 抄一个 Print
前面编写测试代码的过程中,总感觉没有 Print 直接输出来的顺手,于是研究了一下 Print 的实现。基本原理是,对变量格式化后输出到一个 字符串Buffer 中,然后直接输出Buffer。
首先,编写一个测试的 CPP:
#include <UEFI/UEFI.h>
#include <type_traits>
#include "print.h"
EFI_SYSTEM_TABLE* gST;
EFI_STATUS
efi_main(EFI_HANDLE /*image*/, EFI_SYSTEM_TABLE* systemTable)
{
gST=systemTable;
Print(u"%d\n",2024);
return EFI_SUCCESS;
}
其中使用了 Print.h 头文件,定义如下:
UINTN
EFIAPI
Print (
IN const CHAR16 *Format,
...
);
接下来编写Print.cpp,关键代码来自\MdePkg\Library\UefiLib\UefiLibPrint.c
UINTN
EFIAPI
Print (
IN CONST CHAR16 *Format,
...
)
{
VA_LIST Marker;
UINTN Return;
VA_START (Marker, Format);
Return = InternalPrint (Format, gST->ConOut, Marker);
VA_END (Marker);
return Return;
}
其中的InternalPrint() 函数有较大改动,直接在函数中开了一个内存用于当作 Buffer (CharBuffer[]),不需要AllocatePool()动态分配。
UINTN
InternalPrint (
IN CONST CHAR16 *Format,
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Console,
IN VA_LIST Marker
)
{
EFI_STATUS Status;
UINTN Return;
CHAR16 *Buffer;
UINTN BufferSize;
CHAR16 CharBuffer[320];
ASSERT (Format != NULL);
ASSERT (((UINTN)Format & BIT0) == 0);
ASSERT (Console != NULL);
BufferSize = 320;
Buffer = &CharBuffer[0];
ASSERT (Buffer != NULL);
Return = UnicodeVSPrint (Buffer, BufferSize, Format, Marker);
if ((Console != NULL) && (Return > 0)) {
//
// To be extra safe make sure Console has been initialized
//
Status = Console->OutputString (Console, Buffer);
}
return Return;
}
接下来编写编译的批处理,可以看到最主要是编译生成 test8.obj 和 print.obj ,最后将二者Link 在一起即可:
set Target=test8
cl /c /I"C:\\BuildBs\\CppStudy\\Cpp\\UEFI-CPP-headers" /Zc:wchar_t- /Zi /W4 /WX- /diagnostics:column /Od /D _UNICODE /D UNICODE /D HAVE_USE_MS_ABI /D GNU_EFI_USE_EXTERNAL_STDARG /D _UNICODE /D UNICODE /Gm- /MDd /GS- /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++17 /Fo"C:\\BuildBs\\CppStudy\\Cpp\\" /FAsc /Fd"C:\\BuildBs\\CppStudy\\Cpp\\vc142.pdb" /external:W4 /Gd /TP /wd4229 /FC /errorReport:prompt /Oi- %Target%.cpp
cl /c /I"C:\\BuildBs\\CppStudy\\Cpp\\UEFI-CPP-headers" /Zc:wchar_t- /Zi /W4 /WX- /diagnostics:column /Od /D _UNICODE /D UNICODE /D HAVE_USE_MS_ABI /D GNU_EFI_USE_EXTERNAL_STDARG /D _UNICODE /D UNICODE /Gm- /MDd /GS- /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++17 /Fo"C:\\BuildBs\\CppStudy\\Cpp\\" /FAsc /Fd"C:\\BuildBs\\CppStudy\\Cpp\\vc142.pdb" /external:W4 /Gd /TP /wd4229 /FC /errorReport:prompt /Oi- print.cpp
if %errorlevel% NEQ 0 goto EndError
link "/OUT:C:\\BuildBs\\CppStudy\\Cpp\\%Target%.efi" /VERBOSE /INCREMENTAL:NO "/LIBPATH:C:\\BuildBs\\CppStudy\\Cpp\\" libcmtd.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NODEFAULTLIB /MANIFEST:NO /DEBUG:FULL "/PDB:C:\\BuildBs\\CppStudy\\Cpp\\bootx64.pdb" /SUBSYSTEM:EFI_APPLICATION /OPT:REF /TLBID:1 "/ENTRY:efi_main" /NXCOMPAT:NO "/IMPLIB:C:\\BuildBs\\CppStudy\\Cpp\\bootx64.lib" /MACHINE:X64 "C:\\BuildBs\\CppStudy\\Cpp\\%Target%.obj" "C:\\BuildBs\\CppStudy\\Cpp\\print.obj"
copy /y %Target%.efi Emulator\
:EndError
最后将Print.cpp、Test8.CPP和g8.bat 放在一起,即可编译。


模拟器运行结果如下:

完整代码如下,需要注意的是编译批处理内部使用了绝对路径,如果想实验,最好按照之前的文章架设同样名称的目录测试。
基于Ch554 制作的USB喇叭
很早之前使用 Arduino Pro Micro 实现过USB耳机转接器,这次尝试使用 WCH 的 Ch554 来实现(实际上可以使用 更见偏移的 Ch552 来实现,但是因为 Ch552 有烧写次数限制,所以最终是在 Ch554上进行开发)。
无需过多了解 USB Audio的相关知识,所作的工作基本上只有:通过描述符报告自己是一个USB Audio 设备。之后 Windows 就会发送 48Khz 16位双声道的采样数据给设备(如果想了解更多,推荐去USB中文网阅读相关内容)。我们在设备响应的 OUTPUT 端点上即可收到数据。
需要特别注意的是,代码中有一个向HOST 汇报当前支持采样率的描述符。这里申明了2个采样率:22,050Hz和48000Hz。
0x0E, //Size of the descriptor, in bytes
0x24, //CS_INTERFACE Descriptor Type
0x02, //FORMAT_TYPE descriptor subtype
0x01, //FORMAT_TYPE_I
0x02, //Indicates the number of physical channels in the audio data stream.
0x02, //The number of bytes occupied by one audio subframe. Can be 1, 2, 3 or 4.
0x10, //The number of effectively used bits from the available bits in an audio subframe.
0x02, //Indicates how the sampling frequency can be programmed:
0x22,0x56,0x00, // Sampling frequency 1 in Hz for this isochronous data endpoint.
0x80,0xBB,0x00, // Sampling frequency 2 in Hz for this isochronous data endpoint.
在工作过程中,Windows会通知当前使用的采样率。

需要注意的是:
- 如果你只报告支持 48000的采样率,那么Windows就不会发送 SET_CUR
- Windows不支持所有的采样率,我这边实验只支持48000Hz的采样率,换句话,你申明支持 24000或者22050,实际上并不会使用。
具体的数据是下面这样的,可以看到这种同步传输/等时传输的数据和通常的最大区别在于不会有 ACK 信号,相当于HOST 直接丢出来不管对错。

从上面可以看到每个数据之间间隔是1ms,每笔数据 192字节。
对应在代码中会在USBAudioSpeaker.c文件中的Mass_Storage_Out函数进行处理:
void Mass_Storage_Out (void) {
PWM_CTRL |= bPWM2_OUT_EN;
for (uint8_t i = 0; i < BOT_EP_Rx_Length; i=i+4){
PWM_DATA2 = BOT_Rx_Buf[i+1];
// Delay for 20833ns
for (uint16_t j=0;j<51;j++) {
__asm__ ("nop\n");
}
}
PWM_CTRL &= (~bPWM2_OUT_EN);
//Serial0_println("Ending");
BOT_EP_Tx_ISO_Valid();
}
经过前面的工作,现在能够拿到PC输出的音频数据,接下来的问题就是如何将收到的数据通过喇叭播放出去。这个过程相当于一个 DAC (数字到模拟)的过程。这次选择的方法是:通过 PWM 进行模拟。这是使用的是CH554 芯片,它支持PWM:2 组 PWM 输出,PWM1/PWM2 为 2 路 8 位 。在下图可以看到 P1.5/P3.1/P3.0/P3.4都是可以选择的引脚。代码使用了 P3.4这个引脚。

PWM初始化代码如下,特别注意使用了1分频产生 PWM 信号,我们使用的主频为 24Mhz 5V,因此频率是 24000000/256=93750Hz
// 打开 PWM2 功能
PIN_FUNC &= ~(bPWM2_PIN_X);
// PWM 分频设置
// 1 分频,这样 PWM 频率为 Fsys/256
PWM_CK_SE=1;
上述设置之后,直接在 PWM_DATA2 寄存器中填写你要生成的高电平比例即可产生对应的 PWM 信号。对应的代码就在前面提到的void Mass_Storage_Out (void) {} 函数中。此外,使用NOP 指令制作了一个简单的延时,延时 1/48000=20833ns:
// Delay for 20833ns
for (uint16_t j=0;j<51;j++) {
__asm__ ("nop\n");
}
在编译时,还需要对项目进行如下设置:
- 选择 Ch552 Board,前面提到了Ch552和 Ch554 在代码方面是完全兼容的;
- 设置USB 为“U148B USB Ram”
- Clock Source 为 “24Mhz(internal),5V”

硬件方面非常简单,Ch554最小系统,喇叭接到对应引脚即可:

这是我设计的用于测试 Ch554 和 Ch559 最小开发板。Ch554和Ch559 最小系统外围只需要2个电容即可,两颗芯片相互独立:

完整代码:
电路图:
从上面可以看到,Ch55xduino提供的 USB 框架扩展性不错。Ch554 可以方便的通过 Ch55xduino 实现一个USB Speaker 的功能。目前美中不足的只是音频质量较差(所有看到的人都怀疑这个是一个收音机),后续会持续进行改进。
CH559 Arduino 使用 PWM2 的例子
代码非常简单,根据官方例子移植到 Arduino 完成。使用 P2.5 引脚,Ch55xduino 编译:
#define SetPWMClk(CK_SE) (PWM_CK_SE = CK_SE) //分频,默认时钟Fsys
#define SetPWMCycle(Cycle) (PWM_CYCLE = Cycle) //设置循环周期
#define SetPWM1Dat(dat) (PWM_DATA = dat) //设置PWM输出占空比
#define SetPWM2Dat(dat) (PWM_DATA2 = dat)
/*******************************************************************************
* Function Name : InitPWM2(UINT8 polar)
* Description : PWM初始化函数
* Input : polar=0选择默认低电平,高电平输出有效;
polar=1选择默认高电平,低电平输出有效;
* Output : None
* Return : None
*******************************************************************************/
void InitPWM2(uint8_t polar)
{
PWM_CTRL &= ~bPWM_CLR_ALL; //清空FIFO和计数
PWM_CTRL &= ~bPWM_MOD_MFM;
PWM_CTRL |= bPWM_IE_END; //使能PWM计数周期完成中断
PWM_CTRL |= bPWM2_OUT_EN; //PWM2输出使能
PWM_CTRL |= bPWM_IF_END; //清除所有的中断标志
if(polar){
PWM_CTRL |= bPWM2_POLAR; //低电平有效
}
else{
PWM_CTRL &= ~bPWM2_POLAR; //高电平有效
}
}
void setup() {
// put your setup code here, to run once:
SetPWMClk(12); //设置PWM1&2的时钟分频系数为12
InitPWM2(0); //PWM2初始化,高电平有效
SetPWMCycle(1000); //设置循环周期100
SetPWM2Dat(50); //PWM1占空比设置50/100
}
void loop() {
// put your main code here, to run repeatedly:
}
Step to UEFI (289)Cpp UEFI 005 C++函数默认参数
C++ 定义函数时可以直接给形参指定默认值,如果调用函数没有给形参赋值,那就直接使用默认值。这个功能非常容易理解。编写如下代码进行验证:
#include <UEFI/UEFI.h>
#include <type_traits>
EFI_SYSTEM_TABLE* gSystemTable;
void printInt(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* conOut, int value) {
CHAR16 out[32];
CHAR16* ptr = out;
static_assert(std::is_unsigned_v<char16_t>);
if (value == 0)
{
conOut->OutputString(conOut, u"0");
return;
}
ptr += 31;
*--ptr = 0;
int tmp = value;// >= 0 ? value : -value;
while (tmp)
{
*--ptr = '0' + tmp % 10;
tmp /= 10;
}
if (value < 0) *--ptr = '-';
conOut->OutputString(conOut, ptr);
}
void func(int a, int b=2, int c=3){
printInt(gSystemTable->ConOut,a);
gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"\r\n");
printInt(gSystemTable->ConOut,b);
gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"\r\n");
printInt(gSystemTable->ConOut,c);
gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"\r\n");
}
EFI_STATUS
efi_main(EFI_HANDLE /*image*/, EFI_SYSTEM_TABLE* systemTable)
{
gSystemTable=systemTable;
func(30);
return EFI_SUCCESS;
}
上面定义了 void func(int a, int b=2, int c=3) 这个函数,当通过func(30)调用时,相当于只给 a 赋值 30,其余的直接使用了默认值。
需要注意的是,在使用时有一些限制。比如:C++规定,默认参数只能放在形参列表的最后,而且一旦为某个形参指定了默认值,那么它后面的所有形参都必须有默认值。
参考:
1. https://c.biancheng.net/view/2204.html C++函数的默认参数详解