ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程

时间:2021-11-12 18:22:37

ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程

原文地址:http://www.cnblogs.com/NickQ/p/9026545.html

一、开发板与ds18b20的入门

ds18B20是常用的数字温度传感器,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。但楼主在使用过程中发现,ds18b20测量的温度还是需要进行一定的软件校准的。后面我们会谈论到。

除了上面提到的,ds18b20还有很多可圈可点的有点。下面说楼主所关注到的几个。

  1. 单总线协议,称为总线,必然可以挂载很多设备,但却只占用一个IO口。这对于缺乏IO资源的设备来说,就像是救命稻草。
  2. 可以由用户自己权衡测量精度和测量时间。根据Datasheet,18B20控制寄存器有两位是用来控制精度和测量时间的。如下图。

    下图表明:用户可以在精度9bit-12bit中,*切换,这也对应着93.75ms,187.5ms,375ms,750ms四个最大测量时间。也就是说9bit精度意味着,最大测量时间最有93.75ms(对于缓慢变化的温度来说,这已经很快了),但只可以精确到0.25摄氏度。12bit精度意味着,虽然最大测量时间有750ms,但精度却能达到0.0625(750ms对于温度测量不能算很慢,但换来的这个精度却是不低的)。

    ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程
  3. 可以使用寄生电源供电。这也是这个芯片突出的地方。这意味着可以不连接电源线,也为PCB布板,多设备走线省去了很多方便。

二、开发板的硬件电路和寄存器

楼主这里使用的是飞凌2440开发板,做学习之用。

电路连接图如下

ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程

这个板子上是接了电源和外部上拉。事实上这个电源可以由寄生电源,即由信号线DQ上的外部上拉提供。

图中,也可以看出信号线DQ是连接在了GPG0口。

寄存器说明图

寄存器物理地址:

ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程

寄存器配置说明

GPGCON-GPG0

ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程

GPGDAT AND GPGUP

ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程

三、 驱动实现

#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 <asm/io.h> /* ioremap() */
#include <linux/ioport.h> /* request_mem_region() */
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <linux/device.h> //定义驱动模块信息
//模块作者和描述
#define DRV_AUTHOR "Nick <nickxq@qq.com>"
#define DRV_DESC "S3C24XX 18B20 driver" //模块名
#define DEV_NAME "s3c18b20" //模块版本信息(只作用于安装和卸载的打印信息中)
#define DRV_MAJOR_VER 1
#define DRV_MINOR_VER 0
#define DRV_REVER_VER 0 //定义GPG口的寄存器地址,注意偏移地址在程序中的使用
#define S3C_GPG_BASE 0x56000060 //寄存器物理地址基地址
#define GPGCON_OFFSET 0
#define GPGDAT_OFFSET 4
#define GPGUP_OFFSET 8
#define S3C_GPG_LEN 0x10 /* 0x56000060~0x56000070 */ //此处定义的大小包括了四个寄存器地址空间,即4*4字节(包括保留的寄存器地址) //定义ds18b20 DQ线对应的端口的GPIO编号。例如:GPG0 GPIO编号为0
#define GPIO_NUM_18B20 0 //18B20 PORT is PG0 //定义函数操作的参数
//GPIO_Mode
#define GPIO_MODE_INPUT 0x00
#define GPIO_MODE_OUTPUT 0x01
#define GPIO_MODE_EINT 0x10 //GPIO_STATUS
#define GPIO_STATUS_LOW 0
#define GPIO_STATUS_HIGH 1 //GPIO_PULLUP
#define GPIO_PULLUP_ENABLE 0
#define GPIO_PULLUP_DISABLE 1 #define DISABLE 0
#define ENABLE 1 //定义函数宏
#define s3c_gpio_read(reg) __raw_readl((reg)+s3c_gpg_membase)
#define s3c_gpio_write(val,reg) __raw_writel((val),(reg)+s3c_gpg_membase) //设置GPIO模式 参数:操作寄存器的偏移地址、GPIO编号、设置的状态(取值应为上述的宏)
#define s3c_18b20_gpio_mode(gpio_mode) s3c2440_gpio_cfgpin_mode(GPGCON_OFFSET,GPIO_NUM_18B20, gpio_mode)
#define s3c_18b20_gpio_setsta(gpio_status) s3c2440_gpio_cfgpin_status(GPGDAT_OFFSET,GPIO_NUM_18B20, gpio_status)
#define s3c_18b20_gpio_getsta() s3c2440_gpio_getpin_status(GPGDAT_OFFSET,GPIO_NUM_18B20)
#define s3c_18b20_gpio_pullup(gpio_pullup) s3c2440_gpio_cfgpin_pullup(GPGUP_OFFSET,GPIO_NUM_18B20, gpio_pullup) //全局变量的定义
//设备数量、主设备好、次设备号(此处主设备号可由静态给定,只要不为0即可。若主设备号为0,则动态申请)
int dev_count = 1;
int dev_major = 0;
int dev_minor = 0; int debug = DISABLE; //定义存储 映射的虚拟地址空间地址的起始地址 变量
static void __iomem *s3c_gpg_membase;
static struct cdev *s3c_18b20_cdev; //设置GPIO模式,此函数由上述的带参数宏调用
static int s3c2440_gpio_cfgpin_mode(unsigned long gpio_addr,unsigned char gpio_num, unsigned char gpio_mode)
{
volatile unsigned long gpg_con; if((GPIO_MODE_INPUT != gpio_mode) && (GPIO_MODE_OUTPUT != gpio_mode) && (GPIO_MODE_EINT != gpio_mode))
{
return -1;
} /* Set GPxCON register, set correspond GPIO port as input or output mode */
gpg_con = s3c_gpio_read(gpio_addr); //此处的gpio_addr是偏移地址,调用此宏后会根据s3c_gpg_membase转换为绝对虚拟地址
gpg_con &= ~(0x3<<(2*gpio_num)); /* Clear the currespond GPIO configure register */
gpg_con |= gpio_mode<<(2*gpio_num); /* Set the currespond GPIO as output mode */ //带参数宏,实现将gpgdat写入gpio_addr。
s3c_gpio_write(gpg_con,gpio_addr); //此处的gpio_addr是偏移地址,调用此宏后会根据s3c_gpg_membase转换为绝对虚拟地址 return 0;
} //设置GPIO引脚电平状态,此函数由上述的带参数宏调用
static int s3c2440_gpio_cfgpin_status(unsigned long gpio_addr,unsigned char gpio_num, unsigned char gpio_status)
{
volatile unsigned long gpg_dat; if((GPIO_STATUS_LOW != gpio_status) && (GPIO_STATUS_HIGH != gpio_status))
{
return -1;
} /* Set GPxDAT register, set correspond GPIO port power level as high level or low level */
gpg_dat = s3c_gpio_read(gpio_addr); //此处的gpio_addr是偏移地址,调用此宏后会根据s3c_gpg_membase转换为绝对虚拟地址 if(GPIO_STATUS_LOW == gpio_status)
{
gpg_dat &= ~(0x1<<gpio_num); /* This port set to low level */
}
else
{
gpg_dat |= (0x1<<gpio_num); /* This port set to high level*/
} //带参数宏,实现将gpgdat写入gpio_addr。
s3c_gpio_write(gpg_dat,gpio_addr); //此处的gpio_addr是偏移地址,调用此宏后会根据s3c_gpg_membase转换为绝对虚拟地址 return 0;
} //设置GPIO上拉状态,此函数由上述的带参数宏调用
static int s3c2440_gpio_cfgpin_pullup(unsigned long gpio_addr,unsigned char gpio_num, unsigned char gpio_pullup)
{
volatile unsigned long gpg_up; if((GPIO_PULLUP_ENABLE != gpio_pullup) && (GPIO_PULLUP_DISABLE != gpio_pullup))
{
return -1;
} /* Set GPxUP register, set correspond GPIO port pull up resister as enable or disable */
gpg_up = s3c_gpio_read(gpio_addr); if(GPIO_PULLUP_ENABLE == gpio_pullup)
{
gpg_up &= ~(0x1<<gpio_num); /* Enable pull up resister */
}
else
{
gpg_up |= (0x1<<gpio_num); /* Disable pull up resister */
} s3c_gpio_write(gpg_up,gpio_addr); return 0;
} //读取GPIO引脚电平状态,此函数由上述的带参数宏调用
static int s3c2440_gpio_getpin_status(unsigned long gpio_addr,unsigned char gpio_num)
{
volatile unsigned long gpg_dat; /* Get GPxDAT register, get correspond GPIO port power level as high level or low level */
gpg_dat = s3c_gpio_read(gpio_addr); gpg_dat &= (0x1<<gpio_num); if(gpg_dat)
{
return GPIO_STATUS_HIGH;
}
else
{
return GPIO_STATUS_LOW;
}
} //向内核申请4*4个字节的虚拟地址空间,并与寄存器物理地址绑定映射
static int s3c_18b20_addr_init(void)
{ if(!request_mem_region(S3C_GPG_BASE, S3C_GPG_LEN, "s3c2440 18b20"))
{
return -EBUSY;
} if( !(s3c_gpg_membase=ioremap(S3C_GPG_BASE, S3C_GPG_LEN)) )
{
release_mem_region(S3C_GPG_BASE, S3C_GPG_LEN);
return -ENOMEM;
} return 0;
} //释放虚拟地址,解除地址映射
static void s3c_18b20_addr_release(void)
{
release_mem_region(S3C_GPG_BASE, S3C_GPG_LEN); iounmap(s3c_gpg_membase);
} //写ds18b20复位时序
static int s3c_ds18b20_clk_reset(void)
{
int retval = 0; s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);
s3c_18b20_gpio_pullup(GPIO_PULLUP_ENABLE); s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH);
udelay(2);
s3c_18b20_gpio_setsta(GPIO_STATUS_LOW); // 拉低ds18b20总线,复位ds18b20
udelay(500); // 保持复位电平500us s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH); // 释放ds18b20总线
udelay(60); // 若复位成功,ds18b20发出存在脉冲(低电平,持续60~240us)
s3c_18b20_gpio_mode(GPIO_MODE_INPUT);
retval = s3c_18b20_gpio_getsta(); udelay(500);
s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);
s3c_18b20_gpio_pullup(GPIO_PULLUP_ENABLE);
s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH); // 释放总线 return retval;
} //ds18b20写数据时序
static void s3c_ds18b20_clk_write_byte(unsigned char data)
{
int i = 0,flag = 0; s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);
s3c_18b20_gpio_pullup(GPIO_PULLUP_DISABLE); for (i = 0; i < 8; i++)
{
// 总线从高拉至低电平时,就产生写时隙
s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH);
udelay(2);
s3c_18b20_gpio_setsta(GPIO_STATUS_LOW);
flag = data & 0x01;
if(flag)
{
s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH);
}
else
{
s3c_18b20_gpio_setsta(GPIO_STATUS_LOW);
}
udelay(60);
data >>= 1;
}
s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH); // 重新释放ds18b20总线
} //ds18b20读数据时序
static unsigned char s3c_ds18b20_clk_read_byte(void)
{
int i;
unsigned char data = 0; for (i = 0; i < 8; i++)
{
// 总线从高拉至低,只需维持低电平17ts,再把总线拉高,就产生读时隙
s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);
s3c_18b20_gpio_pullup(GPIO_PULLUP_ENABLE);
s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH);
udelay(2);
s3c_18b20_gpio_setsta(GPIO_STATUS_LOW);
udelay(2);
s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH);
udelay(8);
data >>= 1;
s3c_18b20_gpio_mode(GPIO_MODE_INPUT);
if (s3c_18b20_gpio_getsta())
data |= 0x80;
udelay(50);
}
s3c_18b20_gpio_mode(GPIO_MODE_OUTPUT);
s3c_18b20_gpio_pullup(GPIO_PULLUP_ENABLE);
s3c_18b20_gpio_setsta(GPIO_STATUS_HIGH); // 释放ds18b20总线
return data;
} //内核的调用接口
static int s3c_18b20_open(struct inode *inode, struct file *file)
{
int flag = 0; printk(KERN_ERR "open start\n"); flag = s3c_ds18b20_clk_reset(); printk(KERN_ERR "ds18b20 reset is %d\n",flag); if (flag & 0x01)
{
printk(KERN_WARNING "open ds18b20 failed\n");
return -1;
}
printk(KERN_NOTICE "open ds18b20 successful\n");
return 0;
} //内核的调用接口
static int s3c_18b20_release(struct inode *inode, struct file *file)
{
printk(KERN_DEBUG "/dev/s3c_18b20%d closed.\n", iminor(inode)); return 0;
} //内核的调用接口
static ssize_t s3c_18b20_read(struct file *filp, char __user * buf, size_t count, loff_t * f_pos)
{
int flag;
unsigned long err;
unsigned char result[2] = { 0x00, 0x00 }; flag = s3c_ds18b20_clk_reset(); if (flag & 0x01)
{
printk(KERN_WARNING "ds18b20 init failed\n");
return -1;
} s3c_ds18b20_clk_write_byte(0xcc);
s3c_ds18b20_clk_write_byte(0x44); flag = s3c_ds18b20_clk_reset();
if (flag & 0x01)
return -1; s3c_ds18b20_clk_write_byte(0xcc);
s3c_ds18b20_clk_write_byte(0xbe); result[0] = s3c_ds18b20_clk_read_byte(); // 温度低八位
result[1] = s3c_ds18b20_clk_read_byte(); // 温度高八位 err = copy_to_user(buf, &result, sizeof(result));
return err ? -EFAULT : min(sizeof(result), count);
} //定义的ds18b20文件操作的数据结构
static struct file_operations s3c_18b20_fops =
{
.owner = THIS_MODULE,
.open = s3c_18b20_open,
.read = s3c_18b20_read,
.release = s3c_18b20_release,
}; //模块安装调用的初始化
static int __init s3c_18b20_init(void)
{
int result;
dev_t devno; //申请并映射虚拟地址
if( 0 != s3c_18b20_addr_init() )
{
printk(KERN_ERR "s3c2440 18B20 addr initialize failure.\n");
return -ENODEV;
} //为设备注册设备号。如果dev_major不为0,则动态申请主设备号。否者使用dev_major为主设备号
if (0 != dev_major) /* Static */
{
devno = MKDEV(dev_major, dev_minor);
result = register_chrdev_region(devno, dev_count, DEV_NAME);
}
else
{
result = alloc_chrdev_region(&devno, dev_minor, dev_count, DEV_NAME);
dev_major = MAJOR(devno);
} /* Alloc for device major failure */
if (result < 0)
{
printk(KERN_ERR "S3C %s driver can't use major %d\n", DEV_NAME, dev_major);
return -ENODEV;
} printk(KERN_DEBUG "S3C %s driver use major %d\n", DEV_NAME, dev_major); //为s3c_18b20_cdev数据结构申请空间
if(NULL == (s3c_18b20_cdev=cdev_alloc()) )
{
printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
unregister_chrdev_region(devno, dev_count);
return -ENOMEM;
} //绑定字符设备数据结构
s3c_18b20_cdev->owner = THIS_MODULE;
cdev_init(s3c_18b20_cdev, &s3c_18b20_fops); //注册cdev到内核
result = cdev_add(s3c_18b20_cdev, devno, dev_count); if (0 != result)
{
printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result);
goto ERROR;
} printk(KERN_ERR "S3C %s driver[major=%d] version %d.%d.%d installed successfully!\n",
DEV_NAME, dev_major, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);
return 0; ERROR:
printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME);
cdev_del(s3c_18b20_cdev);
unregister_chrdev_region(devno, dev_count);
return result;
} static void __exit s3c_18b20_exit(void)
{
dev_t devno = MKDEV(dev_major, dev_minor); s3c_18b20_addr_release(); cdev_del(s3c_18b20_cdev);
unregister_chrdev_region(devno, dev_count); printk(KERN_ERR "S3C %s driver version %d.%d.%d removed!\n",
DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER); return ;
} /* These two functions defined in <linux/init.h> */
module_init(s3c_18b20_init);
module_exit(s3c_18b20_exit); module_param(debug, int, S_IRUGO);
module_param(dev_major, int, S_IRUGO); MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");

四、编译的Makefile

LINUX_SRC = ${shell pwd}/../kernel/linux-3.0
CROSS_COMPILE=/opt/xtools/arm920t/bin/arm-linux-
INST_PATH=${shell pwd}/
PWD := $(shell pwd)
EXTRA_CFLAGS+=-DMODULE
obj-m += kernel_18b20.o modules:
@make -C $(LINUX_SRC) M=$(PWD) modules
@make clear uninstall:
rm -f ${INST_PATH}/*.ko install: uninstall
cp -af *.ko ${INST_PATH} clear:
@rm -f *.o *.cmd *.mod.c
@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
@rm -f .*ko.cmd .*.o.cmd .*.o.d clean: clear
@rm -f *.ko

解释说明:

LINUX_SRC 指定开发板已编译过得内核路径

CROSS_COMPILE 指定交叉编译器

INST_PATH 安装路径

obj-m += kernel_18b20.o 编译成模块,输出文件名为kernel_18b20.o

编译运行。

修改驱动文件名为kernel_18b20.c

使用make编译后,将kernel_18b20.ko传输至开发板。

使用insmod安装。可以使用dmesg查看安装打印信息,也可以使用lsmod查看设备信息

[root@NickQ_fl2440 driver]# insmod kernel_18b20.ko
[root@NickQ_fl2440 driver]# dmesg
S3C s3c18b20 driver use major 253
S3C s3c18b20 driver[major=253] version 1.0.0 installed successfully!
[root@NickQ_fl2440 driver]# lsmod
kernel_18b20 3250 0 - Live 0xbf000000

然后查看主设备号cat /proc/devices,使用moknod创建设备节点

[root@NickQ_fl2440 driver]# cat /proc/devices | grep s3c18b20
253 s3c18b20
[root@NickQ_fl2440 driver]# mknod -m 755 /dev/s3c18b20 c 253 0
[root@NickQ_fl2440 driver]# ls /dev/s3c18b20
/dev/s3c18b20

mknod 用法

[root@NickQ_fl2440 driver]# mknod --help
BusyBox v1.27.1 (2017-11-20 21:14:15 CST) multi-call binary. Usage: mknod [-m MODE] NAME TYPE MAJOR MINOR Create a special file (block, character, or pipe) -m MODE Creation mode (default a=rw)
TYPE:
b Block device
c or u Character device
p Named pipe (MAJOR and MINOR are ignored)

五、编写测试程序

[nick@XQLY driver]$ vim ~/s3c2440/linux/drivers/test_18b20.c 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/ioctl.h> int main()
{
int fd;
unsigned char result[2];
unsigned char integer_value = 0;
float decimal_value = 0.0;
float temperature = 0.0; fd = open("/dev/s3c18b20", 0); if(fd < 0)
{
perror("open device failed\n");
exit(1);
}
else
printf("Open success!\n"); while(1)
{
read(fd, &result, sizeof(result)); integer_value = ((result[0] & 0xf0) >> 4) | ((result[1] & 0x08) << 4); decimal_value = (result[0] & 0x0f) * 0.0625; temperature = (float)integer_value + decimal_value; printf("Current Temperature:%6.4f\n", temperature); sleep(1);
}
}

解析温度的说明:

ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程

我们读取出来的数据放置在result[2]里。

result[0]对应从LS Byte里读回来的值

result[1]对应从MS Byte里读回来的值

所以integer_value = ((result[0] & 0xf0) >> 4) | ((result[1] & 0x08) << 4);是将LS的高四位和MS的低四位取出(其中有一位符号位S),并合成一个数,即为整数部分。decimal_value = (result[0] & 0x0f) * 0.0625;是提取小数部分

六、测试现象

[root@NickQ_fl2440 driver]# ./start_s3c18b20
Open success!
Current Temperature:26.5625
Current Temperature:26.6250
Current Temperature:26.5625
Current Temperature:26.6250
Current Temperature:26.6250
^C

ARM Linux驱动篇 学习温度传感器ds18b20的驱动编写过程的更多相关文章

  1. Linux随笔-鸟哥Linux基础篇学习总结(全)

    Linux随笔-鸟哥Linux基础篇学习总结(全) 修改Linux系统语系:LANG-en_US,如果我们想让系统默认的语系变成英文的话我们可以修改系统配置文件:/etc/sysconfig/i18n ...

  2. Linux随笔-鸟哥Linux服务器篇学习总结(全)

    作者:Danbo 时间:2015-7-17 在runlevel3启动级别下默认启动网络挂载(autofs)机制,我们可以通过命令将其关闭:chkconfig autofs off 或者 /etc/in ...

  3. Linux基础篇学习——Linux文件系统之文件存储与读取:inode,block,superblock

    Linux文件类型 代表符号 含义 - 常规文件,即file d directory,目录文件 b block device,块设备文件,支持以"block"为单位进行随机访问 c ...

  4. Linux基础篇学习——常见系统命令:ls,pwd,cd,date,hwclock,passwd,su,clear,who,w,uname,uptime,last,dmesg,free,ps,top

    ls 显示指定目录中的内容 ls [OPTION]... [FILE]... OPTION -a --all,显示所有文件包括隐藏文件 -l 列出长属性,显示出文件的属性与权限等数据信息 -i  列出 ...

  5. Linux基础篇学习——文件目录常用管理命令mkdir,cat,more,less,ln,file,cp,find,split,mv

    mkdir 创建目录 -p 递归创建目录 -v 显示创建信息 [root@zycentos7 ~]# mkdir -p {mylinux/{bin,conf,lib,logs,webapps/{doc ...

  6. 羽夏看Win系统内核——驱动篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  7. 20165219 预备作业3 Linux安装及学习

    20165219 预备作业3 Linux安装及学习 安装虚拟机 在安装的过程中遇到了不少的问题,在同学的帮助下都得到了解决.比如在新建虚拟机的时候没有64位这个选项,后来知道需要开启虚拟化,然后是安装 ...

  8. 驱动开发学习笔记&period; 0&period;04 linux 2&period;6 platform device register 平台设备注册 1&sol;2 共2篇

    驱动开发读书笔记. 0.04  linux 2.6 platform device register 平台设备注册  1/2 共2篇下面这段摘自 linux源码里面的文档 : Documentatio ...

  9. 驱动开发学习笔记&period; 0&period;05 linux 2&period;6 platform device register 平台设备注册 2&sol;2 共2篇

    驱动开发读书笔记. 0.05 linux 2.6 platform device register 平台设备注册 2/2 共2篇 下面这段摘自 linux源码里面的文档 : 内核版本2.6.22Doc ...

随机推荐

  1. 中国计算机学会CCF推荐国际学术会议

    中国计算机学会推荐国际学术会议 (计算机系统与高性能计算) 一.A类 序号 会议简称 会议全称 出版社 网址 1 ASPLOS Architectural Support for Programmin ...

  2. sql按字符截取字段

    字段A=’F:\photo\Winter Leaves.jpg’ 要求:分段截取每段字符[字段A不能为TEXT类型,否则报错] 解决方法: ---截取字符串A的第一个\左边的字符串 ) 输出结果:F: ...

  3. 【BZOJ-2843&amp&semi;1180】极地旅行社&amp&semi;OTOCI Link-Cut-Tree

    2843: 极地旅行社 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 323  Solved: 218[Submit][Status][Discuss ...

  4. Codeforces Round &num;313 &lpar;Div&period; 1&rpar; C&period; Gerald and Giant Chess

    这场CF又掉分了... 这题题意大概就给一个h*w的棋盘,中间有一些黑格子不能走,问只能向右或者向下走的情况下,从左上到右下有多少种方案. 开个sum数组,sum[i]表示走到第i个黑点但是不经过其他 ...

  5. UE 使用技巧

    一.关于正则表达式的使用 删除空行: 替换 %[ ^t]++^p 为 空串 替换回车换行符:替换^p 为 空串 删除行尾空格: 替换 [ ^t]+$ 为 空串 删除行首空格: 替换 %[ ^t]+ 为 ...

  6. scala 入门(1)

    大数据“火”的有段日子了,原来打算学习hadoop…… 后知道spark要比hadoop更牛, 故而转学spark.其原码为scala所写,为了更好的研究spark,故又开始学习scala. 将自己所 ...

  7. C&num; 多线程及同步简介示例

           60年代,在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了很多弊端,一是由于进程是资源拥有者,创建.撤消与切换存在较大的时空开销,因此需要引入轻型进程: ...

  8. chmod &plus;x 和 chmod u&plus;x的区别

    常用: chmod a+x tomcat u 代表用户. g 代表用户组. o 代表其他. a 代表所有. 这意味着chmod u+x somefile 只授予这个文件的所属者执行的权限 而 chmo ...

  9. TP框架M方法 create方法丢失字段问题

    TP框架M方法 create方法丢失字段问题! thinkphp框架M方法 create方法丢失字段问题! thinkphp框架M方法 add方法字段丢失问题! 数据库 表新增了字段,用create方 ...

  10. 倍福TwinCAT&lpar;贝福Beckhoff&rpar;常见问题&lpar;FAQ&rpar;-为什么没有自动识别成标准FBD功能块

    新建一个项目,是不会自动把FBD对应名称的模块识别成标准功能块的   你需要引入相应的类库重新输入FBD   然后才会自动生成     更多教学视频和资料下载,欢迎关注以下信息: 我的优酷空间: ht ...