DS18B20 Linux驱动程序 基于AT91SAM9260
调试了很久的DS18B20驱动,前些日子出现的问题一直是读出为0 ,卡了4天之后终于解决了,之前在控制口没有加上拉电阻,
后来想到可能是这个问题,加了个大电阻,果然好了~
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <mach/board.h>
#include <mach/gpio.h>
#include <linux/device.h>
#define DEV_NAME "ds18b20"
#define PFX "ds18b20: "
#define ds18b20_MINOR 120 /*minor number of this ds18b20*/
/*commad*/
#define SKIP_ROM 0xCC /*skip rom operation*/
#define TEMP_CONVET 0x44 /*start temperature convertion*/
#define READ_TEMP 0xBE /*start read temperature*/
#define HIGH 1
#define LOW 0
static int char_major=231;
unsigned char data[2];
/*
* ds18s20_set_input:
*
* @action: set input and pull low
*
* @index: sensor select
*
* @return: none
*/
static void ds18s20_set_input(int high)
{
if(high)
{
at91_set_gpio_input(AT91_PIN_PA24, HIGH);
}
else
{
at91_set_gpio_input(AT91_PIN_PA24, LOW);
}
}
/*
* ds18b20_set_output:
*
* @action: set output and clear io
*
* @index: sensor select
*
* @return: none
*/
static void ds18b20_set_output( int high)
{
if(high)
{
at91_set_gpio_output(AT91_PIN_PA24, HIGH);
}
if(!high)
{
at91_set_gpio_output(AT91_PIN_PA24, LOW);
}
}
/*
* ds18b20_get_io:
*
* @action: get io value
*
* @index: sensor select
*
* @return: 1 for success
* 0 for failure
*/
static unsigned char ds18b20_get_io(void)
{
unsigned char ret = 0;
ret = at91_get_gpio_value(AT91_PIN_PA24);
return ret;
}
/*
* ds18b20_write_byte:
*
* @action: write byte to ds18b20 register
*
* @b: the data value ready to write
*
* @index: sensor select
*
* @return: none
*/
static unsigned char ds18b20_write_byte(unsigned char b)//b=skip ROM operation
{
int i;
/*
// 写“1”时隙:
// 保持总线在低电平1微秒到15微秒之间
// 然后再保持总线在高电平15微秒到60微秒之间
// 理想状态: 1微秒的低电平然后跳变再保持60微秒的高电平
//
// 写“0”时隙:
// 保持总线在低电平15微秒到60微秒之间
// 然后再保持总线在高电平1微秒到15微秒之间
// 理想状态: 60微秒的低电平然后跳变再保持1微秒的高电平
*/
for(i=0;i<8;i++)
{
if(b&1)
{
ds18b20_set_output(LOW);
udelay(8);
ds18b20_set_output(HIGH);
udelay(55);
}
else
{
ds18b20_set_output(LOW);
udelay(55);
ds18b20_set_output(HIGH);
udelay(8);
}
b>>=1;
}
return b;
}
/*
* ds18b20_read_byte:
*
* @action: read data value(byte) form register
*
* @index: sensor select
*
* @return: data value
*/
static unsigned char ds18b20_read_byte(void)
{
unsigned char i=0,byte=0;
for(i=0;i<8;i++)
{
byte>>=1;
ds18b20_set_output(LOW);
udelay(1);
ds18b20_set_output(HIGH);
udelay(1);
ds18s20_set_input(HIGH);
if(ds18b20_get_io())
byte|=0x80;
udelay(60);
}
return byte;
// 读“1”时隙:
// 若总线状态保持在低电平状态1微秒到15微秒之间
// 然后跳变到高电平状态且保持在15微秒到60微秒之间
// 就认为从DS18B20读到一个“1”信号
// 理想情况: 1微秒的低电平然后跳变再保持60微秒的高电平
//
// 读“0”时隙:
// 若总线状态保持在低电平状态15微秒到30微秒之间
// 然后跳变到高电平状态且保持在15微秒到60微秒之间
// 就认为从DS18B20读到一个“0”信号
// 理想情况: 15微秒的低电平然后跳变再保持46微秒的高电平
}
/*
* ds18b20_reset:
*
* @action: reset ds18b20
*
* @index: sensor select
*
* @return: 1 for success
* 0 for failure
*/
static int ds18b20_reset(void)
{
ds18b20_set_output(HIGH);
ds18b20_set_output(LOW);//
udelay(500);
ds18b20_set_output(HIGH);
ds18s20_set_input(HIGH);//set input mode
udelay(15);//wait 15-60us DS18b20 will respond
ds18b20_get_io();//if answer is 0
printk("DS18b20 respond 0\n");
//ds18b20_set_output(HIGH);
udelay(800);
return 0;
}
/*
* ds18b20_reset:
*
* @action: read temperature
*
* @index: sensor select
*
* @return: current temperature
*/
static int ds18b20_read_temp(void)
{
ds18b20_reset();//
//ds18b20_get_io(index);
ds18b20_write_byte(SKIP_ROM); /*skip ROM operation*/
ds18b20_write_byte(TEMP_CONVET); /*start temperature convertion*/
//mdelay(1);
//ds18b20_set_input(index);
//mdelay(1);
mdelay(800);
ds18b20_reset();
ds18b20_write_byte(SKIP_ROM); /*skip ROM operation*/
ds18b20_write_byte(READ_TEMP); /*start read temperature*/
data[0]=ds18b20_read_byte();
data[1]=ds18b20_read_byte();
printk("%x,%x \n",data[1],data[0]);
ds18b20_set_output(HIGH);
return 0;
}
static int ds18b20_open(struct inode * s_node,struct file * s_file)
{
return 0;
}
static ssize_t ds18b20_read(struct file *DS1820_file,char * buf, size_t count, loff_t * l)
{
ds18b20_read_temp();
buf[0] = data[0];
buf[1] = data[1];
return 0;
}
struct file_operations ds18b20_fops =
{
.owner = THIS_MODULE,
.open=ds18b20_open,
.read=ds18b20_read,
};
struct class *myclass ;
static int __init ds18b20_init(void)
{
int ret;
ret = register_chrdev(char_major,DEV_NAME, &ds18b20_fops);
myclass = class_create(THIS_MODULE,DEV_NAME);
device_create(myclass, NULL, MKDEV(char_major, 0), NULL, DEV_NAME);
if(ret<0)
{
printk(KERN_ALERT "DS18b20 REG FAIL!\n");
}
else
{
printk(KERN_ALERT "MAJOR = %d\n",char_major);
}
printk(KERN_INFO "Temperature Sensor Driver For ds18b20\n");
ds18b20_reset();
return ret;
}
static void __exit ds18b20_exit(void)
{
unregister_chrdev(char_major,DEV_NAME);
class_destroy(myclass);
printk(KERN_INFO PFX "ds18b20 dirver removed\n");
}
module_init(ds18b20_init);
module_exit(ds18b20_exit);
MODULE_AUTHOR("St.Dickens");
MODULE_DESCRIPTION("temperature sensor driver for ds18b20");
MODULE_LICENSE("Dual BSD/GPL");
===================================makefile========================================
# Makefile2.6
ifneq ($(KERNELRELEASE),)
#kbuild syntax. dependency relationshsip of files and target modules are listed here.
obj-m := ds18b20.o
else
PWD := $(shell pwd)
KVER = 2.6.27
KDIR := /home/dickens/linux-2.6.27
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions
endif
==============================随便写了个测试程序=======================================
#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"
#define DEV_NAME "/dev/ds18b20"
void delay(unsigned int time)
{
while(time>0)
time--;
}
int main()
{
int fd;
unsigned char buf[2];
float result;
int times=5;
//打开设备文件
fd=open(DEV_NAME,O_RDWR | O_NDELAY | O_NOCTTY);
if(fd<0)
{
printf("Open Device Fail!\n");
return -1;
}
//读取当前设备数值
else
{
printf("OPEND!\n");
}
while(times>0)
{
read(fd, buf, 1);
result = (float)buf[0];
result /= 16;
result += ((float)buf[1] * 16);
printf("%.1f `C\r\n", result);
times--;
sleep(5);
}
close(fd);
}