本驱动用的开发板是:
飞凌公司的OK2440开发板;
Linux内核版本是:linux2.6.35;
编译器:arm-linux-gcc-4.3.2
一、驱动分析
(1).这里采用混杂设备形式注册驱动,对于设备的操作仅且只有一个:即读操作,读函数为s3c2440_18b20_read(),完成的功能主要是从DS18b20中读取温度值然后从内核中将该温度值传给应用程序使用。
当然,要读取这个温度值,首先要令ds18b20这个芯片完成温度的转换,并将转换的值保存在该芯片的RAM中,占用9位。然后内核把这9个位的数读取过来。接着上传给应用程序;读取的过程是:
由于DS18B20采用独特的单总线接口方式,每只DS18B20都有一个唯一存储在ROM中的64位编码。最前面8位是单线系列编码:28H,接着的48位是一个唯一的序列号,最后8位是以上56位的CRC编码。通过单线总线端口访问DS18B20的协议如下:
①初始化;
②发送ROM操作指令;
③发送DS18B20功能指令。
另外要用的函数还有三个,分别是读一个字节函数,写一个字节函数,复位函数;
(2).复位操作流程
①设总线为输出模式;
②向总线发送一个上升沿,保持高电平100 us;
③向总线发送一个下降沿,保持低电平800 us;
④向总线发送一个上升沿,延时100 us;
⑤设总线为输入模式;
⑥判断总线状态,如果为低电平,则复位成功。
(3).写一个字节操作流程
①设总线为输出模式,并设置8次循环;
②向总线发送一个下降沿,保持低电平;
③判断写入数据是0还是1,如果是1,则向总线发送一个上升沿,保持高电平;如果是0,则保持总线低电平不变;
④延时60 us,设总线为高电平,再延时15 us;
⑤循环操作步骤②~④;
⑥设总线为高电平。
(4).读一个字节操作流程
①设循环次数为8;
②设总线为输出,向总线发送一个下降沿,保持低电平,并延时1 us;
③向总线发送一个上升沿,并设为输入;
④读总线状态,并保存为1位,并延时60us;
⑤循环操作步骤②~④,读取1个字节数据。
二、应用程序分析
应用程序中打开设备文件后,调用系统函数read函数读取温度,并用printf函数打印出这个值。
三、驱动程序和应用程序源码
(1)驱动程序
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/configfs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <plat/regs-timer.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-nrs.h>
#include <mach/map.h>
#include <mach/regs-irq.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <mach/regs-clock.h>
#include <asm/irq.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
typedef unsigned char BYTE;
#define DS18B20_PIN S3C2410_GPG(0)
#define DS18B20_PIN_OUTP S3C2410_GPIO_OUTPUT
#define DS18B20_PIN_INP S3C2410_GPIO_INPUT
#define HIGH 1
#define LOW 0
#define DEVICE_NAME "DS18B20"
static BYTE data[2];
BYTE DS18b20_reset (void)
{
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP); //配置GPG0输出模式
s3c2410_gpio_setpin(DS18B20_PIN, HIGH); // 向18B20发送一个上升沿,并保持高电平状态约100微秒
udelay(100);
s3c2410_gpio_setpin(DS18B20_PIN, LOW); // 向18B20发送一个下降沿,并保持低电平状态约600微秒
udelay(600);
s3c2410_gpio_setpin(DS18B20_PIN, HIGH); // 向18B20发送一个上升沿,此时可释放DS18B20总线
udelay(100);
// 以上动作是给DS18B20一个复位脉冲
// 通过再次配置GPIG0引脚成输入状态,可以检测到DS18B20是否复位成功
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_INP);
if(s3c2410_gpio_getpin(DS18B20_PIN)){ printk("DS18b20 reset failed.\r\n"); return 1;} // 若总线在释放后总线状态为高电平,则复位失败
return 0;
}
void DS18b20_write_byte (BYTE byte)
{
BYTE i;
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP); // 配置GPG0为输出模式
// 写“1”时隙:
// 保持总线在低电平1微秒到15微秒之间
// 然后再保持总线在高电平15微秒到60微秒之间
// 理想状态: 1微秒的低电平然后跳变再保持60微秒的高电平
//
// 写“0”时隙:
// 保持总线在低电平15微秒到60微秒之间
// 然后再保持总线在高电平1微秒到15微秒之间
// 理想状态: 60微秒的低电平然后跳变再保持1微秒的高电平
for (i = 0; i < 8; i++)
{
s3c2410_gpio_setpin(DS18B20_PIN, LOW); udelay(1);
if(byte & HIGH)
{
// 若byte变量的D0位是1,则需向总线上写“1”
// 根据写“1”时隙规则,电平在此处翻转为高
s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
}
else
{
// 若byte变量的D0位是0,则需向总线上写“0”
// 根据写“0”时隙规则,电平在保持为低
s3c2410_gpio_setpin(DS18B20_PIN, LOW);
}
// 电平状态保持60微秒
udelay(60);
s3c2410_gpio_setpin(DS18B20_PIN, HIGH); //释放总线
udelay(15);
byte >>= 1;
}
s3c2410_gpio_setpin(DS18B20_PIN, HIGH); //释放总线
}
BYTE DS18b20_read_byte (void)
{
BYTE i = 0;
BYTE byte = 0;
// 读“1”时隙:
// 若总线状态保持在低电平状态1微秒到15微秒之间
// 然后跳变到高电平状态且保持在15微秒到60微秒之间
// 就认为从DS18B20读到一个“1”信号
// 理想情况: 1微秒的低电平然后跳变再保持60微秒的高电平
//
// 读“0”时隙:
// 若总线状态保持在低电平状态15微秒到30微秒之间
// 然后跳变到高电平状态且保持在15微秒到60微秒之间
// 就认为从DS18B20读到一个“0”信号
// 理想情况: 15微秒的低电平然后跳变再保持46微秒的高电平
for (i = 0; i < 8; i++)
{
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP);
s3c2410_gpio_setpin(DS18B20_PIN, LOW);
udelay(1);
byte >>= 1;
s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_INP);
// 若总线在我们设它为低电平之后若1微秒之内变为高
// 则认为从DS18B20处收到一个“1”信号
// 因此把byte的D7为置“1”
if (s3c2410_gpio_getpin(DS18B20_PIN)) byte |= 0x80;
udelay(60);
}
return byte;
}
void DS18b20_proc(void) //读取温度
{
while(DS18b20_reset()); //循环判断DS18B20直到复位,延时120ms;
udelay(120);
DS18b20_write_byte(0xcc); //写入CCH命令,跳过读序列号过程;
DS18b20_write_byte(0x44); //写入44H命令,开始温度转换,延时5 ms;
udelay(5);
while(DS18b20_reset()); //循环判断DS18B20直到复位,延时200 ms;
udelay(200);
DS18b20_write_byte(0xcc); //写入CCH命令,跳过读序列号过程;
DS18b20_write_byte(0xbe); //写入BEH命令,读取寄存器;
data[0] = DS18b20_read_byte(); //读温度整数部分;
data[1] = DS18b20_read_byte(); //读温度小数部分。
}
static ssize_t s3c2440_18b20_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
DS18b20_proc();
buf[0] = data[0];
buf[1] = data[1];
return 1;
}
static struct file_operations s3c2440_18b20_fops =
{
.owner = THIS_MODULE,
.read = s3c2440_18b20_read,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &s3c2440_18b20_fops,
};
static int __init s3c2440_18b20_init(void)
{
int ret;
ret = misc_register(&misc);
printk (DEVICE_NAME" initialized\n");
//devfs_mk_cdev(MKDEV(DEV_MAJOR, 0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEV_NAME);
while(DS18b20_reset());
return ret;
}
static void __exit s3c2440_18b20_exit(void)
{
misc_deregister(&misc);
}
module_init(s3c2440_18b20_init);
module_exit(s3c2440_18b20_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("luo");
MODULE_DESCRIPTION("DS18b20 Drivers for ok2440 Board");
(2)应用程序
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/configfs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <plat/regs-timer.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-nrs.h>
#include <mach/map.h>
#include <mach/regs-irq.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <mach/regs-clock.h>
#include <asm/irq.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
typedef unsigned char BYTE;
#define DS18B20_PIN S3C2410_GPG(0)
#define DS18B20_PIN_OUTP S3C2410_GPIO_OUTPUT
#define DS18B20_PIN_INP S3C2410_GPIO_INPUT
#define HIGH 1
#define LOW 0
#define DEVICE_NAME "DS18B20"
static BYTE data[2];
BYTE DS18b20_reset (void)
{
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP); //配置GPG0输出模式
s3c2410_gpio_setpin(DS18B20_PIN, HIGH); // 向18B20发送一个上升沿,并保持高电平状态约100微秒
udelay(100);
s3c2410_gpio_setpin(DS18B20_PIN, LOW); // 向18B20发送一个下降沿,并保持低电平状态约600微秒
udelay(600);
s3c2410_gpio_setpin(DS18B20_PIN, HIGH); // 向18B20发送一个上升沿,此时可释放DS18B20总线
udelay(100);
// 以上动作是给DS18B20一个复位脉冲
// 通过再次配置GPIG0引脚成输入状态,可以检测到DS18B20是否复位成功
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_INP);
if(s3c2410_gpio_getpin(DS18B20_PIN)){ printk("DS18b20 reset failed.\r\n"); return 1;} // 若总线在释放后总线状态为高电平,则复位失败
return 0;
}
void DS18b20_write_byte (BYTE byte)
{
BYTE i;
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP); // 配置GPG0为输出模式
// 写“1”时隙:
// 保持总线在低电平1微秒到15微秒之间
// 然后再保持总线在高电平15微秒到60微秒之间
// 理想状态: 1微秒的低电平然后跳变再保持60微秒的高电平
//
// 写“0”时隙:
// 保持总线在低电平15微秒到60微秒之间
// 然后再保持总线在高电平1微秒到15微秒之间
// 理想状态: 60微秒的低电平然后跳变再保持1微秒的高电平
for (i = 0; i < 8; i++)
{
s3c2410_gpio_setpin(DS18B20_PIN, LOW); udelay(1);
if(byte & HIGH)
{
// 若byte变量的D0位是1,则需向总线上写“1”
// 根据写“1”时隙规则,电平在此处翻转为高
s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
}
else
{
// 若byte变量的D0位是0,则需向总线上写“0”
// 根据写“0”时隙规则,电平在保持为低
s3c2410_gpio_setpin(DS18B20_PIN, LOW);
}
// 电平状态保持60微秒
udelay(60);
s3c2410_gpio_setpin(DS18B20_PIN, HIGH); //释放总线
udelay(15);
byte >>= 1;
}
s3c2410_gpio_setpin(DS18B20_PIN, HIGH); //释放总线
}
BYTE DS18b20_read_byte (void)
{
BYTE i = 0;
BYTE byte = 0;
// 读“1”时隙:
// 若总线状态保持在低电平状态1微秒到15微秒之间
// 然后跳变到高电平状态且保持在15微秒到60微秒之间
// 就认为从DS18B20读到一个“1”信号
// 理想情况: 1微秒的低电平然后跳变再保持60微秒的高电平
//
// 读“0”时隙:
// 若总线状态保持在低电平状态15微秒到30微秒之间
// 然后跳变到高电平状态且保持在15微秒到60微秒之间
// 就认为从DS18B20读到一个“0”信号
// 理想情况: 15微秒的低电平然后跳变再保持46微秒的高电平
for (i = 0; i < 8; i++)
{
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP);
s3c2410_gpio_setpin(DS18B20_PIN, LOW);
udelay(1);
byte >>= 1;
s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_INP);
// 若总线在我们设它为低电平之后若1微秒之内变为高
// 则认为从DS18B20处收到一个“1”信号
// 因此把byte的D7为置“1”
if (s3c2410_gpio_getpin(DS18B20_PIN)) byte |= 0x80;
udelay(60);
}
return byte;
}
void DS18b20_proc(void) //读取温度
{
while(DS18b20_reset()); //循环判断DS18B20直到复位,延时120ms;
udelay(120);
DS18b20_write_byte(0xcc); //写入CCH命令,跳过读序列号过程;
DS18b20_write_byte(0x44); //写入44H命令,开始温度转换,延时5 ms;
udelay(5);
while(DS18b20_reset()); //循环判断DS18B20直到复位,延时200 ms;
udelay(200);
DS18b20_write_byte(0xcc); //写入CCH命令,跳过读序列号过程;
DS18b20_write_byte(0xbe); //写入BEH命令,读取寄存器;
data[0] = DS18b20_read_byte(); //读温度整数部分;
data[1] = DS18b20_read_byte(); //读温度小数部分。
}
static ssize_t s3c2440_18b20_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
DS18b20_proc();
buf[0] = data[0];
buf[1] = data[1];
return 1;
}
static struct file_operations s3c2440_18b20_fops =
{
.owner = THIS_MODULE,
.read = s3c2440_18b20_read,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &s3c2440_18b20_fops,
};
static int __init s3c2440_18b20_init(void)
{
int ret;
ret = misc_register(&misc);
printk (DEVICE_NAME" initialized\n");
//devfs_mk_cdev(MKDEV(DEV_MAJOR, 0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEV_NAME);
while(DS18b20_reset());
return ret;
}
static void __exit s3c2440_18b20_exit(void)
{
misc_deregister(&misc);
}
module_init(s3c2440_18b20_init);
module_exit(s3c2440_18b20_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("luo");
MODULE_DESCRIPTION("DS18b20 Drivers for ok2440 Board");
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
main()
{
int fd;
unsigned char buf[2];
unsigned short temp=0;
double result=0;
int flag=0;
if ((fd=open("/dev/DS18B20",O_RDWR | O_NDELAY | O_NOCTTY)) < 0)
{
printf("Open Device DS18B20 failed.\r\n");
exit(1);
}
else
{
printf("Open Device DS18B20 successed.\r\n");
while(1)
{
read(fd, buf, sizeof(buf));
printf("read data is 0x%02X-0x%02X\n",buf[1],buf[0]);
temp=((unsigned short)buf[1])<<8;
temp|=(unsigned short)buf[0];
// printf("no error here\n");
result=0.0625*((double)temp);
printf("temperature is %4f \r\n", result);
sleep(2);
}
close(fd);
}
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
main()
{
int fd;
unsigned char buf[2];
unsigned short temp=0;
double result=0;
int flag=0;
if ((fd=open("/dev/DS18B20",O_RDWR | O_NDELAY | O_NOCTTY)) < 0)
{
printf("Open Device DS18B20 failed.\r\n");
exit(1);
}
else
{
printf("Open Device DS18B20 successed.\r\n");
while(1)
{
read(fd, buf, sizeof(buf));
printf("read data is 0x%02X-0x%02X\n",buf[1],buf[0]);
temp=((unsigned short)buf[1])<<8;
temp|=(unsigned short)buf[0];
// printf("no error here\n");
result=0.0625*((double)temp);
printf("temperature is %4f \r\n", result);
sleep(2);
}
close(fd);
}
}
感谢网上的各位大牛,本博文主要参考:
- http://blog.chinaunix.net/uid-25923232-id-366019.html
- http://www.eepw.com.cn/article/236202.htm
- http://www.21ic.com/jichuzhishi/datasheet/DS18B20/yuanli/187677.html