常见电容参数识别

常见电容器参数识别方法

电容单位是F

1法拉(F)= 1000毫法(mF)=1000000微法(μF)

1微法(μF)= 1000纳法(nF)= 1000000皮法(pF)   【参考1】

常见的电容标注:

  1. 直接在电容器上标注的,比如510P 10%。这种很容易识别。
  2. 三位数表示方法,前两位表示有效数字,第三位表示倍乘,有时候后面跟着一个字母表示允许误差,整体单位是 pf。比如:102表示 10X10^2pf。常见的标注如下
1PF 20PF(200) 100PF(101) 510PF(511) 10NF(103) 220NF(224)
4.7PF 22P(220) 150PF(151) 680PF(681) 22NF(223) 330NF(334)
6.8PF 30PF(300) 180PF(181) 1NF(102) 33NF(333) 470NF(474)
10PF(100) 33PF(330) 220PF(221) 2.2NF(222) 47NF(473) 1UF(105)
15PF(150) 47PF(470) 330PF(331) 3.3NF(332) 68NF(683) 2.2UF(225)
18PF(180) 68PF(680) 470PF(471PF) 4.7NF(472) 100NF(104) 4.7UF(475)
3.3UF(335) 10UF(106)
  1. 四位数字表示方法,有下面2中情况
    1. 4位整数,此时单位是pf。比如:6800 表示 6800pf
    2. 用小数(有时不足4位),此时单位是 uf。比如:47表示 0.47uf。【参考2】

参考:

  1. https://baike.baidu.com/item/%E7%94%B5%E5%AE%B9/146658?fr=aladdin#2
  2. 电子工程师必备—-元器件应用宝典

Step to UEFI (160)UEFI 下的 GIF 解码

现在最常见的 GIF 就是各种动图了,因为它足够小,无需支付版权费用和不用安装额外的插件,使得它成为分享动图的首选。
这次介绍来自https://github.com/lecram/gifdec 的 GIF 解码库,可以用来解码静态和动态GIF格式。
需要注意的是,这个库有下面2个局限(GIF 最多支持256色,这个限制是来自他的调色板只有256色。通常情况下,一幅有很多帧的动图都会共享同一个调色板。特殊情况下,每一帧都有一个调色板。这个库不支持后面那种情况):
* no support for GIF files that don’t have a global color table
* no direct support for the plain text extension (rarely used)

实例程序如下,我在 IA32 和 X64 NT32环境下测试过,都是可以正常工作的:

#include <stdlib.h>
#include <stdio.h>
#include <Protocol/GraphicsOutput.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include "gifdec.h"

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
  { \
    0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } \
  }
  
static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;

int
main (
  IN int Argc,
  IN char *Argv[]
  )
{
    EFI_STATUS    Status;
   EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
    int i,j;

    UINT8* RGB32;
    UINT8* frame;

    if (Argc<2) {
            printf("Please input file name\n");
            return 0;
    }

    gd_GIF* gif = gd_open_gif(Argv[1]);
    if (gif == NULL) {
        printf("Open file Error\n");
        return -1;
    }

    printf("width %d height %d\n",gif->width,gif->height);
    printf("number of colors: %d\n", gif->palette->size);
    printf("number of frames: %d\n", gif->loop_count);

    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
                GraphicsOutput = NULL;
                printf("Loading Graphics_Output_Protocol error!\n");
                return EFI_SUCCESS;}

                
    frame=(UINT8*)AllocatePool(gif->width*gif->height*3);
    RGB32 = (UINT8*)AllocatePool(gif->width*gif->height*4); 

       while (1)
       {
        int retx = gd_get_frame(gif);
        if (retx == -1) 
            break;
        gd_render_frame(gif, frame);
                    for (j=0;j<gif->height;j++) {
                            for (i=0;i<gif->width;i++) {
                                RGB32[(j*gif->width+i)*4]  = frame[(j*gif->width+i)*3+2]; //Blue   
                                RGB32[(j*gif->width+i)*4+1]= frame[(j*gif->width+i)*3+1]; //Green 
                                RGB32[(j*gif->width+i)*4+2]= frame[(j*gif->width+i)*3]; //Red  
                                RGB32[(j*gif->width+i)*4+3]=0;
                            }
                    GraphicsOutput->Blt(
                                GraphicsOutput, 
                                (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) RGB32,
                                EfiBltBufferToVideo,
                                0, 0, 
                                0, 0, 
                                gif->width, gif->height, 0);    
                              }                          

        if (retx == 0) 
                break;
            //gd_rewind(gif);
   }

   free(RGB32);  
    free(frame);

    gd_close_gif(gif);    

    return 0;
}

 

工作截图:

完整代码下载:
giftest

简单说一下工作流程,首先gd_open_gif() 函数打开 GIF,这步可以获得尺寸信息,但是从我实验来看,无法取得帧数信息(loop_count),但是readme 中说是可以的;接下来使用gd_get_frame() 函数检查当前GIF播放的位置,返回1表示还有下一帧,0表示达到尾部,-1表示出现错误;然后,gd_render_frame()函数取出来解码后的图像信息,经过格式变换即可通过 BLT显示出来;最后,检查是否播放完毕,完毕之后退出。
代码写的比较简单,效率不高,在处理较大 GIF 的时候,还是能够感受到刷新的。有兴趣的朋友可以琢磨一下如何提速,我猜测瓶颈应该是在处理解码后的数据RGB顺序的那里。

=======================================================================================
2018年11月23日 感谢 Cai 3023772977@qq.com 在评论中之处问题,代码中 GraphicsOutput() 函数,写在循环内了,这是导致播放缓慢的原因,将它移动到循环外面就可以正常工作了。

for (j=0;jheight;j++) {
for (i=0;iwidth;i++) {
RGB32[(j*gif->width+i)*4] = frame[(j*gif->width+i)*3+2]; //Blue
RGB32[(j*gif->width+i)*4+1]= frame[(j*gif->width+i)*3+1]; //Green
RGB32[(j*gif->width+i)*4+2]= frame[(j*gif->width+i)*3]; //Red
RGB32[(j*gif->width+i)*4+3]=0;
}

GraphicsOutput->Blt(
GraphicsOutput,
(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) RGB32,
EfiBltBufferToVideo,
0, 0,
0, 0,
gif->width, gif->height, 0);

}

Teensy 3.2 GPIO 速度测试

做了一个简单的实验,测试 Teensy 3.2 板子IO 的速度,具体代码如下,就是使用digitalWrite进行 GPIO反转,然后示波器查看结果。

void setup() {
  // put your setup code here, to run once:
  pinMode(14,OUTPUT);
  digitalWrite(14,HIGH);
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(14,LOW);
  digitalWrite(14,HIGH);
  digitalWrite(14,LOW);
  digitalWrite(14,HIGH);
  digitalWrite(14,LOW);
  digitalWrite(14,HIGH);
  digitalWrite(14,LOW);
  digitalWrite(14,HIGH);
  digitalWrite(14,LOW);
  digitalWrite(14,HIGH);
}

 

速度首先和主频有关系,可以在下面的位置找到,我这边测试72Mhz和96Mhz的情况。

另外,还和编译选项有关系。

1.72Mhz+Faster 测试结果是 1.21Mhz (光标测量,下同)

2.72Mhz + Faster with LTO 测试结果是 2.34Mhz (光标测量,下同)

3.96Mhz+Faster 测试结果是 1.61Mhz


4. 96Mhz+Faster with LTO 测试结果是 3.07Mhz

Step to UEFI (159)UEFI 下的排序

大多数情况下,2层循环的冒泡排序对于一般用户已经足够,但是如果对于速度有要求,那就需要考虑效率更高的排序算法了。最近偶然看到 MdeModulePkg\Include\Library\SortLib.h 提供的排序功能,研究之后编写如下的例子。
这个库的核心是PerformQuickSort() 函数,调用者给出需要排序的 buffer,然后给出其中每一个元素的大小和数量,然后这个函数还会调用作为参数指定的CompareFunction函数。通过这个函数,不但可以比较数字或者字符串,还可以比较自动定义的规则,比方说设定书记>厂长>科长之类的……

示例代码如下:

/**
  Function to perform a Quick Sort on a buffer of comparable elements.

  Each element must be equally sized.

  If BufferToSort is NULL, then ASSERT.
  If CompareFunction is NULL, then ASSERT.

  If Count is < 2 , then perform no action.
  If Size is < 1 , then perform no action.

  @param[in, out] BufferToSort   On call, a Buffer of (possibly sorted) elements;
                                 on return, a buffer of sorted elements.
  @param[in]  Count              The number of elements in the buffer to sort.
  @param[in]  ElementSize        The size of an element in bytes.
  @param[in]  CompareFunction    The function to call to perform the comparison
                                 of any two elements.
**/
VOID
EFIAPI
PerformQuickSort (
  IN OUT VOID                   *BufferToSort,
  IN CONST UINTN                Count,
  IN CONST UINTN                ElementSize,
  IN       SORT_COMPARE         CompareFunction
  );
代码如下:
#include <stdlib.h>
#include <stdio.h>
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/SortLib.h>

#define TOTAL           100
#define NAMELENGTH        4

/** Expands to an integer constant expression that is the maximum value
    returned by the rand function.
**/
#define RAND_MAX  0x7fffffff
static UINT32 next = 1;

typedef struct {
            char Name[NAMELENGTH+1];
            int  Score;
} TScoreRecord;

/** Compute a pseudo-random number.
  *
  * Compute x = (7^5 * x) mod (2^31 - 1)
  * without overflowing 31 bits:
  *      (2^31 - 1) = 127773 * (7^5) + 2836
  * From "Random number generators: good ones are hard to find",
  * Park and Miller, Communications of the ACM, vol. 31, no. 10,
  * October 1988, p. 1195.
**/
int
rand()
{
  INT32 hi, lo, x;

  /* Can't be initialized with 0, so use another value. */
  if (next == 0)
    next = 123459876;
  hi = next / 127773;
  lo = next % 127773;
  x = 16807 * lo - 2836 * hi;
  if (x < 0)
    x += 0x7fffffff;
  return ((next = x) % ((UINT32)RAND_MAX + 1));
}

/**
  Test comparator.

  @param[in] b1   The first INTN
  @param[in] b2   The other INTN

  @retval 0       They are the same.
  @retval -1      b1 is less than b2
  @retval 1       b1 is greater then b2
**/
INTN
EFIAPI
Compare (CONST TScoreRecord *b1, CONST TScoreRecord *b2)
{
  if ((b1->Score)<(b2->Score)) {
          return -1;
  }
  if ((b1->Score)>(b2->Score)) {
          return 1;
  }
  return (0);
}

int main()
{       
        TScoreRecord   *SR;
        int             Index,NameIndex;
        
        SR = AllocatePool (sizeof(TScoreRecord)*TOTAL);
        
        for (Index=0;Index<TOTAL;Index++) {
            //Generate a name
            for (NameIndex=0;NameIndex<NAMELENGTH;NameIndex++) {
                        SR[Index].Name[NameIndex]=(rand()%26)+'A';
                }
            SR[Index].Name[NAMELENGTH]=0;
            //Generate a score
            SR[Index].Score=rand()%150;
            //Output
            printf("[%d] %s %d\n",Index,SR[Index].Name,SR[Index].Score);
        }

        //Sort
        PerformQuickSort (SR,TOTAL,sizeof(TScoreRecord),Compare);

        //Show the sort result
        for (Index=0;Index<TOTAL;Index++) {
            printf("[%d] %s %d\n",Index,SR[Index].Name,SR[Index].Score);
        }        
        
        FreePool(SR);
        
        return 0;
}

 

运行结果:

完整代码和EFI 下载:

SortTest

此外,还有一个有意思的问题:如果Compare (CONST TScoreRecord *b1, CONST TScoreRecord *b2) 这里定义为Compare (TScoreRecord *b1, TScoreRecord *b2),那么在编译时,会在PerformQuickSort (SR,TOTAL,sizeof(TScoreRecord),Compare); 报告C4090错误。因为编译器报告错误位置和实际位置着实不同,为了找到原因着实让我花费了不少时间。

C# 串口

1.列出当前系统中串口编号的方法:

            Array ports = System.IO.Ports.SerialPort.GetPortNames();
            for (int x = 0; x < ports.Length; x++)
               Console.Write(ports.GetValue(x).ToString());

 

2.列出当前系统中串口设备完整名称的方法:

        static void Main(string[] args)
        {
            ManagementObjectSearcher searcher =
               new ManagementObjectSearcher("root\\CIMV2",
               "SELECT * FROM Win32_PnPEntity");

            foreach (ManagementObject queryObj in searcher.Get())
            {
                if (queryObj["Caption"]!=null)
                if (queryObj["Caption"].ToString().Contains("(COM"))
                {
                    Console.WriteLine(queryObj["Caption"]);
                }

            }

 

需要注意的是,要在文件头部加上 using System.Management; 并且在菜单 Project -> Add Reference -> Assembies -> Framework 选中 System.Management

Step to UEFI (158)UEFI 下的 PNG 解码

前面介绍过 PCX的解码,这次研究一下 PNG 格式的解码,有了前面的经验,这次实现起来简单多了。

首先,找一个C语言的PNG 解码库,经过比较选择了来自【参考1】的 upng。然后根据说明编写一个简单的示例:

#define DR_PCX_IMPLEMENTATION
#include "upng.h"
#include <stdlib.h>
#include <stdio.h>
#include <Protocol/GraphicsOutput.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
  { \
    0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } \
  }
  
static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;

int
main (
  IN int Argc,
  IN char *Argv[]
  )
{
    int width;
    int height;
    EFI_STATUS    Status;
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
    int i,j;

    UINT8* RGB32;
    UINT8* pImageData;
    
    if (Argc<2) {
            printf("Please input file name\n");
            return 0;
    }
    
    upng_t* upng = upng_new_from_file(Argv[1]);
    
    if (upng == NULL) {
        printf("Open file Error\n");
        return -1;
    }
    upng_header(upng);

    width=upng_get_width(upng);
    height=upng_get_height(upng);
    printf("width %d height %d\n",width,height);
    upng_format f=upng_get_format(upng);
    printf("format %d\n",f);
    
    upng_decode(upng);

    pImageData=(UINT8 *)upng_get_buffer(upng);
    
    RGB32 = (UINT8*)AllocatePool(width*height*4); 
    for (j=0;j<height;j++) {
            for (i=0;i<width;i++) {
                RGB32[(j*width+i)*4]  = pImageData[(j*width+i)*4+2]; //Blue   
                RGB32[(j*width+i)*4+1]= pImageData[(j*width+i)*4+1]; //Green 
                RGB32[(j*width+i)*4+2]= pImageData[(j*width+i)*4]; //Red  
                RGB32[(j*width+i)*4+3]=0;
            }
    }

    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
                GraphicsOutput = NULL;
                printf("Loading Graphics_Output_Protocol error!\n");
                return EFI_SUCCESS;}

        
    GraphicsOutput->Blt(
                GraphicsOutput, 
                (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) RGB32,
                EfiBltBufferToVideo,
                0, 0, 
                0, 0, 
                width, height, 0);    
    
         free(RGB32);                
        upng_free(upng);
    return 0;
}

 

运行结果(可以在NT32模拟环境下运行):

完整代码和实例PNG测试图片:
pngtest

特别需要注意的是:解码之后的数据无法直接使用 BLT 在屏幕上显示,因此,特别重新开辟了一个内存空间进行排列。这样的操作也会导致兼容性上的问题,换其他格式的 PNG (比如:例子用的都是RGBA的,此外还有RGB格式的),会出现错误的情况。如果你发现decode之后结果很奇怪,不妨考虑一下是否是这个原因导致的。

参考:
1. https://github.com/elanthis/upng

Windows 下读取 UT395B 激光测距仪返回值

最近有一个需求,需要检测一个物体的运动速度,经过研究我决定使用激光距离传感器来完成这个要求。在模块选择的问题上,我再次遇到了“面粉比面包贵” 的问题—–激光测距模块价格比成品要高。最终选择的是带有USB接口的优利德 UT395B。

最远可以达到150米,我选择的是可以达到70米的

他自带一个软件可以在 Windows 下获得距离,因此可以使用 USBlyzer 来抓取通讯数据数据分析协议。首先抓取Descriptor:

USB 输入设备

Connection Status Device connected
Current Configuration 1
Speed Full (12 Mbit/s)
Device Address 12
Number Of Open Pipes 2

Device Descriptor SDWAY

Offset Field Size Value Description
0 bLength 1 12h
1 bDescriptorType 1 01h Device
2 bcdUSB 2 0200h USB Spec 2.0
4 bDeviceClass 1 00h Class info in Ifc Descriptors
5 bDeviceSubClass 1 00h
6 bDeviceProtocol 1 00h
7 bMaxPacketSize0 1 40h 64 bytes
8 idVendor 2 0483h SGS Thomson Microelectronics
10 idProduct 2 5751h
12 bcdDevice 2 0200h 2.00
14 iManufacturer 1 01h “MyUSB_HID”
15 iProduct 1 02h ” SDWAY “
16 iSerialNumber 1 03h
17 bNumConfigurations 1 01h

Configuration Descriptor 1

Offset Field Size Value Description
0 bLength 1 09h
1 bDescriptorType 1 02h Configuration
2 wTotalLength 2 0029h
4 bNumInterfaces 1 01h
5 bConfigurationValue 1 01h
6 iConfiguration 1 00h
7 bmAttributes 1 C0h Self Powered
4..0: Reserved …00000
5: Remote Wakeup ..0….. No
6: Self Powered .1…… Yes
7: Reserved (set to one)
(bus-powered for 1.0)
1…….
8 bMaxPower 1 C0h 384 mA

Interface Descriptor 0/0 HID, 2 Endpoints

Offset Field Size Value Description
0 bLength 1 09h
1 bDescriptorType 1 04h Interface
2 bInterfaceNumber 1 00h
3 bAlternateSetting 1 00h
4 bNumEndpoints 1 02h
5 bInterfaceClass 1 03h HID
6 bInterfaceSubClass 1 00h
7 bInterfaceProtocol 1 00h
8 iInterface 1 00h

HID Descriptor

Offset Field Size Value Description
0 bLength 1 09h
1 bDescriptorType 1 21h HID
2 bcdHID 2 0110h 1.10
4 bCountryCode 1 00h
5 bNumDescriptors 1 01h
6 bDescriptorType 1 22h Report
7 wDescriptorLength 2 0021h 33 bytes

Endpoint Descriptor 82 2 In, Interrupt, 10 ms

Offset Field Size Value Description
0 bLength 1 07h
1 bDescriptorType 1 05h Endpoint
2 bEndpointAddress 1 82h 2 In
3 bmAttributes 1 03h Interrupt
1..0: Transfer Type ……11 Interrupt
7..2: Reserved 000000..
4 wMaxPacketSize 2 0040h 64 bytes
6 bInterval 1 0Ah 10 ms

Endpoint Descriptor 01 1 Out, Interrupt, 16 ms

Offset Field Size Value Description
0 bLength 1 07h
1 bDescriptorType 1 05h Endpoint
2 bEndpointAddress 1 01h 1 Out
3 bmAttributes 1 03h Interrupt
1..0: Transfer Type ……11 Interrupt
7..2: Reserved 000000..
4 wMaxPacketSize 2 0040h 64 bytes
6 bInterval 1 10h 16 ms

Interface 0 HID Report Descriptor

Item Tag (Value) Raw Data
Usage Page (Bar Code Scanner) 05 8C
Usage 09 01
Collection (Application) A1 01
    Usage 09 03
    Logical Minimum (0) 15 00
    Logical Maximum (-256) 26 00 FF
    Report Size (8) 75 08
    Report Count (24) 95 18
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
    Usage 09 04
    Logical Minimum (0) 15 00
    Logical Maximum (-256) 26 00 FF
    Report Size (8) 75 08
    Report Count (24) 95 18
    Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
End Collection C0

 

从上面的结果可以看出这个设备是一个自定义的 HID设备,因此,需要进步一抓取通讯数据。

上面是一次完整的获得测量距离的过程,一共有8笔数据,4 笔是主机发送给设备,4笔是设备的返回值。 OUTPUT Report是主机发送给 UT395B的数据。第一个数据包: 41 54 4B 30 30 31 23 00 00 00 00 41 00 00 00 54 00 00 00  44 00 00 00 00

发送到设备之后,设备会打开激光(手册上说这是打开瞄准的功能)。第5笔数据和第1笔数据相同,设备会关闭激光。

经过研发总结: 41 54 4B 30 30 31 23 …….这个命令是打开/关闭激光。   41 54 44 30 30 31 23 …….是主机要求返回距离信息的命令。

上面8个数据的含义如下:

  1. 主机要求打开激光
  2. 设备 ECHO
  3. 主机要求获得上一笔数据
  4. 设备返回上一笔数据
  5. 主机要求关闭激光
  6. 设备 ECHO
  7. 主机要求获得距离数据
  8. 设备返回数据,就是我们需要的距离信息

一个典型的返回值:

41 54 44 00 00 3A E8 00 …… 其中的  3A E8 就是距离信息。0x3AE8=15080 对应显示在屏幕上的距离是1.508米。经过多次实验,显示小数点后3位的数值,但是实际上的数据是小数点后4位,最后一位会进行四舍五入。

最终编写 Windows 下 Console 代码如下:

//http://blog.csdn.net/xuxinhua/article/details/6329182

#include "stdafx.h"

#include <stdio.h>

#include <tchar.h>

#include <windows.h>

#include <setupapi.h>



extern "C" {

void __stdcall

HidD_GetHidGuid (

   OUT   LPGUID   HidGuid

   );

typedef struct _HIDD_ATTRIBUTES {

    ULONG   Size; // = sizeof (struct _HIDD_ATTRIBUTES)



    //

    // Vendor ids of this hid device

    //

    USHORT  VendorID;

    USHORT  ProductID;

    USHORT  VersionNumber;



    //

    // Additional fields will be added to the end of this structure.

    //

} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;



BOOLEAN __stdcall

HidD_GetAttributes (

    IN  HANDLE              HidDeviceObject,

    OUT PHIDD_ATTRIBUTES    Attributes

    );



BOOLEAN __stdcall

HidD_SetFeature(

       _In_    HANDLE   HidDeviceObject,

       _In_reads_bytes_(ReportBufferLength) PVOID ReportBuffer,

       _In_    ULONG    ReportBufferLength

);

}



#pragma comment( lib, "hid.lib" )

#pragma comment( lib, "setupapi.lib" )



void Switch(HANDLE hUsb)

{

       BOOL Result;

       //Output DataµÄ»º³åÇø£¬ÒòΪÏÂÃæ·¢ËÍ HID ±¨ÎÄÐèÒª9×Ö½Ú£¬ËùÒÔÕâÀïÖ±½Ó°´ÕÕ×î´óµÄ¶¨Òå

       UCHAR WriteReportBuffer[25];



       WriteReportBuffer[00] = 0x00;



       WriteReportBuffer[1] = 0x41;

       WriteReportBuffer[2] = 0x54;

       WriteReportBuffer[3] = 0x4b;

       WriteReportBuffer[4] = 0x30;



       WriteReportBuffer[5] = 0x30;

       WriteReportBuffer[6] = 0x31;

       WriteReportBuffer[7] = 0x23;

       WriteReportBuffer[8] = 0x00;



       WriteReportBuffer[9] = 0x00;

       WriteReportBuffer[10] = 0x00;

       WriteReportBuffer[11] = 0x00;

       WriteReportBuffer[12] = 0x41;



       WriteReportBuffer[13] = 0x00;

       WriteReportBuffer[14] = 0x00;

       WriteReportBuffer[15] = 0x00;

       WriteReportBuffer[16] = 0x54;



       WriteReportBuffer[17] = 0x00;

       WriteReportBuffer[18] = 0x00;

       WriteReportBuffer[19] = 0x00;

       WriteReportBuffer[20] = 0x44;



       WriteReportBuffer[21] = 0x00;

       WriteReportBuffer[22] = 0x00;

       WriteReportBuffer[23] = 0x00;

       WriteReportBuffer[24] = 0x00;

       DWORD lpNumberOfBytesWritten;



       //µ÷ÓÃWriteFileº¯Êý·¢ËÍÊý¾Ý

       //±¨Îij¤¶ÈÊÇ9×Ö½Ú

       Result = WriteFile(hUsb,

             WriteReportBuffer,

             25,

             &lpNumberOfBytesWritten,

             NULL);



       printf("Written %d bytes Result [%d]\n", lpNumberOfBytesWritten, Result);



}



void SendData(HANDLE hUsb)

{

       BOOL Result;

       //Output DataµÄ»º³åÇø£¬ÒòΪÏÂÃæ·¢ËÍ HID ±¨ÎÄÐèÒª9×Ö½Ú£¬ËùÒÔÕâÀïÖ±½Ó°´ÕÕ×î´óµÄ¶¨Òå

       UCHAR WriteReportBuffer[25];



       WriteReportBuffer[00] = 0x00;



       WriteReportBuffer[1] = 0x41;

       WriteReportBuffer[2] = 0x54;

       WriteReportBuffer[3] = 0x44;

       WriteReportBuffer[4] = 0x30;



       WriteReportBuffer[5] = 0x30;

       WriteReportBuffer[6] = 0x31;

       WriteReportBuffer[7] = 0x23;

       WriteReportBuffer[8] = 0x00;



       WriteReportBuffer[9] = 0x00;

       WriteReportBuffer[10] = 0x00;

       WriteReportBuffer[11] = 0x00;

       WriteReportBuffer[12] = 0x41;



       WriteReportBuffer[13] = 0x00;

       WriteReportBuffer[14] = 0x00;

       WriteReportBuffer[15] = 0x00;

       WriteReportBuffer[16] = 0x54;



       WriteReportBuffer[17] = 0x00;

       WriteReportBuffer[18] = 0x00;

       WriteReportBuffer[19] = 0x00;

       WriteReportBuffer[20] = 0x44;



       WriteReportBuffer[21] = 0x00;

       WriteReportBuffer[22] = 0x00;

       WriteReportBuffer[23] = 0x00;

       WriteReportBuffer[24] = 0x00;

       DWORD lpNumberOfBytesWritten;



       //µ÷ÓÃWriteFileº¯Êý·¢ËÍÊý¾Ý

       //±¨Îij¤¶ÈÊÇ9×Ö½Ú

       Result = WriteFile(hUsb,

             WriteReportBuffer,

             25,

             &lpNumberOfBytesWritten,

             NULL);



       printf("Written %d bytes Result [%d]\n", lpNumberOfBytesWritten, Result);



}

void GetData(HANDLE hUsb)

{

       //½ÓÊÕ±¨¸æµÄ»º³åÇø£¬1×Ö½Ú±¨¸æID+8×Ö½Ú±¨¸æÊý¾Ý¡£                          

       UCHAR ReadReportBuffer[25];

       DWORD lpNumberOfBytesRead;

       UINT LastError;

       BOOL Result;

       DWORD tmp;



       //µ÷ÓÃReadFile ½ÓÊÕÊý¾Ý

       Result = ReadFile(

             hUsb,

             ReadReportBuffer,

              25,

             &lpNumberOfBytesRead,

             NULL);

       //printf("Read %d bytes\n", lpNumberOfBytesRead);

       //Èç¹ûº¯Êý·µ»ØÊ§°Ü£¬Ôò¿ÉÄÜÊÇÕæµÄʧ°Ü£¬Ò²¿ÉÄÜÊÇIO¹ÒÆð

       if (Result == FALSE)

       {

             //»ñÈ¡×îºó´íÎó´úÂë

             LastError = GetLastError();

             //¿´ÊÇ·ñÊÇÕæµÄIO¹Ò        

             if ((LastError == ERROR_IO_PENDING) || (LastError == ERROR_SUCCESS))

             {

                    exit;

             }

             //·ñÔò£¬ÊǺ¯Êýµ÷ÓÃʱ·¢Éú´íÎó£¬ÏÔʾ´íÎó´úÂë

             else

             {

                    printf("Sending error£º%d \n", LastError);

                    //Èç¹û×îºó´íÎóΪ1£¬ËµÃ÷¸ÃÉ豸²»Ö§³Ö¸Ãº¯Êý¡£

                    if (LastError == 1)

                    {

                           printf("This device doesn't support WriteFile function \n");

                    }

             }

       }

       else //Õý³£¶ÁÈ¡

       {

             for (DWORD Index = 0; Index < lpNumberOfBytesRead; Index++) {

                    printf("%02X ", ReadReportBuffer[Index]);

             }

             printf("\n");

             if (0x44 == ReadReportBuffer[0x03]) {

                    tmp = (ReadReportBuffer[0x06] << 8) + ReadReportBuffer[0x07];

                    printf("%d.%d\n", tmp / 10000, (tmp - tmp /10000 * 10000 +5)/10);

             }

       }



       printf("\n");

}

int main(array<System::String ^> ^args)

{  

        GUID HidGuid;

        BOOL Result;

        int  counter=-1;



       //»ñÈ¡HIDÉ豸µÄ½Ó¿ÚÀàGUDI

             HidD_GetHidGuid(&HidGuid);

       //Êä³öһϿ´¿´GUID

             printf("HID GUID: {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n"

                , HidGuid.Data1, HidGuid.Data2, HidGuid.Data3

                , HidGuid.Data4[0], HidGuid.Data4[1], HidGuid.Data4[2], HidGuid.Data4[3], HidGuid.Data4[4]

                , HidGuid.Data4[5], HidGuid.Data4[6], HidGuid.Data4[7] );

      

       //¸ù¾Ý»ñµÃµÄGUIDö¾ÙHIDÉ豸

       HDEVINFO hDevInfo = SetupDiGetClassDevs( &HidGuid, NULL, 0, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE );

    if( INVALID_HANDLE_VALUE != hDevInfo )

    {

        SP_DEVICE_INTERFACE_DATA strtInterfaceData = { sizeof(SP_DEVICE_INTERFACE_DATA) };

        for( DWORD index=0; SetupDiEnumDeviceInterfaces(hDevInfo,NULL,&HidGuid,index,&strtInterfaceData); ++index )

        {

            char buf[1000];

            SP_DEVICE_INTERFACE_DETAIL_DATA& strtDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA&)buf[0];

            strtDetailData.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

            if( SetupDiGetDeviceInterfaceDetail(hDevInfo,&strtInterfaceData,&strtDetailData,_countof(buf),NULL,NULL) )

            {

                printf("[%d] path: %ls\n", index, strtDetailData.DevicePath);

                          

                           //ÕâÀï´ò¿ªµÄÓпÉÄÜÊÇUSB¼üÅÌÊó±êÕâÑù±È½ÏÌØ±ðµÄÉ豸£¨Ö»Äܲéѯ£©

                HANDLE hUsb = CreateFile( strtDetailData.DevicePath,

                                               NULL, FILE_SHARE_WRITE,

                                               NULL, OPEN_EXISTING,

                                               FILE_ATTRIBUTE_NORMAL,

                                               NULL );

                            

                // ²éѯÉ豸±êʶ

                HIDD_ATTRIBUTES strtAttrib = { sizeof(HIDD_ATTRIBUTES) };

                           Result=HidD_GetAttributes(hUsb,&strtAttrib);

               

                           //ËùÒÔÕâÀïÒª¹Ø±Õһϣ¬ºóÃæÕÒµ½ÎÒÃÇ×Ô¼ºµÄÉ豸ȷ¶¨¿ÉÒÔдÈëÔÙ´ò¿ªÒ»´Î

                        CloseHandle( hUsb );

               

                           if(TRUE==Result)

                    {

                                 if ((0x0483 == strtAttrib.VendorID) &&

                                        (0x5751 == strtAttrib.ProductID))       //ÕÒµ½ÎÒÃÇ×Ô¼ºµÄÉ豸

                                 {

                                        printf("VendorID : %hX\n", strtAttrib.VendorID);

                                        printf("ProductID: %hX\n", strtAttrib.ProductID);

                                        printf("VerNumber: %hX\n", strtAttrib.VersionNumber);



                                        //È·¶¨ÊÇÎÒÃÇ×Ô¼ºµÄÉ豸£¬ÔÙ´ò¿ªÒ»´Î,×¢ÒâÎÒÃÇÕâÀïʹÓõÄÊÇͬ²½·¢ËÍ

                                        hUsb = CreateFile(strtDetailData.DevicePath,

                                               GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE,

                                               NULL, OPEN_EXISTING,

                                               FILE_ATTRIBUTE_NORMAL, NULL);



                                        Switch(hUsb); GetData(hUsb);

                                        //Sleep(100);

                                        SendData(hUsb); GetData(hUsb);

                                        Switch(hUsb); GetData(hUsb);

                                        SendData(hUsb); GetData(hUsb);

                                       

                                       



                                                      CloseHandle( hUsb );

                                                     }//if ((0x8888==strtAttrib.VendorID) &&

                                 }            //if(TRUE==Result)

            } // if( SetupDiGetDeviceInterfaceDetail(hDevInfo,&strtInterfaceData,&strtDetailData,_countof(buf),NULL,NULL) )



        }    //for( DWORD index=0;

       

             if( GetLastError() != ERROR_NO_MORE_ITEMS )

                    {printf("No more items!\n"); }



        SetupDiDestroyDeviceInfoList( hDevInfo );

    }  //if( INVALID_HANDLE_VALUE != hDevInfo )



       system("PAUSE");

    return 0;

}

 

运行结果:

命令行刷写 Leonardo

有些情况下,我们需要在命令行下刷写 Leonardo 的 Firmware,一般的方法会遇到问题,原因是 Leonardo 进入 Bootloader 的方式比较特别:使用 1200波特率打开串口,Leonardo立即开始重启,重启之后运行的Booloader会让串口变化成为另外的编号,在这个过程中如果有握手,那么就开始 Firmware 的更新。因为中间串口会发生变化,因此一般的刷新方法都会都会碰到问题。

最近,我在网上网上看到了一个高手编写的代码,能够在 Windows下实现全自动的命令行刷写https://github.com/p1ne/arduino-leonardo-uploader/tree/master/windows

核心是一个 BAT 批处理文件,首先用 WMI 在系统中查找包含 Leonardo 字样的串口,然后用 mode 来设置这个串口,这样 Leonardo 就会去Reset。接下来再使用 WMI 在系统中查找Leonardo Bootloader 的字样确定新出现的串口号。然后就用 avrdude 来进行正常刷写了。

具体代码如下,相信看懂并不困难:

rem @echo off
setlocal

for /f "tokens=1* delims==" %%I in ('wmic path win32_pnpentity get caption  /format:list ^| find "Arduino"') do (
    call :resetCOM "%%~J"
)

:continue

:: wmic /format:list strips trailing spaces (at least for path win32_pnpentity)
for /f "tokens=1* delims==" %%I in ('wmic path win32_pnpentity get caption  /format:list ^| find "Arduino Leonardo bootloader"') do (
    call :setCOM "%%~J"
)

:: end main batch
goto :EOF

:resetCOM <WMIC_output_line>
:: sets _COM#=line
setlocal
set "str=%~1"
set "num=%str:*(COM=%"
set "num=%num:)=%"
set port=COM%num%
echo %port%
mode %port%: BAUD=1200 parity=N data=8 stop=1
timeout /t 1
goto :continue

:setCOM <WMIC_output_line>
:: sets _COM#=line
setlocal
set "str=%~1"
set "num=%str:*(COM=%"
set "num=%num:)=%"
set port=COM%num%
echo %port%
goto :flash

:flash
avrdude -v -CD:\arduino-1.8.4\hardware\tools\avr\etc\avrdude.conf -patmega32u4 -cavr109 -P%port% -b57600 -D -V -Uflash:w:./blink.ino.hex:i

 

特别注意的是:
1. 如果你在 Win10 上使用并且没有安装 Leonardo 驱动的话,设备名称不会出来 Arduino 字样,安装2个驱动(一个是 Bootloader的,一个是 Arduino 本身的)之后即可正常工作;
2. 注意最后 avrdude.conf 的路径不要搞错了
3. 要刷写的文件也是在最后一条命令上,我这里使用的是blink.ino.hex:i

我这边实验的结果如下:

完整的log 如下:
D:\arduino-1.8.4\hardware\tools\avr\bin>f

D:\arduino-1.8.4\hardware\tools\avr\bin>rem @echo off

D:\arduino-1.8.4\hardware\tools\avr\bin>setlocal

D:\arduino-1.8.4\hardware\tools\avr\bin>for /F “tokens=1* delims==” %I in (‘wmic path win32_pnpentity get caption /format:list | find “Arduino”‘) do (call :resetCOM “%~J” )

” ) rduino-1.8.4\hardware\tools\avr\bin>(call :resetCOM “Arduino Leonardo (COM9)

D:\arduino-1.8.4\hardware\tools\avr\bin>setlocal

D:\arduino-1.8.4\hardware\tools\avr\bin>set “str=Arduino Leonardo (COM9)”

D:\arduino-1.8.4\hardware\tools\avr\bin>set “num=9)”

D:\arduino-1.8.4\hardware\tools\avr\bin>set “num=9”

D:\arduino-1.8.4\hardware\tools\avr\bin>set port=COM9

D:\arduino-1.8.4\hardware\tools\avr\bin>echo COM9
COM9

D:\arduino-1.8.4\hardware\tools\avr\bin>mode COM9: BAUD=1200 parity=N data=8 stop=1

Status for device COM9:
———————–
Baud: 1200
Parity: None
Data Bits: 8
Stop Bits: 1
Timeout: OFF
XON/XOFF: OFF
CTS handshaking: OFF
DSR handshaking: OFF
DSR sensitivity: OFF
DTR circuit: OFF
RTS circuit: ON

D:\arduino-1.8.4\hardware\tools\avr\bin>timeout /t 1

Waiting for 0 seconds, press a key to continue …

D:\arduino-1.8.4\hardware\tools\avr\bin>goto :continue

D:\arduino-1.8.4\hardware\tools\avr\bin>for /F “tokens=1* delims==” %I in (‘wmic path win32_pnpentity get caption /format:list | find “Arduino Leonardo bootloader”‘) do (call :setCOM “%~J” )

” ) rduino-1.8.4\hardware\tools\avr\bin>(call :setCOM “Arduino Leonardo bootloader (COM7)

D:\arduino-1.8.4\hardware\tools\avr\bin>setlocal

D:\arduino-1.8.4\hardware\tools\avr\bin>set “str=Arduino Leonardo bootloader (COM7)”

D:\arduino-1.8.4\hardware\tools\avr\bin>set “num=7)”

D:\arduino-1.8.4\hardware\tools\avr\bin>set “num=7”

D:\arduino-1.8.4\hardware\tools\avr\bin>set port=COM7

D:\arduino-1.8.4\hardware\tools\avr\bin>echo COM7
COM7

D:\arduino-1.8.4\hardware\tools\avr\bin>goto :flash

D:\arduino-1.8.4\hardware\tools\avr\bin>avrdude -v -CD:\arduino-1.8.4\hardware\tools\avr\etc\avrdude.conf -patmega32u4 -cavr109 -PCOM7 -b57600 -D -V -Uflash:w:./blink.ino.hex:i

avrdude: Version 6.3, compiled on Jan 17 2017 at 12:00:53
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2014 Joerg Wunsch

System wide configuration file is “D:\arduino-1.8.4\hardware\tools\avr\etc\avrdude.conf”

Using Port : COM7
Using Programmer : avr109
Overriding Baud Rate : 57600
AVR Part : ATmega32U4
Chip Erase delay : 9000 us
PAGEL : PD7
BS2 : PA0
RESET disposition : dedicated
RETRY pulse : SCK
serial program mode : yes
parallel program mode : yes
Timeout : 200
StabDelay : 100
CmdexeDelay : 25
SyncLoops : 32
ByteDelay : 0
PollIndex : 3
PollValue : 0x53
Memory Detail :

Block Poll Page Polled
Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
———– —- —– —– —- —— —— —- —— —– —– ———
eeprom 65 20 4 0 no 1024 4 0 9000 9000 0x00 0x00
flash 65 6 128 0 yes 32768 128 256 4500 4500 0x00 0x00
lfuse 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00
hfuse 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00
efuse 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00
lock 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00
calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00
signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00

Programmer Type : butterfly
Description : Atmel AppNote AVR109 Boot Loader

Connecting to programmer: .
Found programmer: Id = “CATERIN”; type = S
Software Version = 1.0; No Hardware Version given.
Programmer supports auto addr increment.
Programmer supports buffered memory access with buffersize=128 bytes.

Programmer supports the following devices:
Device code: 0x44

avrdude: devcode selected: 0x44
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e9587 (probably m32u4)
avrdude: safemode: hfuse reads as D8
avrdude: safemode: efuse reads as CB
avrdude: reading input file “./blink.ino.hex”
avrdude: writing flash (4142 bytes):

Writing | ################################################## | 100% 0.68s

avrdude: 4142 bytes of flash written

avrdude: safemode: hfuse reads as D8
avrdude: safemode: efuse reads as CB
avrdude: safemode: Fuses OK (E:CB, H:D8, L:FF)

avrdude done. Thank you.

D:\arduino-1.8.4\hardware\tools\avr\bin>goto :EOF

D:\arduino-1.8.4\hardware\tools\avr\bin>for /F “tokens=1* delims==” %I in (‘wmic path win32_pnpentity get caption /format:list | find “Arduino Leonardo bootloader”‘) do (call :setCOM “%~J” )

” ) rduino-1.8.4\hardware\tools\avr\bin>(call :setCOM “Arduino Leonardo bootloader (COM7)

D:\arduino-1.8.4\hardware\tools\avr\bin>setlocal

D:\arduino-1.8.4\hardware\tools\avr\bin>set “str=Arduino Leonardo bootloader (COM7)”

D:\arduino-1.8.4\hardware\tools\avr\bin>set “num=7)”

D:\arduino-1.8.4\hardware\tools\avr\bin>set “num=7”

D:\arduino-1.8.4\hardware\tools\avr\bin>set port=COM7

D:\arduino-1.8.4\hardware\tools\avr\bin>echo COM7
COM7

D:\arduino-1.8.4\hardware\tools\avr\bin>goto :flash

D:\arduino-1.8.4\hardware\tools\avr\bin>avrdude -v -CD:\arduino-1.8.4\hardware\tools\avr\etc\avrdude.conf -patmega32u4 -cavr109 -PCOM7 -b57600 -D -V -Uflash:w:./blink.ino.hex:i

avrdude: Version 6.3, compiled on Jan 17 2017 at 12:00:53
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2014 Joerg Wunsch

System wide configuration file is “D:\arduino-1.8.4\hardware\tools\avr\etc\avrdude.conf”

Using Port : COM7
Using Programmer : avr109
Overriding Baud Rate : 57600
avrdude: ser_open(): can’t open device “\\.\COM7”: The system cannot find the file specified.

avrdude done. Thank you.

D:\arduino-1.8.4\hardware\tools\avr\bin>goto :EOF

RTC Wakeup的秘密

RTC 是一个 Wake Up source,因此可以用它来设置一个时间,每天的那个时间都会自动进行唤醒。
同样,使用这样的原理,可以编写一个 Long run 的测试工具让系统不断关机然后启动,这样可以用来完成 S5 的loop 测试。

具体操作:
1. 进入 Setup 打开 RTC Wake 功能,设置一个唤醒时间,比如:0:5:0
2. 进入系统(UEFI Shell 或者 Windows)之后,重置时间到 0:0:0
3. 关机

之后静静等待,过一会就能唤醒了(RTC 到 0:5:0)。需要特别注意的是:时间不要超过你设定的唤醒时间,比如上面例子中,没有步骤二,结果关机时已经是0:6:0,那么只能等接近24小时才有机会唤醒了;此外,如果你在Win10中手工实验上述动作时发现无法唤醒,很可能的原因是第三步直接使用菜单关机。实际上Win10是做的S4,只有ctrl关机才是真正S5,这里可以从80 Port输出是否为 0005 看出来。

产生这个现象的原因是许多IBV 只在 S5 的SMI相关代码中设置 RTC Wake 相关寄存器,如果按照S4的流程运行就不会跑到其中,因此也无法做到 RTC 唤醒。

EFI_STATUS
SxSleepEntryCallBack (
  IN  EFI_HANDLE                    DispatchHandle,
  IN  CONST VOID                    *DispatchContext,
  IN  OUT VOID                      *CommBuffer,
  IN  OUT UINTN                     *CommBufferSize
  )

 

个人感觉这个应该不是什么 bug ,因为最开始 S4 和 S5 相对独立,哪知道后面 ms 为了更好的用户体验将他们混合在一起—-对于普通用户来说,他们甚至无法得知当前究竟进入的是哪个睡眠状态。

当然,随着时代的发展, S3/S4/S5 即将成为历史,BIOS 工程师会在痛苦中迎接 Connected Standby 的到来,亦如当时 S3/S4 的到来带来的痛苦一样…….