参考博客:基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read、write

https://blog.csdn.net/u014281970/article/details/82145664

/*ad9833.c*/
/** AD9833 of ADI driver code for Beagleboneblack  debian9.5  kernel 4.14.79** Copyright (C) 2018 Wei Haochen   2019/1/21 modify by wangsong** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* MULTIBEANS, NPU Youyi West Ave, Beilin District, Xi'an, China.*/#include <linux/module.h>   /* Every Linux kernel module must include this head */
#include <linux/init.h>     /* Every Linux kernel module must include this head */
#include <linux/kernel.h>   /* printk() */
#include <linux/fs.h>       /* struct fops */
#include <linux/errno.h>    /* error codes */
#include <linux/cdev.h>     /* cdev_alloc()  */
#include <linux/ioport.h>   /* request_mem_region() */
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/types.h>#include <linux/miscdevice.h>   //必须有#include <asm/uaccess.h>
#include <asm/div64.h>#include <asm/atomic.h>
#include <asm/unistd.h>
#include <asm/io.h>#define                DRV_AUTHOR                  "Wei haochen Wangsong"
#define             DRV_DESC                    "AD9833 on AM335X beaglebone"
#define             DRV_NAME                    "AD9833-ADI"  //设备名称#define               AD9833_SIZE                 0x1000
#define             MEM_CLEAR                   0x1
#define             AD9833_MAJOR                230         //主设备号
#define             AD9833_REG_RESET            0x0100      //根据AD9833寄存器设定的重启
#define             AD9833_FREQ0_REG            0
#define             AD9833_FREQ1_REG            1//AD9833_MAGIC ioctl命令需要的参数,描述了ioctl命令的类型,8位。每种设备或系统都可以指定自己的一个类型号
//ioctl用这个类型来表示ioctl命令所属的设备或驱动,一般用ASCII码字符来表示,如‘a’/.
#define             AD9833_MAGIC                'k'
#define             CMD_PARA_FREQ               0x10
#define             CMD_PARA_PHASE              0x11
#define             CMD_PARA_TYPE               0x12//_IO(type,nr):无数据传输。幻数(type)\序号(nr)
#define             CMD_TYPE_SIN                _IO( AD9833_MAGIC, 0)   //命令选择正弦波
#define             CMD_TYPE_TRI                _IO( AD9833_MAGIC, 1)   //命令选择三角波
#define             CMD_TYPE_SQE                _IO( AD9833_MAGIC, 2)   //命令选择方波#define             CMD_FREQ_SET(X)             _IO( CMD_PARA_FREQ, X)
#define             CMD_PHASE_SET(X)            _IO( CMD_PARA_PHASE, X )
#define             CMD_TYPE_SET(X)             _IO( CMD_PARA_TYPE,X )#define               IO_HIGH                     1
#define             IO_LOW                      0//对应的beaglebone这个板子的GPIO
#define             AD9833_FSY_IO               30   //P9header 11
#define             AD9833_CLK_IO               48   //P9header 15
#define             AD9833_DAT_IO               49   //P9header 23//下面三个函数都仅仅在模拟SPI时序时候使用
#define             io_clk(x)                   gpio_set_value( AD9833_CLK_IO,x )
#define             io_fsy(x)                   gpio_set_value( AD9833_FSY_IO,x )
#define             io_dat(x)                   gpio_set_value( AD9833_DAT_IO,x )typedef    struct ad9833_t AD9833;enum ad9833_wavetype_t{SIN,SQU,TRI
};struct ad9833_hw_t {unsigned int clk;unsigned int sdi;unsigned int fsy;
};struct ad9833_t {struct ad9833_hw_t hw;struct ad9833_t *self;enum ad9833_wavetype_t wave_type;struct  cdev    cdev;unsigned char  mem[ AD9833_SIZE ];unsigned int delay;void (*write_reg) ( AD9833 *self, unsigned int reg_value);void (*init_device) ( AD9833 *self );void (*set_wave_freq)( AD9833 *self , unsigned long freqs_data);void (*set_wave_type)( AD9833 *self, enum ad9833_wavetype_t wave_type );void (*set_wave_phase)( AD9833 *self, unsigned int phase );void (*set_wave_para)( AD9833 *self, unsigned long freqs_data, unsigned int phase, enum ad9833_wavetype_t wave_type );
};static void ad9833_set_wave_type( AD9833 *dev, enum ad9833_wavetype_t wave_type );
static void ad9833_set_phase( AD9833 *dev, unsigned int phase_value );
static void ad9833_set_freq( AD9833 *dev, unsigned long freq );
static void ad9833_set_para( AD9833 *dev, unsigned long freqs_value, unsigned int phase_value, enum ad9833_wavetype_t wave_type );
static void ad9833_init_device( AD9833 *dev ) ;
static void ad9833_write_reg( AD9833 *dev, unsigned int reg_value );
static long ad9833_ioctl(struct file  *file, unsigned int cmd, unsigned long arg );AD9833 *ad9833;
static int  ad9833_major    =  AD9833_MAJOR;   //主设备号module_param( ad9833_major, int, S_IRUGO );static const short ad9833_gpios[] = {AD9833_FSY_IO,AD9833_CLK_IO,AD9833_DAT_IO,
};AD9833 *ad9833_dev_new(void)
{AD9833 *dev = (AD9833*)kcalloc(1, sizeof(AD9833), GFP_ATOMIC);dev->hw.fsy            =      AD9833_FSY_IO;dev->hw.sdi            =   AD9833_DAT_IO;dev->hw.clk           =      AD9833_CLK_IO;dev->set_wave_para    =   &ad9833_set_para;dev->init_device      =   &ad9833_init_device;dev->write_reg        =   &ad9833_write_reg;dev->set_wave_freq    =   &ad9833_set_freq;dev->set_wave_phase    =   &ad9833_set_phase;dev->set_wave_type    =   &ad9833_set_wave_type;dev->init_device( dev );return dev;
}static long ad9833_ioctl(struct file  *file, unsigned int cmd, unsigned long arg)
{printk(DRV_NAME "\tRecv cmd: %u\n", cmd);printk(DRV_NAME "\tRecv arg: %lu\n", arg);switch( cmd ) {case CMD_TYPE_SIN:ad9833->set_wave_freq(ad9833, 1500);ad9833->set_wave_type(ad9833, SIN);printk( DRV_NAME " set wave is sine wave! arg = %lu\n" , arg );break;case CMD_TYPE_TRI:ad9833->set_wave_freq(ad9833, 1500);ad9833->set_wave_type(ad9833, TRI);printk( DRV_NAME " set wave is tri wave! arg = %lu\n" , arg );break;case CMD_TYPE_SQE:ad9833->set_wave_freq(ad9833, 1500);ad9833->set_wave_type(ad9833, SQU);printk( DRV_NAME " set wave is sw wave! arg = %lu\n" , arg );break;}return    0;
}//写入数据,模拟的3线SPI写入时序
static void ad9833_write_reg( AD9833 *dev, unsigned int reg_value )
{unsigned short i;io_clk(IO_HIGH);  //需要修改为为beaglebone的设置方式io_fsy(IO_HIGH);ndelay(10);io_fsy(IO_LOW);for ( i = 0; i < 16; i++ ) {if ( reg_value & 0x8000 )io_dat(IO_HIGH);elseio_dat(IO_LOW);ndelay(10);io_clk(IO_LOW);ndelay(10);reg_value = reg_value << 1;ndelay(10);io_clk(IO_HIGH);}io_fsy(IO_HIGH);io_dat(IO_HIGH);
}//ad9833初始化
static void ad9833_init_device( AD9833 *dev )
{dev->write_reg( dev, AD9833_REG_RESET );dev->set_wave_para( dev,1500, 0 ,SIN );
}//设置相位
static void ad9833_set_para( AD9833 *dev, unsigned long freqs_value, unsigned int phase_value, enum ad9833_wavetype_t wave_type )
{unsigned long dds_frequence_data;unsigned int dds_frequence_low;unsigned int dds_frequence_high;unsigned int phase_data;phase_data      =   phase_value | 0xC000;dds_frequence_data      =   freqs_value * 10;dds_frequence_low       =   dds_frequence_data & 0x3FFF;dds_frequence_low       |=  0x4000;dds_frequence_data      =   dds_frequence_data >> 14;dds_frequence_high      =   dds_frequence_data & 0x3FFF;dds_frequence_high      |=  0x4000;//   reset devicedev->write_reg( dev, 0x0110 );dev->write_reg( dev, 0x2100 );dev->write_reg( dev,dds_frequence_low );dev->write_reg( dev,dds_frequence_high );dev->write_reg( dev, phase_data );if( wave_type == TRI ) {dev->write_reg( dev, 0x2002 );}else if( wave_type == SQU ) {dev->write_reg(  dev, 0x2028);}else {dev->write_reg( dev, 0x2000 );}
}//设置频率,寄存器28位,分两次写入
static void ad9833_set_freq( AD9833 *dev, unsigned long freq )
{unsigned long dds_frequence_data;unsigned long dds_frequence_low;unsigned long dds_frequence_high;dds_frequence_data      =   freq;dds_frequence_low       =   dds_frequence_data & 0x3FFF;dds_frequence_low       |=  0x4000;dds_frequence_data      =   dds_frequence_data >> 14;dds_frequence_high      =   dds_frequence_data & 0x3FFF;dds_frequence_high      |=  0x4000;dev->write_reg( dev, dds_frequence_low );dev->write_reg( dev, dds_frequence_high );}static void
ad9833_set_phase( AD9833 *dev, unsigned int phase_value )
{unsigned int phase_temp;phase_temp = phase_value | 0xC000;dev->write_reg( dev, phase_temp );
}//选择产生波的类型:TRI三角波;SQU方波;SIN正弦波
static void ad9833_set_wave_type( AD9833 *dev, enum ad9833_wavetype_t wave_type )
{if( wave_type == TRI ) {dev->write_reg( dev, 0x2002 );}else if( wave_type == SQU ) {dev->write_reg(  dev, 0x2028);}else {dev->write_reg( dev, 0x2000 );}
}static ssize_t ad9833_driver_read( struct file *filp, char __user *buffer, size_t size, loff_t *f_pos )
{unsigned long  p       =  *f_pos;unsigned int     count   =  size;int            ret     =  0;if ( p >= AD9833_SIZE )       //p为读的位置相对稳健开头的偏移return 0;if ( count > AD9833_SIZE - p )count = AD9833_SIZE - p;if ( raw_copy_to_user( buffer, ad9833->mem + p, count) ) { //从内核空间拷贝数据到用户目录,buffer:目标地址: ad9833->mem + p;count:拷贝的字节数//copy_to_user()成功返回0;失败返回1.ret  =  -EFAULT;}else {*f_pos += count;ret =     count;printk( DRV_NAME "\tread %u bytes from %lu\n", count, p );}return ret;
}//从用户目录往内核空间写入数据
static ssize_t ad9833_driver_write( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )
{unsigned long  p       =  *f_pos;unsigned int     count   =  size;int            ret     =  0;char * endp="str"; if ( p >= AD9833_SIZE )return 0;if ( count > AD9833_SIZE - p )count = AD9833_SIZE - p;memset( ad9833->mem,0, AD9833_SIZE ); //用于对内存空间的初始化if ( raw_copy_from_user( ad9833->mem + p, buffer, count) ) {ret =  -EFAULT;}else {*f_pos += count;ret =     count;printk( DRV_NAME "\twrite %u bytes from %lu\n", count, p );printk( DRV_NAME "\tRecv: %s \n", ad9833->mem + p );printk( DRV_NAME "\tSet freq is: %ld \n", simple_strtol(ad9833->mem + p,&endp,0));//将一个字符串转换成unsigend long long型数据ad9833->set_wave_freq(ad9833, simple_strtol(ad9833->mem + p,&endp,0) );}return ret;
}
//用户进程利用在对设备文件进行诸如read/write操作的时候,系统调用通过设备文件的主设备号找
//到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是
//Linux的设备驱动程序工作的基本原理。
static struct file_operations ad9833_fops = {.owner                =  THIS_MODULE,.read               =      ad9833_driver_read,.write               =  ad9833_driver_write,.unlocked_ioctl     =      ad9833_ioctl,
};static struct miscdevice ad9833_miscdev  = {.name                =  DRV_NAME,.fops              =  &ad9833_fops,
};dev_t devno;
static int __init ad9833_dev_init( void )   //入口函数
{int  i,ret;int  index_minor = 0;      //次设备号int mk_major;/** cdev alloc and release device code.* *///返回值:成功执行返回dev_t类型的设备编号//ad9833_major:主设备号devno = MKDEV( ad9833_major, index_minor );        //获取设备在设备表中的位置mk_major  =  MKDEV(ad9833_major, 0);if( ad9833_major ) { //主设备:devno:要分配的设备编号范围的初始值(次设备号常设为0); //1:连续编号范围. //DRV_NAME:编号相关联的设备名称. ret = register_chrdev_region( devno, 1, DRV_NAME );  //注册模块}else {     //如果没有设备号,则动态申请一个设备号//alloc_chrdev_region() 函数用于动态申请设备编号范围ret = alloc_chrdev_region( &devno, 0, 1, DRV_NAME );ad9833_major  =  MAJOR(devno);}if( ret < 0 ) {printk(DRV_NAME "\t cdev alloc space failed.\n");return ret;}/** AD9833 new device* */printk( DRV_NAME "\tApply memory for AD9833.\n" );ad9833 = ad9833_dev_new();if( !ad9833 ) {ret = -ENOMEM;printk(DRV_NAME "\tad9833 new device failed!\n" );goto fail_malloc;}/** AD9833 init gpios.* */printk( DRV_NAME "\tInititial GPIO\n" );for ( i = 0; i < 3; i ++ ) {//ad9833_gpios[i]:则为你要申请的哪一个管脚;"AD9833 GPIO":为其取一个名字ret  =  gpio_request( ad9833_gpios[i], "AD9833 GPIO" );   //GPIO申请注册//返回值为0表示申请成功if( ret ) {printk("\t%s: request gpio %d for AD9833 failed, ret = %d\n", DRV_NAME,ad9833_gpios[i],ret);return ret;}else {printk("\t%s: request gpio %d for AD9833 set succussful, ret = %d\n", DRV_NAME,ad9833_gpios[i],ret);}gpio_direction_output( ad9833_gpios[i],1 );gpio_set_value( ad9833_gpios[i],0 );}/** cdev init.* */cdev_init( &ad9833->cdev, &ad9833_fops );//静态内存定义初始化ad9833->cdev.owner   =  THIS_MODULE;ret = cdev_add( &ad9833->cdev, mk_major,1 );if( ret ) {printk( KERN_NOTICE "Error %d adding ad9833 %d", ret, 1 );return ret;}ret = misc_register( &ad9833_miscdev );printk( DRV_NAME "\tinitialized\n" );return 0;fail_malloc:unregister_chrdev_region( mk_major,1 );return ret;}static void __exit ad9833_dev_exit( void )        //出口函数
{int i;for( i = 0; i < 3; i++) {gpio_free( ad9833_gpios[i] );}misc_deregister( &ad9833_miscdev );unregister_chrdev_region( devno,1 );}module_init( ad9833_dev_init );
module_exit( ad9833_dev_exit );MODULE_AUTHOR( DRV_AUTHOR );
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");

编译模块的Makefile:

#Makefile for ad9833.c
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
ifneq ($(KERNELRELEASE),)obj-m := ad9833.o
else KERNELDIR ?= /home/ws/4.14/linux-4.14/PWD := $(shell pwd)
default:make -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(PWD) cleanrm -rf modules.orderendif

测试用的源码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>#define                AD9833_MAGIC                'k'
#define             CMD_TYPE_SIN                _IO( AD9833_MAGIC, 0)
#define             CMD_TYPE_TRI                _IO( AD9833_MAGIC, 1)
#define             CMD_TYPE_SQE                _IO( AD9833_MAGIC, 2)const char dev_path[]="/dev/AD9833-ADI";int main(int argc , char *argv[])
{int fd = -1, i = 0;printf("ad9833 test program run....\n");fd = open(dev_path, O_RDWR|O_NDELAY);  // 打开设备if (fd < 0) {printf("Can't open /dev/AD9833-ADI\n");return -1;}printf("open device.\n");if( strcmp(argv[1],"1") == 0 ) {ioctl(fd, CMD_TYPE_SIN, 5);printf("argc = %d,sine wave = %s \n", CMD_TYPE_SIN, argv[1]);}else if(  strcmp(argv[1],"2") == 0 ) {ioctl(fd, CMD_TYPE_TRI, 1);printf("argc = %d,tri wave = %s \n", CMD_TYPE_TRI,argv[1]);}else{ioctl(fd, CMD_TYPE_SQE, 1);printf("argc = %d,sqe wave = %s \n", CMD_TYPE_SQE, argv[1]);}write(fd, argv[2], strlen(argv[2]));printf("argc = %d\n", argc);close(fd);return 0;}

编译测试代码的Makefile:

CROSS=arm-linux-gnueabihf-
all: ad9833_test
ad9833_test: ad9833_test.c$(CROSS)gcc -o ad9833_test.o ad9833_test.c -static
clean:@rm -rf ad9833_test *.o

在调试原作者源码时遇到的问题:

1、 error: implicit declaration of function ‘copy_to_user’; did you mean‘raw_copy_to_user’? [-Werror=implicit-function-declaration]
  if ( copy_to_user( buffer, ad9833->mem + p, count) ) {
       ^~~~~~~~~~~~
       raw_copy_to_user
原因:linux4.14的内核取消了copy_to_user,改为了raw_copy_to_user

2、/home/ws/beaglebone_ad9833/driver/ad9833.c:149:9: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
 AD9833 *ad9833_dev_new()
         ^~~~~~~~~~~~~~
原因:函数中传参为空的时候使用void,修改源码*ad9833_dev_new(void)

3、/home/ws/beaglebone_ad9833/driver/ad9833.c:310:25: warning: passing argument 1 of ‘raw_copy_to_user’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  if ( raw_copy_to_user( buffer, ad9833->mem + p, count) ) {

原因:传参的第一个函数将不是const定义
修改:
static ssize_t ad9833_driver_read( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

static ssize_t ad9833_driver_read( struct file *filp, char __user *buffer, size_t size, loff_t *f_pos )

4、/home/ws/beaglebone_ad9833/driver/ad9833.c:345:74: error: passing argument 2 of ‘simple_strtol’ from incompatible pointer type [-Werror=incompatible-pointer-types]
   printk( DRV_NAME "\tSet freq is: %d \n", simple_strtol(ad9833->mem + p,"str",0));
In file included from ./include/linux/list.h:9:0,
                 from ./include/linux/module.h:9,
                 from /home/ws/beaglebone_ad9833/driver/ad9833.c:21:
./include/linux/kernel.h:438:13: note: expected ‘char **’ but argument is of type ‘char *’
 extern long simple_strtol(const char *,char **,unsigned int);
原因:
        printk( DRV_NAME "\tSet freq is: %d \n", simple_strtol(ad9833->mem + p,"str",0));
        ad9833->set_wave_freq(ad9833, simple_strtol(ad9833->mem + p,"str",0) );

修改为:
        char * endp="str";

printk( DRV_NAME "\tSet freq is: %d \n", simple_strtol(ad9833->mem + p,&endp,0));
        ad9833->set_wave_freq(ad9833, simple_strtol(ad9833->mem + p,&endp,0) );

5、/home/ws/beaglebone_ad9833/driver/ad9833.c:363:15: error: variable ‘ad9833_miscdev’ has initializer but incomplete type
 static struct miscdevice ad9833_miscdev  = {
原因:缺少头文件

#include <linux/miscdevice.h>

6、/home/ws/beaglebone_ad9833/driver/ad9833.c:362:25: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
   .unlocked_ioctl   =   ad9833_ioctl,
                         ^~~~~~~~~~~~
原因:内核版本变迁,现在ioctl函数的返回值应该定义为long型。
修改为:
static long ad9833_ioctl(struct file  *file, unsigned int cmd, unsigned long arg)

测试步骤说明:

  1. 将ad9833.c的源码和Makefile拷贝到一个文件夹下,在PC端的终端进入文件夹下,执行make命令,则可以编译成模块,其中Makefile中需要修改依赖的linux编译过得内核路径,也许交叉编译链用的也不一样,也需要修改。
  2. 将编译过的整个文件夹通过TFTP传输到与其相连的Beaglebone中。
  3. 登录到beaglebone上,使用insmod命令挂载模块,lsmod查看挂载状态,rmmod卸载模块。
  4. 接着回到PC端复制测试文件的源码和Makefile,放在一个文件夹下,将文件传输到Beaglebone中,通过串口(minicom)登录到Beaglebone上。在Beaglebone的该文件夹下执行make命令,生成可执行文件。
  5. root权限执行命令。
    sudo ./ad9833 1 2000

    第一个参数表示选择正弦波,第二个参数表示设置频率。

Beagleboneblack 中DDS模块驱动源码(AD9833)相关推荐

  1. linux内核声卡管理,浅析linux 2.6.30.4内核中uda134x声卡驱动源码 - audio和bluetooth

    文件:sound/soc/s3c24xx/s3c24xx_uda134x.c static int __init s3c24xx_uda134x_init(void) { return platfor ...

  2. Mahout中关于MultiLayer Perceptron模块的源码解析

    Mahout中关于MultiLayer Perceptron模块的源码解析 前段时间学习NN时使用到了BPNN,考虑到模型的分布式扩展,我想到使用Mahout的MultiLayer Perceptro ...

  3. Linux kernel SPI源码分析之SPI设备驱动源码分析(linux kernel 5.18)

    SPI基础支持此处不再赘述,直接分析linux中的SPI驱动源码. 1.SPI设备驱动架构图 2.源码分析 本次分析基于kernel5.18,linux/drivers/spi/spidev.c 设备 ...

  4. Python中如何查看模块的源码内容

    ubuntu中查看python模块的源码 1. 打开ipython3,进入交互式解释器 2. 导入模块,以io模块为例,查看io.__file__属性,找到源码文件的路径 3. 使用cat命令查看源码 ...

  5. simulink模块,提供xpctarget下驱动源码

    simulink模块,提供xpctarget下驱动源码 :77999632700099250风中的蜗牛

  6. 字符设备驱动基础篇1——简单的驱动源码分析

    以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除. 参考资料:http://www.cnblogs.com/biaohc/p/6575074.html module_test.c代码 #inc ...

  7. 我的内核学习笔记10:Intel GPIO驱动源码分析

    本文对Intel e3800的GPIO驱动源码进行分析. 一.概述 1.1 内核配置 Intel e3800的GPIO在Linux内核中使用的驱动名为gpio_ich(为了行文方便,将对应的设备称为& ...

  8. [Linux] USB-Storage驱动 源码阅读笔记(一)

    USB-Storage驱动 源码阅读笔记--从USB子系统开始 最近在研究U盘的驱动,遇到很多难以理解的问题,虽然之前也参考过一些很不错的书籍如:<USB那些事>,但最终还是觉得下载一份最 ...

  9. python中selenium模块驱动谷歌详解

    python中selenium模块驱动谷歌详解 Selenium的介绍.配置和调用 Selenium(浏览器自动化测试框架) 是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中 ...

  10. 通用USB设备驱动源码分析

    通用USB设备驱动源码分析 Author:aaron 前段时间写了篇<qualcomm usb modem驱动小结>的文章, 描述了自己如何为高通的一个usb modem设备写驱动的过程, ...

最新文章

  1. 如何使您的Kotlin Android动画可访问
  2. 用javah 导出类的头文件, 常见的错误及正确的使用方法
  3. Nginx(二)------nginx.conf 配置文件
  4. .NET常用工具类(Utils.cs)
  5. iOS 9之3D Touch
  6. boost::fusion::as_map用法的测试程序
  7. C++学习笔记:类的成员函数的声明与定义
  8. springBoot 登录拦截器
  9. Python自然语言处理------分类和标注词汇
  10. 贫血模式or领域模式(转载)
  11. php之$_SESSION的理解
  12. Swift 复数变量定义
  13. RenderTexture实现小地图和炫酷的传送门!(干货收藏)
  14. mysql identity sqlserver_mysql和sqlserver的区别
  15. 万字长文讲清楚4D毫米波雷达
  16. 【罗塞塔石碑】—My Lover(One.iso)
  17. i++与++i是什么意思?
  18. 第五天 游戏策划学习
  19. python修改app定位_appnium定位+操作方式(python)
  20. 前端设计师常用的一些基础工具素材合集

热门文章

  1. WEB程序设计-个人主页,项目编号600001
  2. 自建同步云盘服务器,自建云盘系列——Syncthing (BT Sync的开源替代)
  3. 关于电子计算机的热点,电脑如何变热点?8款电脑wifi热点软件推荐
  4. wifi配网过程的详细介绍
  5. natApp进行内网穿透-外网访问前端本地运行项目
  6. 家庭收支软件用java写,eclipse编写的Java家庭收支记账软件
  7. 在线计算机进制换算,进制转换计算器
  8. 计算机设置开机背景,电脑开关机背景图片怎么换
  9. 计算机网络技术专业职业能力分析的结论与成效
  10. html ui在线生成器,漂亮的CSS按钮样式集以及在线生成工具