如何编写Linux下的USB键盘驱动

Linux下的硬件驱动——USB设备(下)(驱动开发部分) 赵明(carl__zhao@163.com) 联想软件设计中心嵌入式研发处系统设计工程师 2003年7月 USB骨架程序(usb-skeleton),是USB驱动程序的基础,通过对它源码的学习和理解,可以使我们迅速地了解USB驱动架构,迅速地开发我们自己的USB硬件的驱动。 前言 在上篇《Linux下的硬件驱动--USB设备(上)(驱动配制部分)》中,我们知道了在Linux下如何去使用一些最常见的 阅读详情

1.   指定USB键盘驱动所需的头文件:

#include <linux/kernel.h>/*内核头文件,含有内核一些常用函数的原型定义*/

 

#include <linux/slab.h>/*定义内存分配的一些函数*/

 

#include <linux/module.h>/*模块编译必须的头文件*/

 

#include <linux/input.h>/*输入设备相关函数的头文件*/

 

#include <linux/init.h>/*linux初始化模块函数定义*/

 

#include <linux/usb.h> /*USB设备相关函数定义*/


2.   定义键盘码表数组:

/*使用第一套键盘扫描码表:A-1E;B-30;C-2E…*/

 

static unsigned char usb_kbd_keycode[256] = {

 

    0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,

 

    50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,

 

    4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,

 

    27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,

 

    65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,

 

    105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,

 

    72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,

 

    191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,

 

    115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,

 

    122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

 

    29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,

 

    150,158,159,128,136,177,178,176,142,152,173,140


};
3.   编写设备ID表:

static struct usb_device_id usb_kbd_id_table [] = {

 

    { USB_INTERFACE_INFO(3, 1, 1) },/*3,1,1分别表示接口类,接口子类,接口协议;3,1,1为键盘接口类;鼠标为3,1,2*/

 

    { }           /* Terminating entry */

 

};


MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);/*指定设备ID表*/
4.   定义USB键盘结构体:

struct usb_kbd {

 

    struct input_dev *dev; /*定义一个输入设备*/

 

    struct usb_device *usbdev;/*定义一个usb设备*/

 

    unsigned char old[8]; /*按键离开时所用之数据缓冲区*/

 

    struct urb *irq/*usb键盘之中断请求块*/, *led/*usb键盘之指示灯请求块*/;

 

    unsigned char newleds;/*目标指定灯状态*/

 

    char name[128];/*存放厂商名字及产品名字*/

 

    char phys[64];/*设备之节点*/

 

 

    unsigned char *new;/*
按键按下时所用之数据缓冲区*/

 

    struct usb_ctrlrequest *cr;/*控制请求结构*/

 

    unsigned char *leds;/*当前指示灯状态*/

 

    dma_addr_t cr_dma; /*控制请求DMA缓冲地址*/

 

    dma_addr_t new_dma; /*中断urb会使用该DMA缓冲区*/

 

    dma_addr_t leds_dma; /*指示灯DAM缓冲地址*/


};
5.   编写USB键盘驱动结构(任何一个LINUX下的驱动都有个类似的驱动结构):

/*USB键盘驱动结构体*/

 

static struct usb_driver usb_kbd_driver = {

 

    .name =   "usbkbd",/*驱动名字*/

 

    .probe = usb_kbd_probe,/*驱动探测函数,加载时用到*/

 

    .disconnect = usb_kbd_disconnect,/*驱动断开函数,在卸载时用到*/

 

    .id_table =   usb_kbd_id_table,/*驱动设备ID表,用来指定设备或接口*/

 

};


6.   编写模块加载函数(每个驱动都会有一个加载函数,由module_init调用):

/*驱动程序生命周期的开始点,向 USB core 注册这个键盘驱动程序。*/

 

static int __init usb_kbd_init(void)

 

{

 

    int result = usb_register(&usb_kbd_driver);/*注册USB键盘驱动*/

 

    if (result == 0) /*注册失败*/

 

    info(DRIVER_VERSION ":" DRIVER_DESC);

 

    return result;


}
7.   编写模块卸载函数(每个驱动都会有一个卸载函数,由module_exit调用):

/* 驱动程序生命周期的结束点,向 USB core 注销这个键盘驱动程序。 */

 

static void __exit usb_kbd_exit(void)

 

{

 

    printk("SUNWILL-USBKBD:usb_kbd_exit begin.../n");

 

    usb_deregister(&usb_kbd_driver);/*注销USB键盘驱动*/


}
8.   指定模块初始化函数(被指定的函数在insmod驱动时调用):

module_init(usb_kbd_init);

9.   指定模块退出函数(被指定的函数在rmmod驱动时调用):

module_exit(usb_kbd_exit);

10.   编写中断请求处理函数:

/*中断请求处理函数,有中断请求到达时调用该函数*/

 

static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)

 

{

 

    struct usb_kbd *kbd = urb->context;

 

    int i;

 

      switch (urb->status) {

 

    case 0:       /* success */

 

        break;

 

    case -ECONNRESET: /* unlink */

 

    case -ENOENT:

 

    case -ESHUTDOWN:

 

        return;

 

    /* -EPIPE: should clear the halt */

 

    default:   /* error */

 

    goto resubmit;

 

    }

 

 

    //input_regs(kbd->dev, regs);

 

 

    /*
不知道其用意, 注释掉该部分仍可正常工作*/

 

    for (i = 0; i < 8; i++)/*8次的值依次是:29-42-56-125-97-54-100-126*/

 

    {

 

    input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);

 

    }

 

   

 

/*若同时只按下1个按键则在第[2]个字节,若同时有两个按键则第二个在第[3]字节,类推最多可有6个按键同时按下*/

 

    for (i = 2; i < 8; i++) {

 

    /*获取键盘离开的中断*/

 

    if (kbd->old > 3 && memscan(kbd->new + 2, kbd->old, 6) == kbd->new + 8) {/*同时没有该KEY的按下状态*/

 

        if (usb_kbd_keycode[kbd->old])

 

        {

 

        input_report_key(kbd->dev, usb_kbd_keycode[kbd->old], 0);

 

        }

 

        else

 

          info("Unknown key (scancode %#x) released.", kbd->old);

 

    }

 

 

    /*
获取键盘按下的中断*/

 

    if (kbd->new > 3 && memscan(kbd->old + 2, kbd->new, 6) == kbd->old + 8) {/*同时没有该KEY的离开状态*/

 

        if (usb_kbd_keycode[kbd->new])

 

        {

 

          input_report_key(kbd->dev, usb_kbd_keycode[kbd->new], 1);

 

        }

 

        else

 

          info("Unknown key (scancode %#x) pressed.", kbd->new);

 

    }

 

    }

 

    /*同步设备,告知事件的接收者驱动已经发出了一个完整的报告*/

 

    input_sync(kbd->dev);

 

    memcpy(kbd->old, kbd->new, 8);/*防止未松开时被当成新的按键处理*/

 

resubmit:

 

    i = usb_submit_urb (urb, GFP_ATOMIC);/*发送USB请求块*/

 

    if (i)

 

    err ("can't resubmit intr, %s-%s/input0, status %d",

 

        kbd->usbdev->bus->bus_name,

 

        kbd->usbdev->devpath, i);


}
11.   编写事件处理函数:

/*事件处理函数*/

 

static int usb_kbd_event(struct input_dev *dev, unsigned int type,

 

      unsigned int code, int value)

 

{

 

    struct usb_kbd *kbd = dev->private;

 

if (type != EV_LED) /*不支持LED事件 */

 

    return -1;

 

    /*获取指示灯的目标状态*/

 

    kbd->newleds = (!!test_bit(LED_KANA,   dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |

 

      (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |

 

      (!!test_bit(LED_NUML,   dev->led));

 

 

    if (kbd->led->status == -EINPROGRESS)

 

    return 0;

 

 

    /*
指示灯状态已经是目标状态则不需要再做任何操作*/

 

    if (*(kbd->leds) == kbd->newleds)

 

    return 0;

 

 

    *(kbd->leds) = kbd->newleds;

 

    kbd->led->dev = kbd->usbdev;

 

    /*发送usb请求块*/

 

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))

 

    err("usb_submit_urb(leds) failed");

 

    return 0;


}
12.   编写LED事件处理函数:

/*接在event之后操作,该功能其实usb_kbd_event中已经有了,该函数的作用可能是防止event的操作失败,一般注释掉该函数中的所有行都可以正常工作*/

 

static void usb_kbd_led(struct urb *urb, struct pt_regs *regs)

 

{

 

    struct usb_kbd *kbd = urb->context;

 

 

    if (urb->status)

 

    warn("led urb status %d received", urb->status);

 

 

    if (*(kbd->leds) == kbd->newleds)/*
指示灯状态已经是目标状态则不需要再做任何操作*/

 

    return;

 

 

    *(kbd->leds) = kbd->newleds;

 

    kbd->led->dev = kbd->usbdev;

 

    if (usb_submit_urb(kbd->led, GFP_ATOMIC))

 

      err("usb_submit_urb(leds) failed");


}
13.   编写USB设备打开函数:

/*打开键盘设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。 */

 

static int usb_kbd_open(struct input_dev *dev)

 

{

 

    struct usb_kbd *kbd = dev->private;

 

    kbd->irq->dev = kbd->usbdev;

 

    if (usb_submit_urb(kbd->irq, GFP_KERNEL))

 

      return -EIO;

 

    return 0;


}
14.   编写USB设备关闭函数

/*关闭键盘设备时,结束 urb 生命周期。 */

 

static void usb_kbd_close(struct input_dev *dev)

 

{

 

    struct usb_kbd *kbd = dev->private;

 

    usb_kill_urb(kbd->irq); /*取消kbd->irq这个usb请求块*/


}
15.   创建URB

/*分配URB内存空间即创建URB*/

 

static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)

 

{

 

    if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))

 

    return -1;

 

    if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))

 

    return -1;

 

    if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))

 

    return -1;

 

    if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))

 

    return -1;

 

    if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))

 

    return -1;

 

    return 0;


}
16.   销毁URB

/*释放URB内存空间即销毁URB*/

 

static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)

 

{

 

    if (kbd->irq)

 

      usb_free_urb(kbd->irq);

 

    if (kbd->led)

 

      usb_free_urb(kbd->led);

 

    if (kbd->new)

 

      usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);

 

    if (kbd->cr)

 

      usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);

 

    if (kbd->leds)

 

      usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);

 

}


17.   USB键盘驱动探测函数:

/*USB键盘驱动探测函数,初始化设备并指定一些处理函数的地址*/

 

static int usb_kbd_probe(struct usb_interface *iface,

 

        const struct usb_device_id *id)

 

{

 

    struct usb_device *dev = interface_to_usbdev(iface);

 

    struct usb_host_interface *interface;

 

    struct usb_endpoint_descriptor *endpoint;

 

    struct usb_kbd *kbd;

 

    struct input_dev *input_dev;

 

    int i, pipe, maxp;

 

    /*当前选择的interface*/

 

    interface = iface->cur_altsetting;

 

    /*键盘只有一个中断IN端点*/

 

    if (interface->desc.bNumEndpoints != 1)

 

    return -ENODEV;

 

    /*获取端点描述符*/

 

    endpoint = &interface->endpoint[0].desc;

 

    if (!(endpoint->bEndpointAddress & USB_DIR_IN))

 

      return -ENODEV;

 

    if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)

 

      return -ENODEV;

 

    /*将endpoint设置为中断IN端点*/

 

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

 

    /*获取包的最大值*/

 

    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

 

    kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);

 

    input_dev = input_allocate_device();

 

    if (!kbd || !input_dev)

 

      goto fail1;

 

 

    if (usb_kbd_alloc_mem(dev, kbd))

 

      goto fail2;

 

    /* 填充 usb 设备结构体和输入设备结构体 */

 

    kbd->usbdev = dev;

 

    kbd->dev = input_dev;

 

 

    /*
以"厂商名字 产品名字"的格式将其写入kbd->name*/

 

    if (dev->manufacturer)

 

      strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));

 

 

    if (dev->product) {

 

    if (dev->manufacturer)

 

        strlcat(kbd->name, " ", sizeof(kbd->name));

 

    strlcat(kbd->name, dev->product, sizeof(kbd->name));

 

    }

 

 

    /*
检测不到厂商名字*/

 

    if (!strlen(kbd->name))

 

    snprintf(kbd->name, sizeof(kbd->name),

 

        "USB HIDBP Keyboard %04x:%04x",

 

        le16_to_cpu(dev->descriptor.idVendor),

 

        le16_to_cpu(dev->descriptor.idProduct));

 

    /*设备链接地址*/

 

    usb_make_path(dev, kbd->phys, sizeof(kbd->phys));

 

    strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));    

 

    input_dev->name = kbd->name;

 

 

    input_dev->phys = kbd->phys;

 

    /*

 

* input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符

 

    * 中的编号赋给内嵌的输入子系统结构体

 

    */

 

    usb_to_input_id(dev, &input_dev->id);

 

    /* cdev 是设备所属类别(class device) */

 

    input_dev->cdev.dev = &iface->dev;

 

/* input_dev 的 private 数据项用于表示当前输入设备的种类,这里将键盘结构体对象赋给它 */

 

    input_dev->private = kbd;

 

input_dev->evbit[0] = BIT(EV_KEY)/*键码事件*/ | BIT(EV_LED)/*LED事件*/ | BIT(EV_REP)/*自动重覆数值*/;

 

input_dev->ledbit[0] = BIT(LED_NUML)/*数字灯*/ | BIT(LED_CAPSL)/*大小写灯*/ | BIT(LED_SCROLLL)/*滚动灯*/ | BIT(LED_COMPOSE) | BIT(LED_KANA);

 

 

    for (i = 0; i < 255; i++)

 

    set_bit(usb_kbd_keycode, input_dev->keybit);

 

    clear_bit(0, input_dev->keybit);

 

 

    input_dev->event = usb_kbd_event;/*
注册事件处理函数入口*/

 

    input_dev->open = usb_kbd_open;/*注册设备打开函数入口*/

 

    input_dev->close = usb_kbd_close;/*注册设备关闭函数入口*/

 

 

    /*
初始化中断URB*/

 

    usb_fill_int_urb(kbd->irq/*初始化kbd->irq这个urb*/, dev/*这个urb要发送到dev这个设备*/, pipe/*这个urb要发送到pipe这个端点*/,

 

        kbd->new/*指向缓冲的指针*/, (maxp > 8 ? 8 : maxp)/*缓冲长度*/,

 

        usb_kbd_irq/*这个urb完成时调用的处理函数*/, kbd/*指向数据块的指针,被添加到这个urb结构可被完成处理函数获取*/, endpoint->bInterval/*urb应当被调度的间隔*/);

 

    kbd->irq->transfer_dma = kbd->new_dma; /*指定urb需要传输的DMA缓冲区*/

 

kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/*本urb有一个DMA缓冲区需要传输*/

 

    kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;/*操作的是类接口对象*/

 

    kbd->cr->bRequest = 0x09; /*中断请求编号*/

 

    kbd->cr->wValue = cpu_to_le16(0x200);

 

    kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);/*接口号*/

 

    kbd->cr->wLength = cpu_to_le16(1);/*数据传输阶段传输多少个bytes*/

 

 

    /*
初始化控制URB*/

 

    usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),

 

          (void *) kbd->cr, kbd->leds, 1,

 

          usb_kbd_led, kbd);

 

    kbd->led->setup_dma = kbd->cr_dma;

 

    kbd->led->transfer_dma = kbd->leds_dma;

 

    kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP/*如果使用DMA传输则urb中setup_dma指针所指向的缓冲区是DMA缓冲区而不是setup_packet所指向的缓冲区*/);

 

 

    /*
注册输入设备*/

 

    input_register_device(kbd->dev);

 

 

    usb_set_intfdata(iface, kbd);/*设置接口私有数据*/

 

    return 0;

 

 

fail2:   usb_kbd_free_mem(dev, kbd);

 

fail1:   input_free_device(input_dev);

 

    kfree(kbd);

 

    return -ENOMEM;


}
18.   编写断开连接的函数:

/*断开连接(如键盘设备拔出)的处理函数*/

 

static void usb_kbd_disconnect(struct usb_interface *intf)

 

{

 

    struct usb_kbd *kbd = usb_get_intfdata (intf);/*获取接口的私有数据给kbd*/

 

    usb_set_intfdata(intf, NULL);/*设置接口的私有数据为NULL*/

 

    if (kbd) {

 

    usb_kill_urb(kbd->irq);/*取消中断请求*/

 

    input_unregister_device(kbd->dev);/*注销设备*/

 

    usb_kbd_free_mem(interface_to_usbdev(intf), kbd);/*释放内存空间*/

 

    kfree(kbd);

 

    }


}
19.   编写Makefile:

##############################

 

#usbkdb Makefile for linux

 

##############################

 

obj-m:=usbkbd.o

 

KERNELDIR ?= /lib/modules/$(shell uname -r)/build

 

PWD:=$(shell pwd)

 

default:


$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

Linux内核编程(二十一)USB驱动开发-键盘驱动 USB 驱动开发主要分为两种:主机侧的驱动程序和设备侧的驱动程序。主机侧驱动程序用于控制插入到主机中的 USB 设备,而设备侧驱动程序则负责控制 USB 设备如何与主机通信。由于设备侧驱动程序通常与设备功能紧密相关,因此常常被称为 USB gadget 驱动程序。USB gadget 驱动程序的作用是定义如何通过 USB 协议栈与主机端进行通信,确保设备能够正确响应主机的请求。在 USB 系统中,主机侧和设备侧有各自不同的控制器。 阅读详情

相关推荐

2025软考信息安全工程师真题

2025信息安全工程师软考真题

qq_40424393的博客 1707

Linux 自带按键驱动

Linux 自带按键驱动及其设备树节点编写

weixin_46829095的博客 1576

拌合楼管理软件开发(十一) 海康威视车牌识别摄像头安装调试,总算是跑通了。

/保存的文件个数这里之所以需要用到委托,直接更新textbox文本中的内容,以及复制picturebox的image会提示不是从齐创建的线程调用。

morsth的博客 4814

linux按键驱动(input子系统)

linux按键驱动(input子系统)

Calmer_的博客 1181

jenkins安装、打包、部署

一个可以让小白部署发布项目的软件;git 配置 一定要配置git的命令路径jenkins启动命令,一定要加这一段 -Dhudson.util.ProcessTree.disable=true一定要配置jenkins的节点,BUILD_ID和DONTKILLME 否则会导致打包后,会将运行的java程序进程也杀死创建任务SSH中执行服务器B的命令的时候,cd /opt;一定要cd 打开那个目录,否则文件无法传输过去。

学海无涯,我用JAVA 5217

使用STC15F104W产生PWM脉冲

最近的小项目中使用到了STC15F104W产生PWM脉冲

yeats1989的专栏 1万+

【Linux】按键驱动程序

深入学习按键驱动程序,熟悉读取数据的方式。

希希雾里 2314

树莓派编译程序时遇到卡死的问题

使用树莓派编译程序的时候,我遇到了程序比较大,编译到某个文件的时候树莓派会死机,关机之后重启可以继续使用,但是编译到同一个地方树莓派又死机的问题。 这个问题出现的主要原因是我给树莓派安装的Ubuntu mate 16.04 系统默认设置的swap交换空间不够,而编译某些文件的时候需要较大的交换空间,树莓派的交换空间被用满所以树莓派看起来好像是死机了的样子。 解决方法,扩展Ubuntu的交换空间。 解决方法,扩展Ubu

一杯二锅头 1万+

Linux驱动开发(按键驱动)

本篇文章我们来讲解按键的驱动程序,按键驱动程序的主要思路就是使用中断的方式,当按键按下时会发生中断这个时候就可以通过中断来获取按键的状态。按键驱动的编写是比较简单的,主要就是需要注意设备树的编写和gpio硬件信息的获取函数。

m0_49476241的博客 1598

一分钟学会MATLAB-数据清洗(含完整代码)

数据清洗是数据分析过程中的一个重要步骤,目的是提高数据的质量,以便进行更有效的分析。% 删除超出阈值的行。% 用线性插值填充缺失值。% 删除包含缺失值的行。% 找到缺失值的位置。% 可视化数据,识别异常值。缺失值是数据清洗中常见的问题。通过以上步骤,你可以有效地清洗数据,使其准备好进行后续的分析和建模。

2402_87132195的博客 3316

Sigrity Power SI 3D-EM Full Wave Extraction模式如何进行S参数提取和观测3D电磁场和远场实例分析(一)-差分过孔结构

Sigrity Power SI 3D-EM Full Wave Extraction模式如何进行S参数提取和观测3D电磁场和远场操作指导(一)详细介绍了3D-EM Full Wave Extraction全波S参数提取,下面以一个差分过孔结构进行实例分析,以下图为例。模式如何进行S参数提取和观测3D电磁场和远场实例分析(一)-差分过孔结构。是一个简单的4层板,top换层到bottom层的差分过孔结构。起始频率为100Mhz,截止频率为20Ghz,线性扫描。上面那根曲线是插损,下面那根线是回损。

weixin_54787054的博客 343

十六、(正点原子)Linux自带按键驱动程序

Linux 内核也自带了 KEY 驱动,如果要使用内核自带的 KEY 驱动的话需要配置 Linux 内核,不过 Linux 内核一般默认已经使能了 KEY 驱动,但是我们还是要检查一下。选中以后就会在文件中出现“”这一行, Linux 内核就会根据这一行来将 KEY 驱动文件编译进 Linux 内核。Linux 内核自带的 KEY 驱动文件为采用了驱动框架,在 KEY 驱动上使用了子系统实现。可以看出,这就是一个标准的驱动框架,如果要使用设备树来描述 KEY 设备信息的话,设备节点的属性值要设置为“

Tofu_Cabbage的博客 1618

手把手教你学Simulink——基于重复控制(RC)的并网逆变器场景实例:基于RC的单相并网逆变器仿真

本次教程深入剖析了重复控制(RC)在单相并网逆变器中的应用,解决了周期性谐波抑制这一核心难题。核心收获RC是谐波抑制的“核武器”:它利用内模原理,对所有周期性谐波提供无限大增益,实现理论上无静差的跟踪和抑制。PI+RC是黄金组合:PI提供快速响应,RC提供稳态精度,二者结合,兼顾了动态与稳态性能。结构简单,效果卓越:相比多个PR控制器,RC仅需一个模块,即可抑制所有谐波,设计简洁。电能质量的终极追求,是波形的纯净。重复控制,如同一位不知疲倦的校对员它记住每一次错误(上一周期的误差),

MHD0815的博客 189

linux下usb键盘驱动怎么安装驱动程序,如何编写Linux下的USB键盘驱动 有注释

如何编写Linux下的USB键盘驱动1.指定USB键盘驱动所需的头文件:#include /*内核头文件,含有内核一些常用函数的原型定义*/#include /*定义内存分配的一些函数*/#include /*模块编译必须的头文件*/#include /*输入设备相关函数的头文件*/#include /*linux初始化模块函数定义*/#include /*USB设备相关函数定义*/2.定义键盘码...

weixin_39902870的博客 258

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

编写Linux usb 键盘驱动的笔记

编写linux usb 设备驱动参考: \\10.150.50.230\zhangjiaqi\linux-5.8.5\Documentation\driver-api\usb\writing_usb_driver.rst \\10.150.50.230\zhangjiaqi\linux-5.8.5\drivers\usb\usb-skeleton.c //usb设备驱动标准框架,这是一个usb磁盘驱动程序,用户app通过open,wrire,read读写磁盘(u盘); 写linux ...

qq_43418840的博客 933

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

如何编写Linux 下的 USB 键盘驱动

 如何编写Linux 下的 USB 键盘驱动 1. 指定 USB 键盘驱动所需的头文件: #include /*内核头文件,含有内核一些常用函数的原型定义*/ #include /*定义内存分配的一些函数*/ #include /*模块编译必须的头文件*/ #include /*输入设备相关函数的头文件*/ #in

jemofh159的专栏 1376

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

编写Linux下的USB键盘驱动(附源码)

摘自:http://tech.techweb.com.cn/thread-383835-1-1.html   1.   指定USB键盘驱动所需的头文件: 1 t! q5 f( V4 ~9 V 3 [/ M0 P8 J/ `. d" r6 o) Y #include /*内核头文件,含有内核一些常用函数的原型定义*/ 0 H( C2 p, Z; q7 D( b% m #inc

li_qcxy的专栏 5017

8.2 USB键盘驱动编写和测试

目标:根据USB驱动分析和上节的USB鼠标驱动,编写键盘驱动,并测试。 一、原理分析 1. 首先通过打印usb_buf[i]中的8字节数据,看一下按键按下之后会接收到什么。 1)通过按完所有键盘按键打印的结果可知,有8个按键会打印在usb_buf[0]里,即: ctrl左键----0x01----00000001 shift左键----0x02----00000010...

weixin_34378922的博客 542

USB键盘驱动分析

简介 本文介绍USB驱动程序编写的流程,分析一个键盘的USB程序,基于linux-2.6.39 USB驱动概要 分层 主机层面的USB驱动的整体架构可以分成4层,自顶到下依次是 1、USB设备驱动:本文主要讲述的内容,利用USB核心提供的编程接口编写具体硬件设备与系统的交互逻辑 2、USB核心:linux内核实现,管理上层的USB设备驱动,并且对下面的USB通信机制做封装,封...

weixin_30376083的博客 290

USB键盘和鼠标在linux下的支持方式(RH7.2)

USB键盘和鼠标在linux下通过两种方式来支持,一种是HIDBP(Human Interface Device),另一种是USBKBD.O和USBMOUSE.O,这两种方法的作用完全一样(前者是内核成员而后者是内核模块,所以后者可以被动态挂载和卸载),而要测试自己的USB驱动时,因为USB键盘和鼠标在RedHat7.2下有两种方式支持:一种就是HIDBP,另一中就是USBKBD.O和USBMOU

mmpire之blog 4053

嵌入式linux下如何使用usb键盘

首先usb键盘驱动的源代码位于一下目录: drivers/usb/input/usbkbd.c 将usb键盘驱动编译进内核: #make menuconfig Device Drivers--->USB support---->USB HIDBP Keyboard (simple Boot) support (注意:有可能默认设置USB键盘驱动是不可见的,需修改当前目录下的Kconfig

piaofenglanyan的专栏 2285

linux下usb驱动编写

linux下usb驱动编写(内核2.4)——2.6与此接口有区别2006-09-15 14:57我们知道了在Linux下如何去使用一些最常见的USB设备。但对于做系统设计的程序员来说,这是远远不够的,我们还需要具有驱动程序的阅读、修改和开发能力。在此下篇中,就是要通过简单的USB驱动的例子,随您一起进入USB驱动开发的世界。 USB骨架程序(usb-skeleton),是USB驱动程序的

landishu的专栏 748

linux驱动移植-usb键盘接口驱动

在前面的章节我们已经介绍了usb鼠标驱动的编写,并对usb摄像头驱动源码进行了分析。由于usb键盘驱动和usb鼠标驱动代码非常相似,所以这一节就粗略介绍一下usb键盘驱动的编写。 一、接收usb键盘数据准备工作 1.1 键盘数据格式 键盘发送给usb主机控制器的数据格式包含8个字节,BYTE0、BYTE1、BYTE2、BYTE3,BYTE4、BYTE5、BYTE6、BYTE7,定义分别是: ...

Graceful_scenery的博客 318
上一篇: 我所知道的矩阵键盘工作原理
下一篇: 大家来讨论怎么写概要设计
sunwill_chen
博客等级 码龄19年 25粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值