轉載自:http://blog.csdn.net/zqixiao_09/article/details/50973969
供學習之用!
對驅動工程師而言,時序分析的意思是讓芯片之間的訪問滿足芯片手冊中時序圖信號的有效的先後順序、採樣建立時間、保持時間的要求,通過時序來達到數據傳輸和硬件控制的目的。
嵌入式開發平臺:mini2440
DS18B20 所用GPIO:S3C2410_GPF(3)
一、DS18B20 時序分析
DS18B20的一線工作協議流程是:初始化→ROM操作指令→存儲器操作指令→數據傳輸,其工作時序包括:初始化時序、寫時序、讀時序。
1、初始化時序
主機首先發出一個480-960微秒的低電平脈衝,然後釋放總線變爲高電平,並在隨後的480微秒時間內對總線進行檢測,如果有低電平出現說明總線上有器件已做出應答,若無低電平出現一直都是高電平說明總線上無器件應答。
作爲從器件的DS18B20在一上電後就一直在檢測總線上是否有480-960微秒的低電平出現,如果有,在總線轉爲高電平後等待15-60微秒後將總線電平拉低60-240微秒做出響應存在脈衝,告訴主機本器件已做好準備,若沒有檢測到就一直在檢測等待。
static int ds18b20_init(void)
{
int retval = 0;
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 0);
s3c2410_gpio_setpin(DQ, 1);
udelay(2);
s3c2410_gpio_setpin(DQ, 0); // 拉低ds18b20總線,復位ds18b20
udelay(500); // 保持復位電平500us
s3c2410_gpio_setpin(DQ, 1); // 釋放ds18b20總線
udelay(60);
// 若復位成功,ds18b20發出存在脈衝(低電平,持續60~240us)
s3c2410_gpio_cfgpin(DQ, CFG_IN);
retval = s3c2410_gpio_getpin(DQ);
udelay(500);
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 0);
s3c2410_gpio_setpin(DQ, 1); // 釋放總線
return retval;
}
2、寫時序
寫週期最少爲60微秒,最長不超過120微秒,寫週期一開始作爲主機先把總線拉低1微秒錶示寫週期開始,隨後若主機想寫0,則繼續拉低電平最少60微秒直至寫週期結束,然後釋放總線爲高電平;若主機想寫1,在一開始拉低總線電平1微秒後就釋放總線爲高電平,一直到寫週期結束。
而作爲從機的DS18B20則在檢測到總線被拉低後等待15微秒然後從15μs到45μs開始對總線採樣,在採樣期內總線爲高電平則爲1,若採樣期內總線爲低電平則爲0。
static void write_byte(unsigned char data)
{
int i = 0;
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 1);
for (i = 0; i < 8; i++)
{
// 總線從高拉至低電平時,就產生寫時隙
s3c2410_gpio_setpin(DQ, 1);
udelay(2);
s3c2410_gpio_setpin(DQ, 0);
s3c2410_gpio_setpin(DQ, data & 0x01);
udelay(60);
data >>= 1;
}
s3c2410_gpio_setpin(DQ, 1); // 重新釋放ds18b20總線
}
3、讀時序
對於讀數據操作時序也分爲讀0時序和讀1時序兩個過程,讀時序是從主機把單總線拉低之後,在1微秒之後就得釋放單總線爲高電平,以讓DS18B20把數據傳輸到單總線上。DS18B20在檢測到總線被拉低1微秒後,便開始送出數據,若是要送出0就把總線拉爲低電平直到讀週期結束;若要送出1則釋放總線爲高電平。
主機在一開始拉低總線1微秒後釋放總線,然後在包括前面的拉低總線電平1微秒在內的15微秒時間內完成對總線進行採樣檢測,採樣期內總線爲低電平則確認爲0,採樣期內總線爲高電平則確認爲1,完成一個讀時序過程,至少需要60μs才能完成。
static unsigned char read_byte(void)
{
int i;
unsigned char data = 0;
// 總線從高拉至低,只需維持低電平17ts,再把總線拉高,就產生讀時隙
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 0);
for (i = 0; i < 8; i++)
{
s3c2410_gpio_setpin(DQ, 1);
udelay(2);
s3c2410_gpio_setpin(DQ, 0);
udelay(2);
s3c2410_gpio_setpin(DQ, 1);
udelay(8);
data >>= 1;
s3c2410_gpio_cfgpin(DQ, CFG_IN);
if (s3c2410_gpio_getpin(DQ))
data |= 0x80;
udelay(50);
}
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 0);
s3c2410_gpio_setpin(DQ, 1); // 釋放ds18b20總線
return data;
}
二、操作方法
DS18B20單線通信功能是分時完成的,有嚴格的時序概念,如果出現序列混亂,1-WIRE器件將不影響主機,因此讀寫時序很重要。系統對DS18B20的各種操作必須按協議進行,根據DS18B20的協議規定,微控制器控制DS18B20完成溫度的轉換必須經過以下4個步驟:
1)每次讀寫前對DS18B20進行復位初始化。復位要求主CPU將數據線下拉500μs,然後釋放,DS18B20收到信號後等待16μs-60μs左右,然後發出60μs-240μs的存在低脈衝,主CPU收到此信號後表示復位成功。
2)發送一條ROM指令
3)發送存儲器指令
1、讓DS18B20進行一次溫度轉換的具體操作如下:
a – 主機先做個復位操作;
b – 主機再寫跳過ROM的操作(CCH)命令;
c – 然後主機接着寫轉換溫度的操作指令,後面釋放總線至少1秒,讓DS18B20完成轉換操作。需要注意的是每個命令字節在寫的時候都是低字節先寫,例如CCH的二進制爲11001100,在寫到總線上時要從低位開始寫,寫的順序是“0、0、1、1、0、0、1、1”,整個操作的總線狀態如圖所。
2、讀取RAM的溫度數據,同樣,這個操作也要按照三個步驟:
a – 主機發出復位操作並接受DS18B20的應答(存在)脈衝;
b – 主機發出跳過對ROM操作的命令(CCH);
c – 主機發出讀取RAM的命令(BEH),隨後主機依次讀取DS18B20發出的從第0-第8,共九個字節的數據。如果只想讀取溫度數據,那在讀完第0和第1個數據後就不再理會後面DS18B20發出的數據即可,同樣讀取數據也是低位在前,整個操作的總線狀態如圖所示。
三、具體驅動編寫
1、ds18b20_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/device.h>
/* 相關引腳定義,方便以後移植 */
#define DQ S3C2410_GPF(3)
#define CFG_IN S3C2410_GPIO_INPUT
#define CFG_OUT S3C2410_GPIO_OUTPUT
// ds18b20主次設備號(動態分配)
static int ds18b20_major = 0;
static int ds18b20_minor = 0;
static int ds18b20_nr_devs = 1;
// 定義設備類型
static struct ds18b20_device
{
struct cdev cdev;
};
struct ds18b20_device *ds18b20_devp; /*設備結構體指針 */
static struct class *ds18b20_class;
static struct class_device *ds18b20_class_dev;
/* 函數聲明 */
static int ds18b20_open(struct inode *inode, struct file *filp);
static int ds18b20_init(void);
static void write_byte(unsigned char data);
static unsigned char read_byte(void);
static ssize_t ds18b20_read(struct file *filp, char __user * buf, size_t count, loff_t * f_pos);
void ds18b20_setup_cdev(struct ds18b20_device *dev, int index);
static int ds18b20_open(struct inode *inode, struct file *filp)
{
int flag = 0;
flag = ds18b20_init();
if (flag & 0x01)
{
printk(KERN_WARNING "open ds18b20 failed\n");
return -1;
}
printk(KERN_NOTICE "open ds18b20 successful\n");
return 0;
}
static int ds18b20_init(void)
{
int retval = 0;
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 0);
s3c2410_gpio_setpin(DQ, 1);
udelay(2);
s3c2410_gpio_setpin(DQ, 0); // 拉低ds18b20總線,復位ds18b20
udelay(500); // 保持復位電平500us
s3c2410_gpio_setpin(DQ, 1); // 釋放ds18b20總線
udelay(60);
// 若復位成功,ds18b20發出存在脈衝(低電平,持續60~240us)
s3c2410_gpio_cfgpin(DQ, CFG_IN);
retval = s3c2410_gpio_getpin(DQ);
udelay(500);
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 0);
s3c2410_gpio_setpin(DQ, 1); // 釋放總線
return retval;
}
static void write_byte(unsigned char data)
{
int i = 0;
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 1);
for (i = 0; i < 8; i++)
{
// 總線從高拉至低電平時,就產生寫時隙
s3c2410_gpio_setpin(DQ, 1);
udelay(2);
s3c2410_gpio_setpin(DQ, 0);
s3c2410_gpio_setpin(DQ, data & 0x01);
udelay(60);
data >>= 1;
}
s3c2410_gpio_setpin(DQ, 1); // 重新釋放ds18b20總線
}
static unsigned char read_byte(void)
{
int i;
unsigned char data = 0;
for (i = 0; i < 8; i++)
{
// 總線從高拉至低,只需維持低電平17ts,再把總線拉高,就產生讀時隙
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 0);
s3c2410_gpio_setpin(DQ, 1);
udelay(2);
s3c2410_gpio_setpin(DQ, 0);
udelay(2);
s3c2410_gpio_setpin(DQ, 1);
udelay(8);
data >>= 1;
s3c2410_gpio_cfgpin(DQ, CFG_IN);
if (s3c2410_gpio_getpin(DQ))
data |= 0x80;
udelay(50);
}
s3c2410_gpio_cfgpin(DQ, CFG_OUT);
s3c2410_gpio_pullup(DQ, 0);
s3c2410_gpio_setpin(DQ, 1); // 釋放ds18b20總線
return data;
}
static ssize_t ds18b20_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 };
//struct ds18b20_device *dev = filp->private_data;
flag = ds18b20_init();
if (flag & 0x01)
{
printk(KERN_WARNING "ds18b20 init failed\n");
return -1;
}
write_byte(0xcc);
write_byte(0x44);
flag = ds18b20_init();
if (flag & 0x01)
return -1;
write_byte(0xcc);
write_byte(0xbe);
result[0] = read_byte(); // 溫度低八位
result[1] = read_byte(); // 溫度高八位
err = copy_to_user(buf, &result, sizeof(result));
return err ? -EFAULT : min(sizeof(result), count);
}
static struct file_operations ds18b20_dev_fops = {
.owner = THIS_MODULE,
.open = ds18b20_open,
.read = ds18b20_read,
};
void ds18b20_setup_cdev(struct ds18b20_device *dev, int index)
{
int err, devno = MKDEV(ds18b20_major, ds18b20_minor + index);
cdev_init(&dev->cdev, &ds18b20_dev_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&(dev->cdev), devno, 1);
if (err)
{
printk(KERN_NOTICE "ERROR %d add ds18b20\n", err);
}
}
static int __init ds18b20_dev_init(void)
{
int result;
dev_t dev = 0;
dev = MKDEV(ds18b20_major, ds18b20_minor);
if (ds18b20_major)
{
result = register_chrdev_region(dev, ds18b20_nr_devs, "ds18b20");
}
else
{
result = alloc_chrdev_region(&dev, ds18b20_minor, ds18b20_nr_devs, "ds18b20");
ds18b20_major = MAJOR(dev);
}
if (result < 0)
{
printk(KERN_WARNING "ds18b20: failed to get major\n");
return result;
}
/* 爲新設備分配內存和初始化 */
ds18b20_devp = kmalloc(sizeof(struct ds18b20_device), GFP_KERNEL);
if (!ds18b20_devp)
{ /*申請失敗 */
result = -ENOMEM;
goto fail_malloc;
}
memset(ds18b20_devp, 0, sizeof(struct ds18b20_device));
ds18b20_setup_cdev(ds18b20_devp, 0);
/* 自動創建設備節點 */
ds18b20_class = class_create(THIS_MODULE, "ds18b20_sys_class");
if (IS_ERR(ds18b20_class))
return PTR_ERR(ds18b20_class);
ds18b20_class_dev =
device_create(ds18b20_class, NULL, MKDEV(ds18b20_major, 0), NULL, "ds18b20");
if (unlikely(IS_ERR(ds18b20_class_dev)))
return PTR_ERR(ds18b20_class_dev);
return 0;
fail_malloc:
unregister_chrdev_region(dev, 1);
return result;
}
static void __exit ds18b20_dev_exit(void)
{
cdev_del(&ds18b20_devp->cdev); /*註銷cdev */
kfree(ds18b20_devp); /*釋放設備結構體內存 */
unregister_chrdev_region(MKDEV(ds18b20_major, 0), ds18b20_nr_devs); /*釋放設備號 */
device_unregister(ds18b20_class_dev);
class_destroy(ds18b20_class);
}
module_init(ds18b20_dev_init);
module_exit(ds18b20_dev_exit);
MODULE_LICENSE("Dual BSD/GPL");
2、app-ds18b20.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/ioctl.h>
// 函數聲明
void ds18b20_delay(int i);
int main()
{
int fd, i;
unsigned char result[2]; // 從ds18b20讀出的結果,result[0]存放低八位
unsigned char integer_value = 0;
float decimal_value = 0; // 溫度數值,decimal_value爲小數部分的值
float temperature = 0;
fd = open("/dev/ds18b20", 0);
if (fd < 0)
{
perror("open device failed\n");
exit(1);
}
while (1)
{
i++;
read(fd, &result, sizeof(result));
integer_value = ((result[0] & 0xf0) >> 4) | ((result[1] & 0x07) << 4);
// 精確到0.25度
decimal_value = 0.5 * ((result[0] & 0x0f) >> 3) + 0.25 * ((result[0] & 0x07) >> 2);
temperature = (float)integer_value + decimal_value;
printf("Current Temperature:%6.2f\n", temperature);
ds18b20_delay(500);
}
}
void ds18b20_delay(int i)
{
int j, k;
for (j = 0; j < i; j++)
for (k = 0; k < 50000; k++) ;
}
測試結果:
[root@www.linuxidc.com home]#
[root@www.linuxidc.com home]#./app-ds18b20
open ds18b20 successful
Current Temperature: 23.50
Current Temperature: 23.50
Current Temperature: 23.25
Current Temperature: 23.50
Current Temperature: 23.50
Current Temperature: 23.50
^C
[root@www.linuxidc.com home]#