最近在用18B20想测温度,但是纠结了好多天,温度总是0,8,16,2000多这样乱跳,大多还是0,时序我也是按照18B20手册的,但是还是不对。还有一个不懂的是释放总线这个概念,有些地方说是直接给总线置1,有些是把IO改为输入模式,我试了下用置1的方法不成功。 下面是我的代码:::
- C/C++ code
#include <linux/miscdevice.h>#include <linux/delay.h>#include <asm/irq.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/fs.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/moduleparam.h>#include <linux/slab.h>#include <linux/errno.h>#include<linux/init.h>#include<linux/kernel.h>#include<linux/module.h>#include<linux/fs.h>#include<asm/uaccess.h>#include<asm/io.h>#include<linux/device.h>#include<asm/gpio.h>#include<linux/poll.h>#include<linux/interrupt.h>#include<linux/irq.h>#include<linux/time.h>#include<linux/delay.h>#include<asm/hardware.h>#include <asm-arm/arch-s3c2410/regs-gpio.h>MODULE_LICENSE("GPL");#define DEVICE_NAME "18B20-drv"#define GPJCON (unsigned long)(0x560000D0)#define GPJDAT (unsigned long)(0x560000D4)#define GPJUP (unsigned long) (0x560000D8)typedef char BYTE;static volatile unsigned long* _io_gpjcon;static volatile unsigned long* _io_gpjdat;static volatile unsigned long* _io_gpjup;static BYTE data[2];//store 18B20rature 1 and 0static int major=0;static int minor=0;static int temp_open(struct inode *, struct file *);static int temp_release(struct inode *, struct file *);static ssize_t temp_read(struct file *, char *, size_t, loff_t *);//static ssize_t temp_write(struct file *, const char *, size_t, loff_t *);//static int temp_ioctl(struct inode *, struct file *, unsigned int cmd, unsigned long arg);void DQ_OUTP(void);void DQ_INTP(void);void set_DQ(unsigned int);unsigned long get_DQ(void);int DQ_reset(void);unsigned char DQ_RBYTE(void);int DQ_PRO(void);static struct file_operations fops = { .owner = THIS_MODULE, .open = temp_open, .release = temp_release, .read = temp_read,// .write = temp_write, //.ioctl = temp_ioctl,};static struct class *dev_class;//use this to create device nodestatic int temp_init(void){ major = register_chrdev(0,DEVICE_NAME,&fops); if(major < 0) { printk("register %s error\n",DEVICE_NAME); return 1; } printk("DEV Number:%d,%d\n",major,minor); dev_class = class_create(THIS_MODULE,DEVICE_NAME); if(dev_class == NULL) { printk("18B20 node create error\n"); printk("TODO:mknod /dev/%s c %d %d\n",DEVICE_NAME,major,minor); } else { device_create(dev_class,NULL,MKDEV(major,minor),"%s%d",DEVICE_NAME,minor); printk("/dev/%s%d register sucess\n",DEVICE_NAME,minor); } return 0;}static void temp_exit(void){ if(dev_class) { device_destroy(dev_class,MKDEV(major,minor)); class_destroy(dev_class); } unregister_chrdev(major,DEVICE_NAME);}static int temp_open(struct inode * pnode, struct file * pfile){ _io_gpjcon = (unsigned long*)ioremap(GPJCON,4); _io_gpjdat = (unsigned long*)ioremap(GPJDAT,4); _io_gpjup = (unsigned long*)ioremap(GPJUP,4); printk("18b20 open\n"); return 0;}static int temp_release(struct inode *pnode, struct file *pfile){ printk("18b20 release\n"); return 0;}module_init(temp_init);module_exit(temp_exit);////////////////////////////registerover/////////////////////////////////void DQ_OUTP()//GPJCON为输出模式{ unsigned long temp; temp = *(unsigned long*)_io_gpjcon; temp &=(~0x3); temp |= 0x1; *(unsigned long*)_io_gpjcon = temp;}void DQ_INTP()//GPJCON为输入状态{ unsigned long temp; temp = *(unsigned long*)_io_gpjcon; temp &=(~0x3); *(unsigned long*)_io_gpjcon = temp;}void set_DQ(unsigned int flag)//设置GPJ0的高低电平{ unsigned long temp; temp = *(unsigned long*)_io_gpjdat; temp &= ~0x1; if(flag&0x1) temp |= 0x1; *(unsigned long*)_io_gpjdat = temp;}unsigned long get_DQ()//获得当前18B20的DQ状态{ unsigned long temp; temp = *(unsigned long*)_io_gpjdat; temp &=0x01; return temp;}/* 复位流程: 1.总线先置为高电平 2.拉低至少480us 3再拉高15-60us 4检测是否18B20产生复位电平,复位点评存在 60-240us*/int DQ_reset(void){ //unsigned long temp; DQ_OUTP(); set_DQ(1); udelay(1); set_DQ(0); udelay(600); //set_DQ(1);//这里释放总线如果用置1,则复位不成功,但是改为输入口,则可以 DQ_INTP(); udelay(65); if(get_DQ()==0) { printk("reset sucessfully\n"); DQ_OUTP(); set_DQ(1); return 0; } else { DQ_OUTP(); set_DQ(1); return 1; }}/* 18b20写ROM指令: 1.GPJ0为输出状态,并拉低电平 2.向总线写入相应的位,并延时1us 3.在接下来的15~60us内,18B20对总线采样。 若是高电平,则写入1,并将电平拉高 若是低电平,则继续保持低电平。 直到60us结束 4,拉高电平,并延时至少1us */void DQ_WBYTE(unsigned long flag){ int i; int d; DQ_OUTP(); printk("%x\n",flag); for(i=0;i<8;i++) { set_DQ(0); udelay(2); if(flag & 1) { set_DQ(1); udelay(60);///// } else { udelay(60); set_DQ(1);///// } flag>>=1; udelay(1); }}/* 读一个bit的程序: 1.GPJ0为输入模式 2.拉低总线1us,然后释放总线 3.在接下去的14us内读数据,若为1,则拉高总线 若为低电平则保持低电平到周期结束 4.延时60~120us,拉高总线*/unsigned char DQ_RBYTE(){ int i; unsigned char data=0; for(i = 0;i<8;i++) { data>>=1; DQ_OUTP(); set_DQ(0); udelay(2); DQ_INTP(); if(get_DQ()) data|=0x80; udelay(60); set_DQ(1);///change by zjc udelay(2); } return data;}/* 温度转换进程: 1.复位 2.写ROM指令 3.写RAM指令 4.读温度*/int DQ_PRO(){ if(DQ_reset()) return 1; DQ_WBYTE(0xcc); DQ_WBYTE(0x44); mdelay(500); if(DQ_reset()) return 1; DQ_WBYTE(0xcc); DQ_WBYTE(0xbe); data[0] = DQ_RBYTE(); data[1] = DQ_RBYTE(); DQ_reset(); return 0;}static ssize_t temp_read(struct file *pfile, char *buff, size_t len, loff_t *ploff){ if(DQ_PRO()) printk("reset failed\n"); buff[0] = data[0];//这里我想把温度直接通过buff穿回去,可以吗,还是必须要用copy_to_user(); buff[1] = data[1];// printk("%x %x\n",buff[0],buff[1]); return 0;}